        TITLE  GETSET - GETting and SETting MS-DOS system calls
        NAME   GETSET


IF 1
__TONYI__ = 1           ; Speed/Space hacks
__TONYI__1= 1           ; Speed/Space hacks 10/28/92
__TONYI__2= 1           ; Speed/Space hacks 11/02/92
VCB_FIX_2165 = 1        ; Fix for bug in INT 21/65A0-A2
ENDIF


;
;       Microsoft Confidential
;       Copyright (C) Microsoft Corporation 1991
;       All Rights Reserved.
;
;**     GETSET - System Calls which get and set various things
;
;       $GET_VERSION
;       $GET_VERIFY_ON_WRITE
;       $SET_VERIFY_ON_WRITE
;       $INTERNATIONAL
;       $GET_DRIVE_FREESPACE
;       $GET_DMA
;       $SET_DMA
;       $GET_DEFAULT_DRIVE
;       $SET_DEFAULT_DRIVE
;       $GET_INTERRUPT_VECTOR
;       $SET_INTERRUPT_VECTOR
;       RECSET
;       $CHAR_OPER
;       $GetExtendedError               DOS 3.3
;       Get_Global_CdPg                 DOS 4.0
;       $ECS_CALL                       DOS 4.0
;
;       Revision history:
;
;       Created: ARR 30 March 1983
;
;       A000    version 4.0   Jan. 1988
;       A006    D503-- fake version for IBMCACHE
;       A008    P4070- fake version for MS WINDOWS
;
;       DOS5    added new getversion call AX=3001H -- 4/3/90
;               Returns flags in BH:
;
;                       bits 0-2 = DOS internal revision
;                       bits 3-7 = DOS type flags
;                            bit 3    = DOS is in ROM
;                            bit 4    = DOS in in HMA
;                            bits 5-7 = reserved
;
;       M004 - MS PASCAL 3.2 support. Please see under tag M003 in dossym.inc
;              7/30/90
;
;       M007 - Change to new style GetVersion call - 8/6/90
;
;       M068 - use a count value (A20OFF_COUNT) rather than a bit to
;              indicate to dos dispatcher to turn  a20 off before iret.
;              See M004.
;
; ==========================================================================

.xlist
.xcref
        INCLUDE version.inc
        INCLUDE dosseg.inc
        INCLUDE dossym.inc
        INCLUDE devsym.inc
        INCLUDE doscntry.inc
        INCLUDE mult.inc
        INCLUDE pdb.inc
.cref
.list

IFNDEF  ALTVECT
        ALTVECT EQU     0                       ; FALSE
ENDIF

; ==========================================================================

DosData SEGMENT WORD PUBLIC 'DATA'

        EXTRN   USERNUM         :WORD
        EXTRN   MSVERS          :WORD
        EXTRN   VERFLG          :BYTE
        EXTRN   CNTCFLAG        :BYTE
        EXTRN   DMAADD          :DWORD
        EXTRN   CURDRV          :BYTE
        EXTRN   chSwitch        :BYTE
        EXTRN   COUNTRY_CDPG    :byte           ;DOS 3.3
        EXTRN   CDSCount        :BYTE
        EXTRN   ThisCDS         :DWORD
        EXTRN   EXTERR          :WORD
        EXTRN   EXTERR_ACTION   :BYTE
        EXTRN   EXTERR_CLASS    :BYTE
        EXTRN   EXTERR_LOCUS    :BYTE
        EXTRN   EXTERRPT        :DWORD
        EXTRN   UCASE_TAB       :BYTE
        EXTRN   FILE_UCASE_TAB  :BYTE
        EXTRN   InterCon        :BYTE
        EXTRN   CURRENTPDB      :WORD

        EXTRN   DBCS_TAB        :BYTE
        EXTRN   NLS_YES         :BYTE
        EXTRN   NLS_yes2        :BYTE
        EXTRN   NLS_NO          :BYTE
        EXTRN   NLS_no2         :BYTE

        EXTRN   Special_version :WORD
; VCB - Delete unreferenced data area Fake_Count
;       EXTRN   Fake_Count      :BYTE
        EXTRN   A20OFF_COUNT    :BYTE   ; M068, M004
        EXTRN   DOS_FLAG        :BYTE   ; M068

DosData ENDS

; ==========================================================================

DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE

        allow_getdseg

        EXTRN   CurrentPDB      :WORD

        EXTRN   Get_User_Stack  :NEAR   ; return pointer to user stack

BREAK <$Get_Version -- Return DOS version number>
; =========================================================================
;       $Get_Version - Return DOS Version Number
;
;       Fake_Count is used to lie about the version numbers to support
;       old binarys.  See ms_table.asm for more info.
;
;       ENTRY   none
;       EXIT    (bl:cx) = user number (24 bits)
;               (al.ah) = version # (in binary)
;
;               if input al = 00
;                 (bh) = OEM number
;               else if input al = 01
;                 (bh) = version flags
;
;                        bits 0-2 = DOS internal revision
;                        bits 3-7 = DOS type flags
;                             bit 3    = DOS is in ROM
;                             bit 4    = DOS in in HMA
;                             bits 5-7 = reserved
;               M007 change - only bit 3 is now valid.  Other bits
;               are 0 when AL = 1

;       USES    all
; =========================================================================

