;       SCCSID = @(#)ansiint.asm	6.2 91/02/23
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
        PAGE    60,132
        TITLE   ANSIINTP.ASM - ANSI Control Code Sequence Interpreter

;********************** START OF SPECIFICATIONS *******************************
;*
;*   Subroutine Name:  ANSIInterp
;*
;*   Descriptive Name:  ANSI Control Code Sequence (ACCS) Interpreter
;*
;*   Function:
;*          Scans the ASCIIZ string at bvs_TTYoff.  Calls doAnsiFn to handle
;*          the requested ANSI function.  Updates string offset and length
;*          fields in the current BVSCB.
;*
;*   Notes: Detection of ESC occurs in calling routine.  At entry, bvs_TTYoff
;*          typically points to ESC char (EXCEPT when the call is a continua-
;*          tion of previous ESC sequence).  If character following ESC is not
;*          "[" an immediate EXIT occurs.  The interpreter changes state as
;*          each character is processed until a term char or invalid char is
;*          read, whichever occurs first.  At exit, bvs_TTYoff points to the
;*          to the character immediately following the last valid char in the
;*          input string. (preceeding chars having been "eaten").  At exit,
;*          bvs_TTYlen is the number of characters remaining in the string.
;*
;*          On each call from BvsWrtTTY, AnsiInterp is responsible
;*          for checking the bvs_Kd_CP0 word in the bvscb to see
;*          if initialization should be done.  When bvs_Kd_CP0
;*          is 0, indicating a new session had been created, this
;*          routine must allocate the initial KeyDef segments to
;*          support each codepage in this system.
;*
;*   Entry Point:  ANSIInterp
;*       Linkage:  CALL FAR - dynamic link routine
;*
;*   Input:  address of BVSCB pushed on stack:
;*              PUSH    WORD    BVSCB   ; selector for bvs control block
;*
;*    NOTE:  BVSCB contains numerous fields used by ANSI, such as:
;*
;*              bvscb.bvs_TTYsel        ; selector of input string
;*              bvscb.bvs_TTYoff        ; offset of input string
;*              bvscb.bvs_TTYlen        ; length of input string
;*              bvscb.bvs_scrattr       ; screen cell attribute byte
;*              bvscb.bvs_flags         ; bvs flags (ie. _ansiwrap, _ansiprog)
;*
;*   Exit - NORMAL: AX = 0 (no error)
;*
;*   Exit - ERROR:  AX > 0  (INT 3)
;*
;*   Internal References:  ANSIDAT      ; Per process data segment
;*            Routines:    DoAnsiFn     ; ANSI Function dispatch routine
;*
;*   External References:  BVSCB        ; Base Video Subsysem control block
;*            Routines:    None
;*
;***********************  END OF SPECIFICATIONS  ******************************
;
;   Since the AnsiInterp routine is actually a Finite State machine, the
;   value of bvscb.bvs_AIStatus may take on one of the following:
;       value = state0 :  START NEW SEQUENCE (waiting for ESC char)
;       value = state1 :  ESCAPE SEQUENCE (waiting for LEFT_BRACKET)
;       value = state2 :  EXPECTING NEW PARAMETER OR TERMINATOR
;       value = state3 :  BUILDING PARAMETER VALUE
;       value = state4 :  BUILDING A QUOTED STRING (for KeyDef)
;    (These values represent the code offset in the routine which will
;     be the jump point for handling that state.)
;
;******************************************************************************
;
;   Called from module which handles TTY string output (bvsWrtTTY).  Whenever
;   bvs_flags(bvs_f_ansiprog) is set, AnsiInterp has incomplete sequence from
;   a previous call.  The input string is handled as the remainder of an
;   ANSI sequence partially processed in the preceeding call.
;
;*****************************************************************************
;       Change History
;
;       91/01/17  WKB   Allow default value of 1 in HVP, J200223
;       91/02/11  TPL   J200233 fix (also see 1.3 B720147)
;
;******************************************************************************


.xlist
INCLUDE         struc.inc               ; Structure macro file          ;@T52
INCLUDE         bvscb.inc               ; BVS structure definitions
INCLUDE         ansidecl.inc            ; ANSI common data declarations
INCLUDE         error.inc               ; DOS ERROR declarations
INCLUDE         ansiextr.inc            ; External declarations
.list
        .386p                           ; Generate 386 instructions

        EXTRN   DoAnsiFn:NEAR           ; ANSI function handler/dispatcher

ANSIDAT SEGMENT WORD PUBLIC 'DATA' USE16 ; Instance data segment        ;@T52
ANSIDAT ENDS

ANSI1   SEGMENT BYTE PUBLIC 'CODE' USE16                                ;@T52
ANSI1   ENDS


ANSI1   SEGMENT BYTE PUBLIC 'CODE' USE16                                ;@T52

        PUBLIC  ANSIINTERP
        PUBLIC  state0
        PUBLIC  state1
        PUBLIC  state2
        PUBLIC  state3
        PUBLIC  state4
        PUBLIC  GetNxtCH
        PUBLIC  IntComp

ANSIINTERP PROC FAR
        ASSUME  CS:ANSI1,DS:NOTHING,ES:NOTHING
;
;       int     3                       ; ******** BUG-BUG ********
;
        sub     AX,AX                   ; Returns no error
        pusha                           ; Save all general purpose registers
        mov     BP,SP                   ; Establish addressability to
        mov     BP,[BP].SP_REG          ;   parameter in the stack
        push    DS                      ; Save data segment registers
        push    ES
;
;   ***  ANSI will address BVSCB using ES and base offset of zero ****
;
        mov     BX,ANSIDAT              ; establish DSEG addressibility
        mov     DS,BX                   ; (DS) = ANSI data segment selector
        mov     ES,[BP].BVSCB_SEL       ; Get BVSCB selector
        mov     BvscbSel,ES             ; save selector for BVSCB
;
;   Initialize ANSI state as necessary                                  ;@T52
;
        .if     <es:[bvs_AIStatus] e ax>                                ;@T52
            mov     es:[bvs_AIStatus],OFFSET state0                     ;@T52
        .endif                                                          ;@T52
;
;   Get address of input string from BVSCB.
;
        mov     CX,ES:[bvs_TTYlen]      ; (CX) = length of input string
        mov     BX,bvs_ParmBuffPtr      ; (BX) = offset of Parm Buffer
        add     BX,ES:[bvs_ParmCount]   ; (BX) = index to next parm byte
;
;   Characters from input string are referenced using ES:[BP+DI]
;
        push    ES                      ; save BVSCB selector
        mov     BP,ES:[bvs_TTYoff]      ; (BP) = offset to input string
        mov     ES,ES:[bvs_TTYsel]      ; (ES) = selector of input string
        sub     DI,DI                   ; (BP) = current index to input string
        mov     AL,ES:[BP]              ; get a character from input string
        pop     ES                      ; restore BVSCB selector
;
;   ES:[0] addresses the BVSCB except for limited access to input string
;   ES:[SI+BX] is the address in the Parameter Buffer to place parameters.
;
        mov     DX,ES:[bvs_AIStatus]    ; (DX) = offset of the current state
        jmp     DX                      ; jump to the current state offset
;
;
state0:                                 ; TEST FOR ESCAPE
        mov     ES:[bvs_Parameter],0    ; reset parameter to zero
        mov     ES:[bvs_ParmCount],0    ; reset parameter buffer index to zero
        cmp     AL,ESCAPE               ; check if character is ESCAPE
        je      st0j1
        jmp     intcomp                 ; if not ESCAPE, go to exit
st0j1:
        mov     BX,bvs_ParmBuffPtr      ; (BX) = index to ParmBuff[0]
        mov     DX,OFFSET ANSI1:state1  ; save state1 offset
        or      ES:[bvs_flags],BVS_F_ANSIPROG   ; SET "in progress" flag
        jmp     GetNxtCh                ; get next char
;
;
state1:                                 ; TEST FOR LEFT BRACKET
        cmp     AL,LEFT_BRACKET         ; check if character is '['
        je      st1j1
        mov     DX,OFFSET ANSI1:state0  ; save state0 offset
        jmp     intcomp                 ; if not LEFT_BRACKET, goto exit
st1j1:
        mov     DX,OFFSET ANSI1:state2  ; save state2 offset
        jmp     GetNxtCh                ; get next char
;
;
state2:
        cmp     AL,'0'
        jb      st2j1                   ; char is before '0'
        cmp     AL,'9'
        ja      st2j3                   ; char is after '9'
        mov     DX,OFFSET ANSI1:state3  ; save state3 offset
        jmp     DX                      ; process character at state 3
;
st2j1:                                  ; char < '0', expect ['] or ["]
        cmp     AL,QUOTE_1
        je      st2j2
        cmp     AL,QUOTE_2              ; char was one of the QUOTE chars
        je      st2j2
        mov     DX,OFFSET ANSI1:state0  ; save state0 offset
        jmp     intcomp                 ; not a quote (sequence invalid) - exit
;
st2j2:
        mov     ES:[bvs_QuoteChar],AL   ; remember closing quote character
        mov     DX,OFFSET ANSI1:state4  ; save state4 offset
        jmp     GetNxtCh                ; get next char
;
st2j3:                                  ; char > '9'
        cmp     AL,'A'                  ; IF char is a letter ("A" or above)
        jae     st2j4                   ; THEN check for term char

        cmp     AL,';'                  ; IF char is a semi-colon (";") ;J200223
        jne     st2j3b                  ; consider as default of 1      ;J200223

        inc     ES:[bvs_ParmCount]      ; increment parm count          ;J200223
        mov     byte ptr ES:[BX],0      ; move default into ParmBuff   ;J200223f
        inc     BX                      ; increment parm buffer index   ;J200223

st2j3b:                                 ;                               ;J200223
        jmp     GetNxtCh                ; get next char

st2j4:                                  ; char is 'A' or above
        mov     DX,OFFSET ANSI1:state0  ; save state0 offset
        call    DoAnsiFn                ; attempt to handle ANSI function
;       jc      st2j5                   ; IF CF=1 THEN handle invalid sequence
;       or      AX,AX                   ; ELSE IF NO VIOxxx error in DoAnsiFn
;       jz      GetNxtCh                ;   THEN get the next character
;       int     3                       ;   ELSE Unexpected error!!!
        jnc     GetNxtCh
st2j5:
        jmp     intcomp                 ; terminate - INVALID SEQUENCE
;
;
state3:
        cmp     AL,'0'
        jb      st3j3                   ; char is before '0' - INVALID SEQUENCE
        cmp     AL,'9'
        ja      st3j1                   ; char is after '9'
        push    DX                      ; save DX
        mov     DL,ES:[bvs_Parameter]   ; get accumulated parameter value
        sub     AL,'0'                  ; (AL) = binary conversion of digit
        shl     DL,1                    ; (DL) = 2 * parameter
        add     AL,DL                   ; (AL) = digit + 2*parameter
        shl     DL,2                    ; (DL) = 8 * parameter
        add     AL,DL                   ; (AL) = digit + 10 * parameter
        mov     ES:[bvs_Parameter],AL   ; store updated parameter
        pop     DX                      ; restore DX
        jmp     SHORT GetNxtCh          ; get next char
;
st3j1:                                  ; char is > '9', expect ';' or letter
        cmp     AL,40h                  ; IF char is a letter ("A" or above)
        ja      st3j2                   ; THEN check for term char
        cmp     AL,';'                  ; check for parameter separator
        jne     st3j3                   ; else INVALID SEQUENCE
;
st3j2:                                  ; char is a letter or separator
        cmp     ES:[bvs_ParmCount],PBUFF_SIZE   ; check if parm buffer full
        jge     st3j3                   ; if full terminate sequence
        inc     ES:[bvs_ParmCount]      ; else increment parameter count
        mov     AH,ES:[bvs_Parameter]   ; (AL) = parameter value
        mov     ES:[BX],AH              ; move parameter value into ParmBuff
        inc     BX                      ; increment parm buffer index
        mov     ES:[bvs_Parameter],0    ; reset parameter to 0
        mov     DX,OFFSET ANSI1:state2  ; reset to state 2
        cmp     AL,';'                  ; check for parameter separator
        je      GetNxtCh                ; if separator then get next char
        jmp     DX                      ; else go directly to state 2
;
st3j3:                                  ; SEQUENCE is INVALID
        mov     DX,OFFSET ANSI1:state0  ; save state0 offset
        jmp     SHORT intcomp           ; terminate sequence
;
;
state4:
        cmp     AL,ES:[bvs_QuoteChar]   ; IF char is END-QUOTE
        jne     st4j1
        mov     DX,OFFSET ANSI1:state2  ; THEN save state2 offset
        jmp     SHORT GetNxtCh          ;   get next char
;
st4j1:                                  ; ELSE add character to buffer
        cmp     ES:[bvs_ParmCount],PBUFF_SIZE   ; IF parm buffer full
        jge     st4j2                   ; THEN - terminate sequence
        inc     ES:[bvs_ParmCount]      ; ELSE increment parameter counter
        mov     ES:[BX],AL              ;   put character in buffer
        inc     BX                      ;   increment parm buffer index
        jmp     SHORT GetNxtCh          ;   get next char
;
st4j2:                                  ; SEQUENCE is INVALID
        mov     DX,OFFSET ANSI1:state0  ; save state0 offset
        jmp     SHORT intcomp           ; terminate sequence
;
;
GetNxtCh:                               ; GET NEXT CHARACTER
        inc     DI                      ; increment index to input string
        cmp     DI,CX                   ; IF index = string length
        je      intcomp                 ; THEN terminate sequence
        mov     ES,ES:[bvs_TTYsel]      ; ELSE (ES) = selector of input string
        mov     AL,ES:[DI+BP]           ;   get char from input string
        mov     ES,BvscbSel             ;   (ES) = selector of BVSCB
        jmp     DX                      ;   jump to the current state handle
;
;
IntComp:
        add     BP,DI                   ; (DI) = new offset of input string
        sub     CX,DI                   ; (CX) = current length of string
        mov     ES:[bvs_AIStatus],DX    ; save the offset to state handle
        cmp     DX,OFFSET ANSI1:State0  ; IF AIStatus != State0
        jne     ic01                    ; THEN leave "ANSI in progress" set
        and     ES:[bvs_flags],NOT bvs_f_ansiprog  ; clear "in progress" fl
;
;  Then update the BVSCB fields to reference current state of input string.
;
ic01:
        mov     ES:[bvs_TTYlen],CX      ; update the input string length
        mov     ES:[bvs_TTYoff],BP      ; update the input string offset
_exit:
        pop     ES
        pop     DS
        popa
        ret     2                       ; return & remove 1 parm from stack

AnsiInterp   ENDP

ANSI1   ENDS
        END
