;       SCCSID = @(#)d_casemp.asm	6.1 91/02/15
;       SCCSID = @(#)d_casemp.asm	1.6 90/01/22
; ****************************************************************************
; *									     *
; *			  IBM/Microsoft Confidential			     *
; *									     *
; *		    Copyright (c) IBM Corporation  1987, 1990		     *
; *		    Copyright (c) Microsoft Corp.  1987, 1990		     *
; *			      All Rights Reserved			     *
; *									     *
; ****************************************************************************
;
;       06-Jan-87 (billme): Replace initial pushes and final pops
;       with calls to __api_begin and __api_end resp.  This will
;       reduce the size of FAPI and allow support for signals.
;
;       13-Jan-87 (billme): Clean up version code; call local version
;       provider (__api_getvrsn) and revise the logic to cut code size.
;
; @@    2/5/87  CWY:  add the section to compare if the character is in DBCS
;                     range, and modify the part about look_in_table. The
;                     first word in casemap table is the length of the table.
;
; @@    Apr-3-87 (GAA): Check for zero string length and return.
;
; @@    4/22/87    CWY: Since DOS3.3 do not support DBCS table, but in CPDOS
;                       real and protect mode, We do support DBCS table, we
;                       have to bypass DBCS table if the table
;                       is not there.
;                       Beware the following fact:
;                         When running under DOS3.3 using "interrupt" call
;                         int 21-6507 (getdbcs), only failed for the
;                         the current country and codepage combination.
;                         int 21-6507 should failed for other combination
;                         but it did not.
;
;
;       04-May-87 (robertc): Install hooks for DOSErrorClass Support.
;       All int 21h errors are passed through api_get_extended to get the
;       extended errorcode and to provide the class, action, and locus for
;       the dynamic error FIFO queue.
;
; @@    6/03/87    CWY: Now DOSCASEMAP will check for the valid country and
;                       codepage before it checks for the BUFFER length
;                       in order to compatible with protect mode.
;                       see ptm5173.
;
; @@    6/10/87    Tarng: fix unreachable code for the dbcs_flag set to 1.
;       6/17/87    Rasmussen: fix check_DBCS to not trash the stack.
; @@    7/21/87    Giacomoa: since DBCS environment always returns a trailing
; @@               word of zeroes - subtract word length (2) from the table
; @@               length in check_DBCS routine to indicate when true
; @@               end of table has been reached.
;       3/8/88     check_dbcs should not set carry if dbcs not supported
;
;
;/*************** START OF SPECIFICATIONS ******************************/
;/*                                                                    */
;/* SUBROUTINE NAME: DosCaseMap                                        */
;/*                                                                    */
;/* SOURCE FILE NAME: D_CaseMp.asm                                     */
;/*                                                                    */
;/* DESCRIPTIVE NAME: Dos Case Map                                     */
;/*                                                                    */
;/* FUNCTION:  Performs case mapping on a string                       */
;/*                                                                    */
;/* NOTES:  @@ DosCaseMap is a new routine to the 1.10 Family API      */
;/*         Library.                                                   */
;/*                                                                    */
;/* ENTRY POINT:                                                       */
;/*         LINKAGE:  EXTRN DosCaseMap:FAR                             */
;/*                                                                    */
;/* INPUT:  PUSH     WORD    Length       ;Length of string to case map*/
;/*         PUSH@    OTHER   Structure    ;Address of country code     */
;/*                                       ;and code page               */
;/*         PUSH@    OTHER   BinaryString ;Address of string of binary */
;/*                                       ;values to case map          */
;/*         call  DosCaseMap                                           */
;/*                                                                    */
;/*                                                                    */
;/* EXIT-NORMAL:  AX = 0                                               */
;/*                                                                    */
;/* EXIT-ERROR:  AX = Error Code                                       */
;/*                                                                    */
;/*    ReturnCode Value = 1, Error_invalid_function                    */
;/*                       ?, Country code not found in file            */
;/*                       ?, Case Map information not found            */
;/*                       ?, Code page value not found                 */
;/*                                                                    */
;/* EFFECTS:  BP, ES, DS, DI, SI, BX, CX, and DX, registers are saved. */
;/*                                                                    */
;/* PSUEDO CODE:                                                       */
;/*                                                                    */
;/*     save registers                                                 */
;/*                                                                    */
;/*     get DOS version                                                */
;/*                                                                    */
;/*     If version < 3.30                                              */
;/*         then AX=invalid DOS version and goto DosCaseMapExit        */
;/*                                                                    */
;/*     use BP as pointer to stack parameters                          */
;/*                                                                    */
;/*     load registers for int 21 call                                 */
;/*       al = 02 = Get Case Map table address                         */
;/*       bx = codepage                                                */
;/*       dx = country_id                                              */
;/*       cx = length                                                  */
;/*       es:di = address of country_info (internal buffer)            */
;/*                                                                    */
;/*     execute int 21 with function 6502H                             */
;/*                                                                    */
;/*     if carry flag set goto DOSCaseMapExit                          */
;/*                                                                    */
;/*     casetblseg = segment of case map table                         */
;/*                  (bytes 4 and 5 in country_info)                   */
;/*     casetbloff = offset of case map table                          */
;/*                  (bytes 2 and 3 in country_info)                   */
;/*                                                                    */
;/*     load registers for int 21 call                                 */
;/*       al = 07 = Get DBCS table address                             */
;/*       bx = codepage                                                */
;/*       dx = country_id                                              */
;/*       cx = length                                                  */
;/*       es:di = address of country_info (internal buffer)            */
;/*                                                                    */
;/*     execute int 21 with function 6507H                             */
;/*                                                                    */
;/*     if carry flag set goto DOSCaseMapExit                          */
;/*                                                                    */
;/*     dbcstblseg = segment of dbcs table                             */
;/*                  (bytes 4 and 5 in country_info)                   */
;/*     dbcstbloff = offset of dbcs table                              */
;/*                  (bytes 2 and 3 in country_info)                   */
;/*                                                                    */
;/*     Perform Case Map using tables pointed at by                    */
;/*     casetblseg:casetbloff and dbcstblseg:dbcstbloff                */
;/*                                                                    */
;/*     For I = 1 to Length                                            */
;/*                                                                    */
;/*         Begin                                                      */
;/*         Char = Character at BinaryString[I]                        */
;/*         If Char is in table dbcstblseg:dbcstbloff                  */
;/*             Then I=I+2   (skip over double byte character)         */
;/*         Else                                                       */
;/*             If ((ascii value Char) < 128)                          */
;/*                 Then if ( "a" <= (ascii value Char) >= "z"         */
;/*                     Then BinaryString[I] = Ascii value             */
;/*                          Char + "A" - "a"*/                        */
;/*                                                                    */
;/*                 Else BinaryString[I] = Character in CaseMapTable   */
;/*                      at the                                        */
;/*                      location (Ascii value Char - 128)             */
;/*                                                                    */
;/*         End                                                        */
;/*                                                                    */
;/*     AX=0                                                           */
;/*                                                                    */
;/*DosCaseMapExit:                                                     */
;/*                                                                    */
;/*     restore registers                                              */
;/*                                                                    */
;/*     return clearing stack frame                                    */
;/*                                                                    */
;/*                                                                    */
;/* INTERNAL REFERENCES:                                               */
;/*    ROUTINES:                                                       */
;/*                                                                    */
;/* EXTERNAL REFERENCES:                                               */
;/*    ROUTINES:    INT 21H FUNCTION 6502H  GET CASE MAP TABLE ADDRESS */
;/*                 __api_begin                                        */
;/*                 __api_end                                          */
;/*                 _api_getvrsn                                       */
;/*                                                                    */
;/*                                                                    */
;/*************** END OF SPECIFICATIONS ********************************/
;
;
; LIBRARIES
;
.XLIST
include struc.inc
include basemaca.inc
include realmac.inc
include error2.inc
include error.inc

        EXTRN   __api_begin:near
        EXTRN   __api_end:near
        EXTRN   _api_getvrsn:near
        EXTRN   api_get_extended:far    ;@@ added support for DOSErrorClass
.LIST
;
; PUBLIC SYMBOLS
;
        PUBLIC  DosCaseMap

        PAGE

_TEXT   segment byte public 'CODE'
        assume  CS:_TEXT,DS:NOTHING

country_info    db      5  dup (0)              ; Buffer for returning from
                                                ; Get Extended Country Info call
country_id      dw      0                       ; Country to return info for
                                                ; Environment
size_cntry_info dw      5                       ; Size of country_info
casetblseg      dw      0                       ; Segment of case map table
casetbloff      dw      0                       ; Offset of case map table
dbcstblseg      dw      0                       ; Segment of dbcs table
dbcstbloff      dw      0                       ; Offset of case map table
casetbl         db      130 dup (" ")           ; buffer for case map table

dbcs_flag       db      0                       ; default is no DBCS
;
; stack frame:
; +0    bp
; +2    ip
; +4    cs
; +6    ptr_binarystr
; +10   ptr_structure
; +14   strlength
;
str     struc
old_bp          dw      ?
return_address  dd      ?       ; @@ label name change to include dosmac.inc
ptr_binarystr   dd      ?
ptr_structure   dd      ?
strlength       dw      ?
str     ends
;
DosCaseMap      PROC    FAR
        call    __api_begin     ; init bp and save bp,ds,bx,cx,dx,si,di,es
                                ; required to save: bp,es,ds,di,si,bx,cx,dx
;
                push    cs                      ; Set DS to CS so we can get to
                pop     ds                      ; our own variables
;
; Check DOS version to make sure PC DOS 3.30 or higher
;

                call    _api_getvrsn            ; AH <- major, AL <- minor

                cmp     ax,031Eh                ; if version >= 3.30
                jae     valid_version           ; then version is valid
                jmp     DOS_Version_error       ; else version is NOT valid

valid_version:
                cmp     ax,0328h                ; is version 3.40 or above?
                jb      part_valid              ;  no, does not support dbcs
                mov     dbcs_flag, 1            ; @@ then DBCS exists, p4173
part_valid:

;
; Now set up get Case Map Table interrupt
;
                push    ds                      ; Save DS
                lds     di,[bp].ptr_structure   ; Ptr_structure points to a
                mov     dx,word ptr ds:[di]     ; double word.  First word is
                mov     bx,word ptr ds:[di+2]   ; country code of interest.
                                                ; Second word is code page of
                                                ; interest.
                pop     ds                      ; Restore DS
                or      dx,dx                   ; If country code = 0, then
                jne     not_default_cntrycode   ; default is used, in PC DOS
                dec     dx                      ; 3.30, -1 says use active
not_default_cntrycode:
                or      bx,bx                   ; If code page = 0, then
                jne     not_default_codepage    ; current is used, in PC DOS
                dec     bx                      ; 3.30, -1 says use active
not_default_codepage:
                mov     cx,size_cntry_info      ; CX = Size of cntry_info
                push    ds
                pop     es
                mov     di,offset country_info  ; ES:DI = buffer to fill
                mov     ax,6502H                ; Get Case Map Table address
                int     21h
                jnc     got_case_table          ; Get dbcs table if no
                                                ; error, else exit
                jmp     DosCaseMapErr
;
got_case_table:
                xor     ax,ax                   ; @@ assume no error
                cmp     [bp].strlength, ax      ; @@ input string length = 0?
                ljz     DosCaseMapexit          ; @@ yes, so we're done
;
;
; The PC DOS Get Extended Country Info call returns in the buffer provided:
;     First byte = item requested (in this case 02 for get Case Map table)
;     Bytes 2 and 3 = Offset of Case Map table
;     Bytes 4 and 5 = Segment of Case Map table
; THEN copy Case Map table to the local area
                mov     di,offset country_info
                                                ; Get address of case map table
;               mov     ax,word ptr ds:[di+1]
;               mov     casetbloff,ax
;               mov     ax,word ptr ds:[di+3]
;               mov     casetblseg,ax

                mov     si,word ptr ds:[di+1]   ; @@ si -> to offset
                push    ds                      ; @@
                mov     ax,word ptr ds:[di+3]   ; @@
                mov     ds,ax                   ; @@ now ds:si -> seg:offset
                mov     di,offset casetbl       ; @@ di -> to local case tbl
                mov     cx, 65                  ; case table length, 65 words
                cld                             ; @@ clear DF to auto increment
          rep   movsw                           ; @@
                mov     ax, es                  ; @@
                mov     casetblseg, ax          ; @@
                mov     ax, offset  casetbl     ; @@
                mov     casetbloff, ax          ; @@

                pop     ds                      ; @@


;
; Get DBCS table address
;
                mov     cx,size_cntry_info      ; CX = Size of cntry_info
                push    ds
                pop     es
                mov     di,offset country_info  ; ES:DI = buffer to fill
                mov     ax,6507H                ; Get DBCS Table address
                int     21h
                jnc     got_dbcs_table          ; Go perform case map if no
                                                ; error, else exit
                .IF  < ax EQ ERROR_INVALID_FUNCTION > ; @@ DBCS not available
                      jmp  case_string          ; @@ go ahead do the casemap
                .ELSE                           ; @@
                      jmp  DosCaseMaperr        ;@@ added support for DOSErrorClass
                .ENDIF                          ; @@
;
got_dbcs_table:
;
; The PC DOS Get Extended Country Info call returns in the buffer provided:
;     First byte = item requested (in this case 07 for get DBCS table)
;     Bytes 2 and 3 = Offset of DBCS table
;     Bytes 4 and 5 = Segment of DBCS table
;
                mov     di,offset country_info
                mov     ax,word ptr ds:[di+1]
                mov     dbcstbloff,ax
                mov     ax,word ptr ds:[di+3]
                mov     dbcstblseg,ax
;
; Perform case mapping
;
case_string:
                les     di,[bp].ptr_binarystr   ; Destination of move is app's
                                                ; buffer
;
; At this point, casetblseg:casetbloff points to Case Map Table,
; dbcstblseg:dbcstbloff points to the dbcs table,
; and ES:DI points to string to case map
;
                mov     cx,[bp].strlength       ; CX = Length of binary string
Next_string_char:
                call check_DBCS                 ;
                jc      Character_Done          ; @@ do not upshift is the
                                                ; @@ char is DBCS.
comp_alpha:                                     ; @@
                cmp     byte ptr es:[di],127    ; See if ascii value > 127
                ja      look_in_table           ; if so look in case map table
                cmp     byte ptr es:[di],"a"    ; else is ascii "a" - "z"
                jb      Character_done          ; if not character is done
                cmp     byte ptr es:[di],"z"
                ja      Character_done
;
; Character is between "a" - "z".  So map to corresponding uppercase character.
;
                add     byte ptr es:[di],"A"-"a" ; Convert to uppercase ascii
                jmp     Character_done
;
; Character is > 127 so we will have to look in casemap table at position
; (ascii value current character - 128).  casetblseg:casetbloff points to
; beginning of casemap table.
;
Look_in_table:
                xor     ah,ah
                mov     al,byte ptr es:[di]     ; Get character
                push    ds
                push    casetbloff              ; Save casetbloff
                add     casetbloff,ax           ; Set casetbloff to proper
                                                ;  offset
                sub     casetbloff,126          ; @@ (128 - 2) 2 is the tbl len
                mov     ds,casetblseg           ;
                mov     bx,word ptr cs:[casetbloff] ; @@
                mov     al,byte ptr ds:[bx]     ;@@  get the casemaped char
                mov     byte ptr es:[di],al     ;@@ move to user BUF
                pop     casetbloff              ; Restore casetbloff
                pop     ds
Character_done:
                inc     di                      ; Point to next character
                loop    Next_string_char        ; Case map next character
                xor     ax,ax                   ; No errors returned
                jmp     DosCaseMapexit
Dos_version_error:
                mov     ax,error_invalid_function ; Return invalid function
                jmp     DosCaseMapexit
DosCaseMapErr:
                call    api_get_extended ;@@ added support for DOSErrorClass
DosCaseMapexit:

        call    __api_end       ; check sigs & restore es,di,si,dx,cx,bx,ds,bp

                ret     size str - 6            ; Clear stack frame
DosCaseMap      ENDP


check_DBCS      PROC    NEAR
;
;   if byte ptr es[di] is in table pointed at by dbcstblseg:dbcstbloff
;       then            ; character is leading byte of dbcs character
;           begin
;           inc di      ; increment string pointer one extra time to get past
;                       ; second byte of dbcs character
;
;           dec cx      ; decrement counter one extra time to get past second
;                       ; byte of dbcs character
;           set carry
;           end
;
 .IF  < dbcs_flag EQ 1 >                        ; @@ if DBCS exists
                push    cx                      ; @@
                push    ds                      ; @@
                mov     bx, word ptr  ds:[dbcstbloff] ; @@
                mov     ds,dbcstblseg           ; @@
                mov     cx, word ptr  ds:[bx]   ; @@ cx has the tbl len
                mov     bx,dbcstbloff           ; @@ Get offset
dbcs_loop:                                      ; @@

; check if DOS 4.0 is used b785619
        cmp     cx,0                            ; @@ any lead byte range ??
        jz      dos40fix
        sub     cx,2                            ; @@ tbl len includes null word
dos40fix:
        or      cx,cx                           ; @@ any lead byte range ??
    .WHILE NZ                                   ; @@
        add     bx,2                            ; @@ Point to next range
        mov     al,byte ptr es:[di]             ; @@ Get input character
        .IF  < al GE <byte ptr ds:[bx]>> AND    ; @@
        .IF  < al LE <byte ptr ds:[bx+1]>>      ; @@
            inc di      ; increment string pointer one extra time to get past
                        ; second byte of dbcs character
            pop     ds                          ; @@
            pop     cx                          ; @@
            dec     cx                          ; @@
            stc                                 ; @@
            ret                                 ; @@
        .ENDIF                                  ; @@
        sub     cx,2                            ; @@
    .ENDWHILE                                   ; @@
                pop     ds                      ; @@
                pop     cx                      ; @@
                clc                             ; @@
                ret
 .ENDIF                                         ; @@ end of checking DBCS
  clc                                           ; @@ clear carry if no dbcs;
  ret                                       ; @@ discard the ax value on stack
check_DBCS      ENDP

_TEXT   ends
                END