PROCEDURE $Get_Version ,NEAR
IFDEF __TONYI__2
;--------------------------------------------------------------
; Save a byte...faster too.
;--------------------------------------------------------------
        lds     cx, dword ptr ss:[UserNum]
        mov     bx, ds
        context DS                      ; SS is DOSDATA
ELSE
        context DS                      ; SS is DOSDATA

        mov     BX,[UserNum + 2]
        mov     CX,[UserNum]
ENDIF

                ;If AL == 1, ROMDOS will return BH = dos internal version # &
                ;DOS flags

        cmp     AL,1
        jnz     Norm_Vers

ifdef ROMDOS

        mov     BH, DOSINROM            ; Just set the bit for ROM version

else

        xor     bh,bh                   ; Otherwise return 0
endif                                   ;M007 end
                                        ;
norm_vers:                              ; MSVERS is a label in TABLE segment
        push    DS                      ; Get the version number from the
        mov     DS,CurrentPDB           ; current app's PSP segment
        mov     AX,DS:[PDB_Version]     ; AX = DOS version number
        pop     DS


        call    Get_User_Stack          ; Returns DS:SI --> Caller's stack

        ASSUME  DS:NOTHING
IFDEF __TONYI__2
SetUserAXBXCX:
ENDIF
        mov     [SI.User_AX],AX         ; Put values for return registers
IFDEF __TONYI__2
SetUserBXCX:
ENDIF
IFDEF __TONYI__2
        mov     [SI.User_CX],CX         ; stack addressed by DS:SI
SetUserBX:
        mov     [SI.User_BX],BX         ; in the proper place on the user's
ELSE
        mov     [SI.User_BX],BX         ; in the proper place on the user's
        mov     [SI.User_CX],CX         ; stack addressed by DS:SI
ENDIF
        return

ENDPROC $Get_Version

; =========================================================================


BREAK <$Get/Set_Verify_on_Write - return/set verify-after-write flag>
; =========================================================================
;**     $Get_Verify_On_Write - Get Status of Verify on write flag
;
;       ENTRY   none
;       EXIT    (al) = value of VERIFY flag
;       USES    all
; =========================================================================

procedure   $GET_VERIFY_ON_WRITE,NEAR

;hkn; SS override
        MOV     AL,[VERFLG]
        return

EndProc $GET_VERIFY_ON_WRITE



;**     $Set_Verify_On_Write - Set Status of Verify on write flag
;
;       ENTRY   (al) = value of VERIFY flag
;       EXIT    none
;       USES    all

procedure   $SET_VERIFY_ON_WRITE,NEAR

        AND     AL,1
;hkn; SS override
        MOV     [VERFLG],AL
        return

EndProc $SET_VERIFY_ON_WRITE

BREAK <$International - return country-dependent information>
;----------------------------------------------------------------------------
;
; Procedure Name : $INTERNATIONAL
;
; Inputs:
;       MOV     AH,International
;       MOV     AL,country      (al = 0 => current country)
;      [MOV     BX,country]
;       LDS     DX,block
;       INT     21
; Function:
;       give users an idea of what country the application is running
; Outputs:
;       IF DX != -1 on input (get country)
;         AL = 0 means return current country table.
;         0<AL<0FFH means return country table for country AL
;         AL = 0FF means return country table for country BX
;         No Carry:
;            Register BX will contain the 16-bit country code.
;            Register AL will contain the low 8 bits of the country code.
;            The block pointed to by DS:DX is filled in with the information
;            for the particular country.
;               BYTE  Size of this table excluding this byte and the next
;               BYTE  Country code represented by this table
;                       A sequence of n bytes, where n is the number specified
;                       by the first byte above and is not > internat_block_max,
;                       in the correct order for being returned by the
;                       INTERNATIONAL call as follows:
;               WORD    Date format 0=mdy, 1=dmy, 2=ymd
;               5 BYTE  Currency symbol null terminated
;               2 BYTE  thousands separator null terminated
;               2 BYTE  Decimal point null terminated
;               2 BYTE  Date separator null terminated
;               2 BYTE  Time separator null terminated
;               1 BYTE  Bit field.  Currency format.
;                       Bit 0.  =0 $ before #  =1 $ after #
;                       Bit 1.  no. of spaces between # and $ (0 or 1)
;               1 BYTE  No. of significant decimal digits in currency
;               1 BYTE  Bit field.  Time format.
;                       Bit 0.  =0 12 hour clock  =1 24 hour
;               DWORD   Call address of case conversion routine
;               2 BYTE  Data list separator null terminated.
;         Carry:
;            Register AX has the error code.
;       IF DX = -1 on input (set current country)
;         AL = 0 is an error
;         0<AL<0FFH means set current country to country AL
;         AL = 0FF means set current country to country BX
;         No Carry:
;           Current country SET
;           Register AL will contain the low 8 bits of the country code.
;         Carry:
;            Register AX has the error code.
;-----------------------------------------------------------------------------

procedure   $INTERNATIONAL,NEAR   ; DOS 3.3
        CMP     AL,0FFH
        JZ      BX_HAS_CODE             ; -1 means country code is in BX
        MOV     BL,AL                   ; Put AL country code in BX
        XOR     BH,BH
BX_HAS_CODE:
        PUSH    DS
        POP     ES
        PUSH    DX
        POP     DI                      ; User buffer to ES:DI

;hkn; SS is DOSDATA
        context DS

        CMP     DI,-1
        JZ      international_set
        OR      BX,BX
        JNZ     international_find

;hkn; country_cdpg is in DOSDATA segment.
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG

        JMP     SHORT international_copy

international_find:
IFDEF __TONYI__
        xor     bp, bp  ; Save a byte ; flag it for GetCntry only
ELSE
        MOV     BP,0                     ; flag it for GetCntry only
ENDIF
        CALL    international_get
        JC      errtn
IFDEF __TONYI__
        or      bx, bx  ; Save a byte
ELSE
        CMP     BX,0                     ; nlsfunc finished it ?
ENDIF
        JNZ     SHORT international_copy ; no, copy by myself
        MOV     BX,DX                    ; put country back
        JMP     SHORT international_ok3

international_get:

;hkn; country_cdpg is in DOSDATA segment.
;hkn; use ss override to access COUNTRY_CDPG fields
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG
        CMP     BX,ss:[SI.ccDosCountry]  ; = current country id;smr;SS Override

        retz                             ; return if equal
        MOV     DX,BX
        XOR     BX,BX                    ; bx = 0, default code page
        CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory
        CMP     AL,0FFH
        JNZ     interr                     ; not in memory
        or      bp,bp                    ; GetCntry ?
        JNZ     stcdpg
        CallInstall GetCntry,NLSFUNC,4   ; get country info
        JMP     short chkok
stcdpg:
        CallInstall SetCodePage,NLSFUNC,3  ; set country info
chkok:
        or      al,al                      ; success ?
        retz                               ; yes
setcarry:
        STC                              ; set carry
        ret
interr:
        MOV     AL,0FFH                    ; flag nlsfunc error
        JMP     SHORT setcarry                                        ;AC000;

international_copy:

;hkn; country_cdpg is in DOSDATA segment.
;hkn; use ss override to access COUNTRY_CDPG fields
        MOV     BX,ss:[SI.ccDosCountry]  ; = current country id;smr;SS Override
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG.ccDFormat

IFDEF __TONYI__
        MOV     CX,(OLD_COUNTRY_SIZE shr 1)
ELSE
        MOV     CX,OLD_COUNTRY_SIZE
ENDIF

;hkn;   must set up DS to SS so that international info can be copied
        push    ds
        push    ss                                      ; cs -> ss
        pop     ds

IFDEF __TONYI__
        rep     movsw
        IF (OLD_COUNTRY_SIZE and 0001H)
                movsb
        ENDIF
ELSE
        REP     MOVSB                    ;copy country info
ENDIF

;hkn;   restore ds
        pop     ds

international_ok3:
        call    get_user_stack
ASSUME  DS:NOTHING
IFDEF __TONYI__2
;----------------------------------------
; Save a byte.......
;----------------------------------------
        call    SetUserBX
ELSE
        MOV     [SI.user_BX],BX
ENDIF
international_ok:
        MOV     AX,BX                ; Return country code in AX too.
        transfer SYS_RET_OK

international_set:

;hkn; ASSUME    DS:DOSGROUP
ASSUME  DS:DOSDATA

        MOV     BP,1                 ; flag it for SetCodePage only
        CALL    international_get
        JNC     international_ok
errtn:
        CMP     AL,0FFH
        JZ      errtn2
        transfer SYS_RET_ERR         ; return what we got from NLSFUNC
errtn2:
        error   error_Invalid_Function  ; NLSFUNC not existent


EndProc $INTERNATIONAL



BREAK <$GetExtCntry - return extended country-dependent information>
;---------------------------------------------------------------------------
;
; Procedure Name : $GetExtCntry
;
; Inputs:
;       if AL >= 20H
;         AL= 20H    capitalize single char, DL= char
;             21H    capitalize string ,CX= string length
;             22H    capitalize ASCIIZ string
;             23H    YES/NO check, DL=1st char DH= 2nd char (DBCS)
;             80H bit 0 = use normal upper case table
;                     1 = use file upper case table
;          DS:DX points to string
;
;       else
;
;       MOV     AH,GetExtCntry   ; DOS 3.3
;       MOV     AL,INFO_ID      ( info type,-1  selects all)
;       MOV     BX,CODE_PAGE    ( -1 = active code page )
;       MOV     DX,COUNTRY_ID   ( -1 = active country )
;       MOV     CX,SIZE         ( amount of data to return)
;       LES     DI,COUNTRY_INFO ( buffer for returned data )
;       INT     21
; Function:
;       give users extended country dependent information
;       or capitalize chars
; Outputs:
;         No Carry:
;            extended country info is succesfully returned
;         Carry:
;            Register AX has the error code.
;            AX=0, NO    for YES/NO CHECK
;               1, YES
;-------------------------------------------------------------------------------

procedure   $GetExtCntry,NEAR   ; DOS 3.3
        CMP     AL,CAP_ONE_CHAR         ; < 20H ?
ifdef DBCS
        jnb     capcap
        jmp     SHORT notcap                                          ;AC000;
else
        JB      notcap
endif
capcap:                                 ;
        TEST    AL,UPPER_TABLE          ; which upper case table
        JNZ     fileupper               ; file upper case

;hkn; UCASE_TAB in DOSDATA
        MOV     BX,OFFSET DOSDATA:UCASE_TAB+2 ; get normal upper case
        JMP     SHORT capit
fileupper:
IFDEF VCB_FIX_2165
; Fix bug causing functions 65A0-65A2 to not work
        AND     AL,7FH
ENDIF

;hkn; FILE_UCASE_TAB in DOSDATA
        MOV     BX,OFFSET DOSDATA:FILE_UCASE_TAB+2 ; get file upper case
capit:                                  ;
        CMP     AL,CAP_ONE_CHAR         ; cap one char ?
        JNZ     chkyes                  ; no
        MOV     AL,DL                   ; set up AL
        invoke  GETLET3                 ; upper case it
        call    get_user_stack          ; get user stack
        MOV     byte ptr [SI.user_DX],AL; user's DL=AL
        JMP     SHORT nono              ; done
chkyes:                                 ;
        CMP     AL,CHECK_YES_NO         ; check YES or NO ?
        JNZ     capstring               ; no
        XOR     AX,AX                   ; presume NO
IFDEF  DBCS                             ;
        PUSH    AX                      ;
        MOV     AL,DL                   ;
        invoke  TESTKANJ                ; DBCS ?
        POP     AX                      ;
        JNZ     dbcs_char               ; yes, return error
ENDIF                                   ;

;hkn; NLS_YES, NLS_NO, NLS_yes2, NLS_no2 is defined in msdos.cl3 which is
;hkn; included in yesno.asm in the DOSCODE segment.

        CMP     DL,cs:NLS_YES           ; is 'Y' ?
        JZ      yesyes                  ; yes
        CMP     DL,cs:NLS_yes2          ; is 'y' ?
        JZ      yesyes                  ; yes
        CMP     DL,cs:NLS_NO            ; is  'N'?
        JZ      nono                    ; no
        CMP     DL,cs:NLS_no2           ; is 'n' ?
        JZ      nono                    ; no
dbcs_char:                              ;
        INC     AX                      ; not YES or NO
yesyes:                                 ;
        INC     AX                      ; return 1
nono:                                   ;
        transfer SYS_RET_OK             ; done
capstring:                              ;
        MOV     SI,DX                   ; si=dx
        CMP     AL,CAP_STRING           ; cap string ?
        JNZ     capascii                ; no
IFDEF __TONYI__
        jcxz    nono    ; Save 2 bytes
ELSE
        OR      CX,CX                   ; check count 0
        JZ      nono                    ; yes finished
ENDIF
concap:                                 ;
        LODSB                           ; get char
 IFDEF  DBCS                            ;
        invoke  TESTKANJ                ; DBCS ?
        JZ      notdbcs                 ; no
        INC     SI                      ; skip 2 chars
IFDEF __TONYI__1
;------------------------------------------------------
; LOOP is the same as DEC CX/JNZ but a byte smaller.
;------------------------------------------------------
        loop    next99 ; <--not really a loop! ; yes
ELSE
        DEC     CX                      ; bad input, one DBCS char at end
        JNZ     next99                   ; yes
ENDIF
notdbcs:                                ;
 ENDIF                                  ;

        invoke  GETLET3                 ; upper case it
        MOV     byte ptr [SI-1],AL      ; store back
next99:                                 ;
        LOOP    concap                  ; continue
        JMP     SHORT nono                    ; done                  ;AC000;
capascii:                               ;
        CMP     AL,CAP_ASCIIZ           ; cap ASCIIZ string ?
        JNZ     capinval                ; no
concap2:                                ;
        LODSB                           ; get char
        or      al,al                   ; end of string ?
        JZ      nono                    ; yes
 IFDEF  DBCS                            ;
        invoke  TESTKANJ                ; DBCS ?
        JZ      notdbcs2                ; no
        CMP     BYTE PTR [SI],0         ; bad input, one DBCS char at end
        JZ      nono                    ; yes
        INC     SI                      ; skip 2 chars
        JMP     SHORT concap2                 ;                       ;AC000;
notdbcs2:                               ;
 ENDIF                                  ;
        invoke  GETLET3                 ; upper case it
        MOV     byte ptr [SI-1],AL      ; store back
        JMP     SHORT concap2                 ; continue              ;AC000;


notcap:
        CMP     CX,5                    ; minimum size is 5
        jb      short sizeerror

GEC_CONT:
;hkn; SS is DOSDATA
        context DS

;hkn; COUNTRY_CDPG is in DOSDATA
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG

        CMP     DX,-1                   ; active country ?
        JNZ     GETCDPG                 ; no

;hkn; use DS override to accesss country_cdpg fields
        MOV     DX,[SI.ccDosCountry]    ; get active country id;smr;use DS

GETCDPG:
        CMP     BX,-1                   ; active code page?
        JNZ     CHKAGAIN                ; no, check again

;hkn; use DS override to accesss country_cdpg fields
        MOV     BX,[SI.ccDosCodePage]   ; get active code page id;smr;Use DS

CHKAGAIN:
        CMP     DX,[SI.ccDosCountry]    ; same as active country id?;smr;use DS
        JNZ     CHKNLS                  ; no
        CMP     BX,[SI.ccDosCodePage]   ; same as active code pg id?;smr;use DS
        JNZ     CHKNLS                  ; no
CHKTYPE:
        MOV     BX,[SI.ccSysCodePage]   ; bx = sys code page id;smr;use DS
SELONE:
        PUSH    CX                      ; save cx
        MOV     CX,[SI.ccNumber_of_entries]     ;smr;use DS
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG.ccSetUcase;smr;CDPG in DOSDATA
NXTENTRY:
        CMP     AL,[SI]                 ; compare info type;smr;use DS
        JZ      FOUNDIT
        ADD     SI,5                    ; next entry
        LOOP    NXTENTRY
        POP     CX
capinval:
IFDEF __TONYI__2
;------------------------------------------------
; Save a byte....
;------------------------------------------------
NearToInvalidFunc:
        jmp     errtn2
ELSE
        error   error_Invalid_Function  ; info type not found
ENDIF
FOUNDIT:

        MOVSB                           ; move info id byte
        POP     CX                      ; retsore char count
        CMP     AL,SetCountryInfo       ; select country info type ?
        JZ      setsize
        MOV     CX,4                    ; 4 bytes will be moved
        MOV     AX,5                    ; 5 bytes will be returned in CX
OK_RETN:

        REP     MOVSB                   ; copy info
        MOV     CX,AX                   ; CX = actual length returned
        MOV     AX,BX                   ; return sys code page in ax
GETDONE:
        call    get_user_stack          ; return actual length to user's CX
        MOV     [SI.user_CX],CX
        transfer SYS_RET_OK
setsize:
        SUB     CX,3                    ; size after length field
        CMP     WORD PTR [SI],CX        ; less than table size;smr;use ds
        JAE     setsize2                ; no
        MOV     CX,WORD PTR [SI]        ; truncate to table size;smr;use ds
setsize2:
        MOV     ES:[DI],CX              ; copy actual length to user's
IFDEF __TONYI__
        inc     di  ; saves a couple of bytes
        inc     di
        inc     si
        inc     si
ELSE
        ADD     DI,2                    ; update index
        ADD     SI,2
ENDIF
        MOV     AX,CX
        ADD     AX,3                    ; AX has the actual length
        JMP     SHORT OK_RETN                 ; go move it            ;AC000;
CHKNLS:
        XOR     AH,AH
IFDEF __TONYI__2
;------------------------------------------------------------
; Faster.........same size
;------------------------------------------------------------
        mov     bp,ax
ELSE
        PUSH    AX                         ; save info type
        POP     BP                         ; bp = info type
ENDIF
        CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory
        CMP     AL,0FFH
        JZ      NLSNXT                     ;     in memory
sizeerror:
IFDEF __TONYI__2
;------------------------------------------------
; Save a byte....
;------------------------------------------------
NearToInvalidFunc2:
        jmp     short NearToInvalidFunc
ELSE
        error   error_Invalid_Function
ENDIF
NLSNXT: CallInstall GetExtInfo,NLSFUNC,2  ;get extended info
        CMP     AL,0                       ; success ?
        JNZ     NLSERROR
        MOV     AX,[SI.ccSysCodePage]   ; ax = sys code page id;smr;use ds;BUGBUG;check whether DS is OK after the above calls
        JMP     SHORT GETDONE                                         ;AC000;
NLSERROR:
        transfer SYS_RET_ERR            ; return what is got from NLSFUNC

EndProc $GetExtCntry

BREAK <$GetSetCdPg - get or set global code page>

;**     $GetSetCdPg - Get or Set Global Code Page
;
;   System call format:
;
;       MOV     AH,GetSetCdPg   ; DOS 3.3
;       MOV     AL,n            ; n = 1 : get code page, n = 2 : set code page
;       MOV     BX,CODE_PAGE    ( set code page only)
;       INT     21
;
;       ENTRY   (al) = n
;               (bx) = code page
;       EXIT    'C' clear
;                 global code page is set       (set global code page)
;                 (BX) = active code page id    (get global code page)
;                 (DX) = system code page id    (get global code page)
;               'C' set
;                 (AX) = error code

procedure   $GetSetCdPg,NEAR   ; DOS 3.3

;hkn; SS is DOSDATA
        context DS

;hkn; COUNTRY_CDPG is in DOSDATA
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG

        CMP     AL,1                   ; get global code page
        JNZ     setglpg                ; set global cod epage
        MOV     BX,[SI.ccDosCodePage]  ; get active code page id;smr;use ds
        MOV     DX,[SI.ccSysCodePage]  ; get sys code page id;smr;use ds
        call    get_user_stack
ASSUME DS:NOTHING
IFDEF __TONYI__2
;--------------------------------------
; Save a byte...
;--------------------------------------
        call    SetUserBX
ELSE
        MOV     [SI.user_BX],BX        ; update returned bx
ENDIF
        MOV     [SI.user_DX],DX        ; update returned dx
OK_RETURN:
        transfer SYS_RET_OK
ASSUME  DS:DOSDATA

setglpg:
        CMP     AL,2
        JNZ     nomem
        MOV     DX,[SI.ccDosCountry]                    ;smr;use ds
        CallInstall NLSInstall,NLSFUNC,0 ; check if NLSFUNC in memory
        CMP     AL,0FFH
        JNZ     nomem                      ; not in memory
        CallInstall SetCodePage,NLSFUNC,1  ;set the code page
        or      al,al                      ; success ?
        JZ      OK_RETURN                  ; yes
        CMP     AL,65                      ; set device code page failed
        JNZ     seterr
IFDEF __TONYI__
        cbw     ;; Save a couple of bytes. AL *IS* 65 right now
                ;; if we get to this point right?
ELSE
        MOV     AX,65
ENDIF
        MOV     [EXTERR],AX
        MOV     [EXTERR_ACTION],errACT_Ignore
        MOV     [EXTERR_CLASS],errCLASS_HrdFail
        MOV     [EXTERR_LOCUS],errLOC_SerDev
        transfer   From_GetSet

seterr:
IFDEF __TONYI__2
;-----------------------------------------
; Save a byte.....
;-----------------------------------------
        jmp     short NLSERROR
ELSE
        transfer  SYS_RET_ERR
ENDIF
nomem:
IFDEF __TONYI__2
;------------------------------------------------
; Save a byte....
;------------------------------------------------
NearToInvalidFunc3:
        jmp     short NearToInvalidFunc2
ELSE
        error   error_Invalid_Function ; function not defined
ENDIF
;
EndProc $GetSetCdPg


BREAK <$Get_Drive_Freespace -- Return bytes of free disk space on a drive>


;**     $Get_Drive_Freespace - Return amount of drive free space
;
;       $Get_Drive_Freespace returns the # of free allocation units on a
;               drive.
;
;       This call returns the same info in the same registers (except for the
;       FAT pointer) as the old FAT pointer calls
;
;       ENTRY   DL = Drive number
;       EXIT    AX = Sectors per allocation unit
;                  = -1 if bad drive specified
;               On User Stack
;                   BX = Number of free allocation units
;                   DX = Total Number of allocation units on disk
;                   CX = Sector size

procedure   $GET_DRIVE_FREESPACE,NEAR

;hkn; SS is DOSDATA
        context DS

        MOV     AL,DL
        invoke  GetThisDrv              ; Get drive
SET_AX_RET:
        JC      BADFDRV
        invoke  DISK_INFO
        XCHG    DX,BX
        JC      SET_AX_RET              ; User FAILed to I 24
        XOR     AH,AH                   ; Chuck Fat ID byte
DoSt:
        call    get_user_stack
ASSUME  DS:NOTHING
        MOV     [SI.user_DX],DX
IFDEF __TONYI__2
;------------------------------------------------
; Save a bunch.....
;------------------------------------------------
        jmp     SetUserAXBXCX
ELSE
        MOV     [SI.user_CX],CX
        MOV     [SI.user_BX],BX
        MOV     [SI.user_AX],AX
        return
ENDIF
BADFDRV:
        invoke  FCB_RET_ERR
        MOV     AX,-1
        JMP     SHORT DoSt                                            ;AC000;
EndProc $GET_DRIVE_FREESPACE

        BREAK <$Get_DMA, $Set_DMA -- Get/Set current DMA address>


;**     $Get_DMA - Get Disk Transfer Address
;
;       ENTRY   none
;       EXIT    ES:BX is current transfer address
;       USES    all

procedure   $GET_DMA,NEAR

;hkn; ss override for DMAADD
        MOV     BX,WORD PTR [DMAADD]
        MOV     CX,WORD PTR [DMAADD+2]
        call    get_user_stack
    ASSUME DS:nothing
IFDEF __TONYI__2
;------------------------------------------------
; Save a few bytes.....
;------------------------------------------------
        MOV     [SI.user_ES],CX
        jmp     SetUserBX
ELSE
        MOV     [SI.user_BX],BX
        MOV     [SI.user_ES],CX
        return
ENDIF
EndProc $GET_DMA



;**     $Set_DMA - Set Disk Transfer Address
;
;       ENTRY   DS:DX is current transfer address
;       EXIT    none
;       USES    all

procedure   $SET_DMA,NEAR
;hkn; ss override for DMAADD
        MOV     WORD PTR [DMAADD],DX
        MOV     WORD PTR [DMAADD+2],DS
        return

EndProc $SET_DMA

        BREAK <$Get_Default_Drive, $Set_Default_Drive -- Set/Get default drive>

;**     $Get_Default_Drive - Get Current Default Drive
;
;       ENTRY   none
;       EXIT    (AL) = drive number
;       USES    all

procedure   $GET_DEFAULT_DRIVE,NEAR

;hkn; SS override
        MOV     AL,[CURDRV]
        return

EndProc $GET_DEFAULT_DRIVE


;**     $Set_Default_Drive - Specify new Default Drive
;
;       $Set_Default_Drive sets a new default drive.
;
;       ENTRY   (DL) = Drive number for new default drive
;       EXIT    (AL) = Number of drives, NO ERROR RETURN IF DRIVE NUMBER BAD

procedure   $SET_DEFAULT_DRIVE,NEAR

        MOV     AL,DL
        INC     AL                      ; A=1, b=2...
        invoke  GetVisDrv               ; see if visible drive
        JC      SETRET                  ; errors do not set

;hkn; SS override
        MOV     [CURDRV],AL             ; no, set

SETRET:

;hkn; SS override
        MOV     AL,[CDSCOUNT]           ; let user see what the count really is
        return

EndProc $SET_DEFAULT_DRIVE

BREAK <$Get/Set_Interrupt_Vector - Get/Set interrupt vectors>


;**     $Get_Interrupt_Vector - Get Interrupt Vector
;
;       $Get_Interrupt_Vector is the official way for user pgms to get the
;       contents of an interrupt vector.
;
;       ENTRY   (AL) = interrupt number
;       EXIT    (ES:BX) = current interrupt vector

procedure   $GET_INTERRUPT_VECTOR,NEAR

        CALL    RECSET
        LES     BX,DWORD PTR ES:[BX]
        call    get_user_stack
IFDEF __TONYI__2
;--------------------------------------
; Save a few bytes.......
;--------------------------------------
SetUserESBX:
        MOV     [SI.user_ES],ES
        jmp     SetUserBX
ELSE
        MOV     [SI.user_BX],BX
        MOV     [SI.user_ES],ES
        return
ENDIF
EndProc $GET_INTERRUPT_VECTOR



;**     $Set_Interrupt_Vector - Set Interrupt Vector
;
;       $Set_Interrupt_Vector is the official way for user pgms to set the
;       contents of an interrupt vector.
;
;       M004, M068: Also set A20OFF_COUNT to 1 if EXECA20OFF bit has been set
;       and if A20OFF_COUNT is non-zero. See under tag M003 in inc\dossym.inc
;       for explanation.
;
;       ENTRY   (AL) = interrupt number
;               (ds:dx) = desired new vector value
;       EXIT    none
;       USES    all

procedure   $SET_INTERRUPT_VECTOR,NEAR

        CALL    RECSET
        CLI                             ; Watch out!!!!! Folks sometimes use
        MOV     ES:[BX],DX              ;   this for hardware ints (like timer).
        MOV     ES:[BX+2],DS
        STI
                                        ; M004, M068 - Start
        test    [DOS_FLAG], EXECA20OFF
                                        ; Q: was the previous call an int 21
                                        ;    exec call
        jnz     @f                      ; Y: go set count
        return                          ; N: return

@@:
        cmp     [A20OFF_COUNT], 0       ; Q: is count 0
        jne     @f                      ; N: done
        mov     [A20OFF_COUNT], 1       ; Y: set it to 1 to indicate to dos
                                        ; dispatcher to turn A20 Off before
                                        ; returning to user.
@@:
        ret                             ; M004, M068 - End

EndProc $SET_INTERRUPT_VECTOR

procedure   RECSET,NEAR

        IF      ALTVECT
        context ES

;hkn; SS override for LSTVEC and LSTVEC2
        MOV     [LSTVEC],AL             ; Terminate list with real vector
        MOV     [LSTVEC2],AL            ; Terminate list with real vector
        MOV     CX,NUMVEC               ; Number of possible translations

;hkn; VECIN is in DOSDATA
        MOV     DI,OFFSET DOSDATA:VECIN    ; Point to vectors

        REPNE   SCASB
        MOV     AL,ES:[DI+NUMVEC-1]     ; Get translation
        ENDIF

        XOR     BX,BX
        MOV     ES,BX
        MOV     BL,AL
        SHL     BX,1
        SHL     BX,1
        return
EndProc recset

        BREAK <$Char_Oper - hack on paths, switches so that xenix can look like PCDOS>


;**     $Char_Oper - Manipulate Switch Character
;
;       This function was put in to facilitate XENIX path/switch compatibility
;
;       ENTRY   AL = function:
;                   0 - read switch char
;                   1 - set switch char (char in DL)
;                   2 - read device availability
;                       Always returns available
;                   3 - set device availability
;                       No longer supported (NOP)
;       EXIT    (al) = 0xff iff error
;               (al) != 0xff if ok
;                 (dl) = character/flag, iff "read switch char" subfunction
;       USES    AL, DL
;
;       NOTE    This already obsolete function has been deactivated in DOS 5.0
;               The character / is always returned for subfunction 0,
;               subfunction 2 always returns -1, all other subfunctions are ignored.

procedure   $CHAR_OPER,NEAR

        or      al,al                           ; get switch?
        mov     dl,'/'                          ; assume yes
        jz      chop_1                          ; jump if yes
        cmp     al,2                            ; check device availability?
        mov     dl,-1                           ; assume yes
        jz      chop_1                          ; jump if yes
        return                                  ; otherwise just quit

; subfunctions requiring return of value to user come here.  DL holds
; value to return

chop_1:
        call    Get_User_Stack
        ASSUME  DS:Nothing
IFDEF __TONYI__2
SetUserDX:
ENDIF
        mov     [si].User_DX,dx                 ; store value for user
        return


EndProc $CHAR_OPER

BREAK <$GetExtendedError - Return Extended DOS error code>


;**     $GetExtendedError - Return Extended error code
;
;       This function reads up the extended error info from the static
;       variables where it was stored.
;
;       ENTRY   none
;       EXIT    AX = Extended error code (0 means no extended error)
;               BL = recommended action
;               BH = class of error
;               CH = locus of error
;               ES:DI = may be pointer
;       USES    ALL

procedure   $GetExtendedError,NEAR

;hkn; SS is DOSDATA
        Context DS
        MOV     AX,[EXTERR]
        LES     DI,[EXTERRPT]
        MOV     BX,WORD PTR [EXTERR_ACTION]     ; BL = Action, BH = Class
        MOV     CH,[EXTERR_LOCUS]
        call    get_user_stack
ASSUME  DS:NOTHING
        MOV     [SI.user_DI],DI
IFDEF __TONYI__2
;-------------------------------------------------
; Save several bytes....
;-------------------------------------------------
        call    SetUserESBX
ELSE
        MOV     [SI.user_ES],ES
        MOV     [SI.user_BX],BX
ENDIF
        MOV     [SI.user_CX],CX
IFDEF __TONYI__2
NearTo_SYS_RET_OK:
ENDIF
        transfer SYS_RET_OK

EndProc $GetExtendedError

BREAK <$Get_Global_CdPg  - Return Global Code Page>
;---------------------------------------------------------------------------
;
; input:    None
; output:   AX = Global Code Page
;
;---------------------------------------------------------------------------

Procedure   Get_Global_CdPg,NEAR
        PUSH    SI

;hkn; COUNTRY_CDPG is in DOSDATA
        MOV     SI,OFFSET DOSDATA:COUNTRY_CDPG
        MOV     AX,SS:[SI.ccDosCodePage]                        ;smr;CS->SS
        POP     SI
        return

EndProc Get_Global_CdPg

;-------------------------------Start of DBCS 2/13/KK
BREAK   <ECS_call - Extended Code System support function>

;---------------------------------------------------------------------------
; Inputs:
;       AL = 0  get lead byte table
;               on return DS:SI has the table location
;
;       AL = 1  set / reset interim console flag
;               DL = flag (00H or 01H)
;               no return
;
;       AL = 2  get interim console flag
;               on return DL = current flag value
;
;       AL = OTHER then error, and returns with:
;               AX = error_invalid_function
;
;  NOTE: THIS CALL DOES GUARANTEE THAT REGISTER OTHER THAN
;        SS:SP WILL BE PRESERVED!
;---------------------------------------------------------------------------

procedure   $ECS_call,NEAR

 IFDEF  DBCS                                                                    ;AN000;
                                                                                ;AN000;
        or      al, al                  ; AL = 0 (get table)?                   ;AN000;
        je      get_lbt                                                         ;AN000;
        cmp     al, SetInterimMode      ; AL = 1 (set / reset interim flag)?    ;AN000;
        je      set_interim                                                     ;AN000;
        cmp     al, GetInterimMode      ; AL = 2 (get interim flag)?            ;AN000;
        je      get_interim                                                     ;AN000;
        error   error_invalid_function                                          ;AN000;
                                                                                ;AN000;
get_lbt:                                ; get lead byte table                   ;AN000;
        push    ax                                                              ;AN000;
        push    bx                                                              ;AN000;
        push    ds                                                              ;AN000;

;hkn; SS is DOSDATA
        context DS                                                              ;AN000;
        MOV     BX,offset DOSDATA:COUNTRY_CDPG.ccSetDBCS                        ;AN000;
        MOV     AX,[BX+1]               ; set EV address to DS:SI               ;AN000;smr;use ds
        MOV     BX,[BX+3]                                                       ;AN000;smr;use ds
        ADD     AX,2                    ; Skip Lemgth                           ;AN000;
        call    get_user_stack                                                  ;AN000;
 assume ds:nothing                                                              ;AN000;
        MOV     [SI.user_SI], AX                                                ;AN000;
        MOV     [SI.user_DS], BX                                                ;AN000;
        pop     ds                                                              ;AN000;
        pop     bx                                                              ;AN000;
        pop     ax                                                              ;AN000;
IFDEF __TONYI__2
;-------------------------------------------
; Save a byte......
;-------------------------------------------
        jmp     short NearTo_SYS_RET_OK
ELSE
        transfer SYS_RET_OK                                                     ;AN000;
ENDIF

set_interim:                            ; Set interim console flag              ;AN000;
        push    dx                                                              ;AN000;
        and     dl,01                   ; isolate bit 1                         ;AN000;

;hkn; SS override
        mov     [InterCon], dl                                                  ;AN000;
        push    ds
;hkn; SS override
        mov     ds, [CurrentPDB]                                                ;AN000;
        mov     byte ptr ds:[PDB_InterCon], dl  ; update value in pdb           ;AN000;
        pop     ds                                                              ;AN000;
        pop     dx                                                              ;AN000;
IFDEF __TONYI__2
;-------------------------------------------
; Save a byte......
;-------------------------------------------
        jmp     short NearTo_SYS_RET_OK
ELSE
        transfer SYS_RET_OK                                                     ;AN000;
ENDIF

get_interim:                                                                    ;AN000;
        push    dx                                                              ;AN000;
        push    ds                                                              ;AN000;
;hkn; SS override
        mov     dl,[InterCon]                                                   ;AN000;
        call    get_user_stack          ; get interim console flag              ;AN000;
 assume ds:nothing                                                              ;AN000;
IFDEF __TONYI__2
;----------------------------------------------
; Save a byte..
;----------------------------------------------
        call    SetUserDX
ELSE
        mov     [SI.user_DX],DX                                                 ;AN000;
ENDIF
        pop     ds                                                              ;AN000;
        pop     dx                                                              ;AN000;
IFDEF __TONYI__2
;-------------------------------------------
; Save a byte......
;-------------------------------------------
        jmp     short NearTo_SYS_RET_OK
ELSE
        transfer SYS_RET_OK                                                     ;AN000;
ENDIF
 ELSE                                                                           ;AN000;
        or      al, al                  ; AL = 0 (get table)?
        jnz     okok
get_lbt:
        call    get_user_stack
 assume ds:nothing

;hkn; dbcs_table moved low to dosdata
        MOV     [SI.user_SI], Offset DOSDATA:DBCS_TAB+2

        push    es
        getdseg <es>                    ; es = DOSDATA
        assume  es:nothing
        MOV     [SI.user_DS], es
        pop     es

okok:
IFDEF __TONYI__2
;-------------------------------------------
; Save a byte......
;-------------------------------------------
        jmp     short NearTo_SYS_RET_OK
ELSE
        transfer SYS_RET_OK             ;
ENDIF

 ENDIF

$ECS_call endp

DOSCODE ENDS
        END
