PAGE    ,132
NAME    DFIOCS
TITLE   DFIOCS.ASM - i/o control stuff
.286p

;***    DFIOCS.ASM
;
;       I/O control routines
;
;

.xlist
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debdesc.inc
        include debinit.inc
.list

extrn   _fmtaddr:far
extrn   DosExit:far
extrn   DosRead:far
extrn   DosPeekNmPipe:far
extrn   DosWrite:far
extrn   DosBufReset:far
extrn   DosConnectNmPipe:far
extrn   DosDisConnectNmPipe:far
extrn   KbdCharIn:far
extrn   _lowercase:far

DataSegmentStart

Extern DebData,         WORD
Extern DefType,         BYTE
Extern CmdInputBuffer,  BYTE            ; command line buffer
Extern pTXT,            WORD
Extern pTXTstart,       WORD
Extern fFrontEnd,       WORD
Extern fDebug,          WORD
Extern hPipe,           WORD
Extern Hugeinc          WORD

Global CursorColumn,    BYTE,   0       ; cursor column
Static UnGetChar,       BYTE,   0       ; !0 == char to get on next inchr
Static fLower,          BYTE,   0       ; !0 force lower case output

Global numBytes,        WORD,   0       ; temp word for dosread
Global tmpByte,         BYTE,   0       ; temp byte for doswrite
Global tmpDword,        DWORD,  0       ; temp dword for dosread

keybuf  dw  0,0,0,0,0,0,0           ; keyboard info buffer

DataSegmentEnd

CodeSegmentStart


Extern  CmdBreak, NEAR
Extern  perr, NEAR

;***    LowerCaseOn - force lower case display
;
;       Forces lower case display if INIT_DISLWR flags is set.
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    Flags
;
        assumeDATA <ds, es, ss>

DefProc LowerCaseOn, NEAR, PUBLIC
        test    DebData.InitFlag,INIT_DISLWR
        jz      short lcox
        mov     [fLower],1
lcox:
EndProc

;***    LowerCaseOff - allows case sensitive output again
;
;       ENTRY   NONE
;
;       EXIT    NONE
;
;       USES    NONE
;
        assumeDATA <ds, es, ss>

DefProc LowerCaseOff, NEAR, PUBLIC
        mov     [fLower],0
EndProc

;***    inbuf
;
;       Get input line. Convert all characters NOT in quotes
;       to upper case.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc inbuf, NEAR, PUBLIC

        call    rbufin                  ;  rbufin();
;;;     Pcall   crlf
inb2:
        push    si
        mov     si,dataOFFSET CmdInputBuffer
        Pcall   StrUpr
        pop     si
        mov     pTXT,dataOFFSET CmdInputBuffer
        mov     pTXTstart,dataOFFSET CmdInputBuffer
EndProc

;***    rbufin
;
;       Read buffer in
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc rbufin, NEAR, LOCAL
        push    ax
        push    si
        push    cx

        mov     byte ptr CmdInputBuffer, 0

;//////////////////////////////////////////////////////////////////////////
; davidj   6/8/93 not used
;//////////////////////////////////////////////////////////////////////////
if 0
        test    word ptr _fFrontEnd,-1          ; are we cmd line or debugo?
;        jz      @F
        jmp     @F

        push    _hPipe
        call    DosConnectNmPipe
endif
;//////////////////////////////////////////////////////////////////////////

@@:     test    word ptr _fDebug,-1          ; are we in debug mode?
        jz      inbnodeb

        mov     si,dataOFFSET CmdInputBuffer
inbrd:  push    0
        push    ds
        push    si
        push    1
        push    ds
        push    dataOFFSET numBytes
        call    DosRead
;        mov     ax, numBytes
        cmp     ax, 0
        cmp     numBytes, 0
        jnz      @F

        push    0                ; end of standard in or error in dosread
        push    0
        call    DosExit

@@:     cmp     byte ptr [si], 0ah
        jz      @F
        inc     si
        jmp     inbrd

@@:     inc     si              ;diem 03/21/94
        mov     byte ptr[si],0  ;terminate command line
        dec     si              ;
        dec     si
        Pcall   prtstr, <dataOFFSET CmdInputBuffer>
        jmp     inbufexit


inbnodeb:
        push    0
        push    ds
        push    dataOFFSET CmdInputBuffer
        push    CMDLINELEN
        push    ds
        push    dataOFFSET numBytes
        call    DosRead

        cmp     ax, 0
        jz      @F

;int     3

@@:     mov     si,dataOFFSET CmdInputBuffer
        add     si, numBytes

;//////////////////////////////////////////////////////////////////////////
; davidj   6/8/93 not used
;//////////////////////////////////////////////////////////////////////////
if 0
        test    word ptr _fFrontEnd,-1          ; are we cmd line or debugo?
;      jz      @F
        jmp     @F

        cmp     byte ptr CmdInputBuffer, 3
        jz      $$10

        ; since we're echoing to a pipe, send the cmd line back to stdout

        push    1
        push    ds
        push    dataOFFSET CmdInputBuffer
        push    numBytes
        push    ds
        push    dataOFFSET numBytes
        call    DosWrite

$$10:   push    _hPipe
        call    DosDisConnectNmPipe
endif
;//////////////////////////////////////////////////////////////////////////

@@:     dec     si
        cmp     byte ptr [si], 0ah
        jnz     inbufexit
        dec     si

inbufexit:
        mov     byte ptr [si], 0

        pop     cx
        pop     si
        pop     ax
EndProc

;***    StrUpr
;
;       Convert all characters not in quotes or double quotes to
;       upper case.
;
;       ENTRY:  (SI) = pointer to string in debugger data segment.
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc StrUpr, NEAR, PUBLIC
        push    ax
        push    si

stp1:   lodsb
        Pcall   upper
        mov     [si - 1],al
        or      al,al
        jz      stp3
        Pcall   CheckString
        jne     stp1

        mov     ah,al
stp2:   lodsb
        or      al,al
        jz      stp3
        cmp     al,ah
        jne     stp2
        cmp     ah,[si]         ; check if two quotes in a row
        jne     stp1
        inc     si
        jmp     short stp2

stp3:   pop     si
        pop     ax
EndProc

;***    upper
;
;       Convert character to upper case
;
;       Entry:  AL = character to convert.
;
;       Exit:   AL = character upper case.
;
;       Uses:   AL, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc upper, NEAR, PUBLIC
        cmp     al,'a'
        jb      short upr1
        cmp     al,'z'
        ja      short upr1
        add     al,"A"-"a"              ; Convert to upper case
upr1:
EndProc

;***    lower - Convert character to lower case.
;
;       Entry:  AL = character to convert.
;
;       Exit:   AL = character lower
;
;       Uses:   AL, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc lower, NEAR, PUBLIC
        test    [DebData].InitFlag,INIT_DISLWR
        jz      short lwrx
        cmp     al,'A'
        jb      short lwrx
        cmp     al,'Z'
        ja      short lwrx
        sub     al,'A' - 'a'            ; Convert to lower case
lwrx:
EndProc

;***    prtstr
;
;       Prints a string out to console, used by the putstr macro.
;
;       ENTRY:  (Addr) = address of string terminated by a NULL
;
;       EXIT:   NONE
;
;       USES:   NONE
;
        assumeDATA <ds, es, ss>

DefProc prtstr, NEAR, PUBLIC

ArgVar  Addr, WORD

BeginProc
        push    dx
        mov     dx,Addr
        Pcall   rprbuf
        pop     dx
EndProc

;***    rprbuf
;
;       Writes a string out to console
;
;       ENTRY:  (DX) = address of string terminated by a NULL
;
;       EXIT:   NONE
;
;       USES:   NONE
;
        assumeDATA <ds, es, ss>

DefProc rprbuf, NEAR, PUBLIC
        pushf
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
        mov     si,dx

        push    ds
        pop     es          ; make sure ES points to our data segment
        cld

        test    [fLower],1  ; convert to lowercase?
        jz      short @F

        push    ds
        push    si              ; pass pointer to string
        call    far ptr _lowercase      ; convert to lowercase if DISLWR is set
        add     sp,4

@@:     mov     cx,-1
        mov     al,0
        mov     di,si
        repnz   scasb   ; get the length of this string
        not     cx
        dec     cx

        push    1       ; STDOUT
        push    ds
        push    si      ; far pointer to string
        push    cx      ; number of bytes to write
        push    ds
        push    dataOFFSET numBytes
        call    DosWrite    ; print this string

        push    1       ; STDOUT
        call    DosBufReset    ; print this string

        pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        popf
EndProc

;***    OutAddrOff
;
;       Pcall OutAddrOff, <AddrPtr, OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (AddrPtr) = Pointer to AddrS Structure to print
;               (OffAddr) = Value to add to above AddrS
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc OutAddrOff, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  OffAddr, DWORD
LocVar  TmpAddr, <size AddrS>

BeginProc
        SaveReg <ax, bx>
        mov     ax,AddrPtr              ; (AX) = AddrS pointer
        lea     bx,TmpAddr              ; (BX) = Temporary AddrS pointer
        Pcall   CopyAddr, <ax, bx>
        Pcall   AddAddr, <OffAddr.LowWord, OffAddr.HighWord>
        Pcall   OutAddrS, <bx>
        RestReg <bx, ax>
EndProc

;***    OutAddrS
;
;       Pcall OutAddrS, <AddrPtr>
;
;       ENTRY:  (AddrPtr) = Pointer to AddrS Structure to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc OutAddrS, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
LocVar  Buffer,  64

BeginProc
        SaveReg <ax, bx, cx, dx, si, di>
        lea     bx,Buffer                       ; (BX) = pointer to buffer
        mov     ax,FMT_ADDR_SEG                 ; (AX) = format flags
        test    DebData.InitFlag,INIT_ENV386    ; check 386 env flag
        jz      oar1
        or      ax,FMT_ADDR_32ENV               ; (AX) = format 32 bit offset
oar1:   push    ax
        push    ds
        push    AddrPtr
        push    ds
        push    bx
        call    _fmtaddr
        add     sp,10
        lea     dx,Buffer
        Pcall   rprbuf                          ; print buffer out
        RestReg <di, si, dx, cx, bx, ax>
EndProc

;***    OutColonDword
;
;       Prints an dword in the format ssss:oooo.
;
;       Pcall OutColonDword, <Data>
;
;       Entry:  (Data)  = address to print.
;
;       Exit:   NONE
;
;       Uses:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc OutColonDword, NEAR, PUBLIC

ArgVar  _Data, DWORD

BeginProc
        SaveReg <ax, dx>
        mov     dx,_Data.HighWord
        Pcall   out16
        mov     al,":"
        Pcall   put_ch
        mov     dx,_Data.LowWord
        Pcall   out16
        RestReg <dx, ax>
EndProc

;***    Out32
;
;       Print data in dword hex format.
;
;       ENTRY:  AX, DX = value to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc Out32, NEAR, PUBLIC
        Pcall   OutRadix, <dx, ax, 16, 8, "0">
EndProc

;***    Out24
;
;       Print data in 24 bit hex format.
;
;       ENTRY:  AL, DX = value to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc Out24, NEAR, PUBLIC
        Pcall   OutRadix, <dx, ax, 16, 6, "0">
EndProc

;***    Out20
;
;       Print data in 20 bit hex format.
;
;       ENTRY:  AL, DX = value to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc Out20, NEAR, PUBLIC
        Pcall   OutRadix, <dx, ax, 16, 5, "0">
EndProc

;***    out16
;
;       Print out 16-bit value in DX in hex
;
;       ENTRY:  DX = value to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc out16, NEAR, PUBLIC
        Pcall   OutRadix, <dx, 0, 16, 4, "0">
EndProc

;***    hex
;
;       Output byte in AL as two hex digits
;
;       ENTRY:  AL = byte to print.
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc hex, NEAR, PUBLIC
        Pcall   OutRadix, <ax, 0, 16, 2, "0">
EndProc

;***    OutHex
;
;       Outputs (AX:DX) as a hex number with no padding.
;
;       ENTRY:  (AX:DX) = data to print.
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc OutHex, NEAR, PUBLIC
        Pcall   OutRadix, <dx, ax, 16, 0, 0>
EndProc

;***    fmtnum
;
;       Format data in any radix.
;
;       EndPtr = fmtnum(BufPtr,Data,Radix,PadWidth,Precision,PadChar,LeftJust);
;
;       ENTRY:  (BufPtr) = word offset to buffer to put number.
;               (Data) = dword data to print.
;               (Radix) = word base to print data in.
;               (PadWidth) = minimum field width.
;               (Precision) = maximum field width.
;               (PadChar) = character used to pad data less than field width.
;               (LeftJust) = if 0, right justify result, !0 left justify.
;
;       EXIT:   (AX) = end of string (points to null)
;
;       USES:   AX, FLAGS
;
;       Uses C calling convention.
;
        assumeDATA <ds, es, ss>

DefProc fmtnum, FAR, PUBLIC, C

ArgVar  BufPtr, WORD
ArgVar  junk,  WORD         ; LARRYB - this is the buffer selector
ArgVar  _Data, DWORD
ArgVar  Radix, WORD
ArgVar  PadWidth, WORD
ArgVar  Precision, WORD
ArgVar  PadChar, BYTE
ArgVar  LeftJust, BYTE
LocVar  FmtBuf, 64                      ; format buffer

BeginProc
        SaveReg <dx, si, di>
        xor     si,si                   ; si = buffer index
        push    ds
        pop     es              ; make sure ES points to our data segment

;       Loop converting the data to ascii

frx1:   mov     ax,_Data.HighWord       ; divide high word of divisor
        xor     dx,dx
        div     Radix
        mov     _Data.HighWord,ax       ; save high word of quotient

        mov     ax,_Data.LowWord                ; divide low word of divisor
        div     Radix
        mov     _Data.LowWord,ax                ; save low word of quotient

        mov     al,dl                   ; get remainder
        cmp     al,10
        jb      frx2
        add     al,"A" - "0" - 10       ; convert remainder to character
        test    [DebData].InitFlag,INIT_DISLWR
        jz      short frx2
        sub     al,'A' - 'a'            ; Convert to lower case

frx2:   add     al,"0"
        mov     FmtBuf[si],al           ; put character into buffer
        inc     si
        cmp     _Data.LowWord,0         ; check if quotient zero
        jne     frx1                    ;
        cmp     _Data.HighWord,0                ;
        jne     frx1

;       Trunicate result if too big or pad result if not big enough
;       (SI) = number of characters in buffer

        mov     dx,Precision            ; (DX) = max width
        or      dx,dx                   ; if field zero, then no trunication
        jz      frx3

        cmp     si,dx                   ; if size is greater than field
        jbe     frx3                    ;  width, trunicate size.
        mov     si,dx

frx3:   cmp     LeftJust,0              ; jump if left justify result
        jne     frx5                    ;

        mov     dx,PadWidth             ; (DX) = minimum width
        or      dx,dx                   ; if field zero, then no padding
        jz      frx5

        mov     al,PadChar              ; get pad character
frx4:   cmp     si,dx                   ; put pad characters in until
        jae     frx5                    ;  number of characters is
        mov     FmtBuf[si],al           ;   field width.
        inc     si
        cmp     si,64                   ; don't overflow buffer
        jb      frx4                    ;

;       Now copy the padded number to the output buffer

frx5:   push    si                      ; save buffer index
        mov     di,BufPtr               ; (DI) = output buffer pointer
frx6:   dec     si                      ; move buffer in reverse order
        mov     al,FmtBuf[si]
        stosb
        or      si,si
        jnz     frx6                    ; jump if not end of buffer
        pop     si                      ; restore buffer index

;       Left justify flag result if flag set.

        cmp     LeftJust,0
        je      frx8                    ; jump if right justify result

        mov     al,PadChar              ; (AL) = pad character
        mov     dx,PadWidth             ; (DX) = pad width
frx7:   cmp     si,dx
        jae     frx8                    ; left justify result
        inc     si
        stosb
        jmp     short frx7

frx8:   mov     byte ptr [di],0         ; null terminate string
        mov     ax,di                   ; (AX) = end of string
        RestReg <di, si, dx>
; LARRYB
        mov     dx,ds                   ; make DX:AX = far pointer
EndProc

;***    OutRadix
;
;       Output data to console in any radix.
;
;       Pcall   OutRadix, <Data.LowWord,Data.HighWord,Radix,PadWidth,PadChar>
;
;       ENTRY:  (Data) = dword data to print.
;               (Radix) = word base to print data in.
;               (PadWidth) = byte field width. If negative left justify the
;                         padding and if positive right justify (normal).
;               (PadChar) = character used to pad data less than field width.
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc OutRadix, NEAR, PUBLIC

ArgVar  _Data, DWORD
ArgVar  Radix, WORD
ArgVar  PadWidth, WORD
ArgVar  PadChar, WORD
LocVar  Buffer, 64

BeginProc
        SaveReg <ax, bx, dx>
        lea     bx,Buffer               ; (BX) = buffer to put result
        xor     ax,ax                   ; (AX) = right justify flag
        mov     dx,PadWidth             ; (DX) = pad width
        or      dx,dx                   ; if field zero, then no padding
        jns     orx1                    ; jump if positive
        neg     dx                      ; get absolute value
        not     ax                      ; (AX) = left justify flag
orx1:   push    ax
        push    PadChar
        push    dx
        push    dx
        push    Radix
        push    _Data.HighWord
        push    _Data.LowWord
        push    ds
        push    bx
        call    _fmtnum
        add     sp,18
; Ccall   fmtnum, <bx,ds,_Data.LowWord,_Data.HighWord,Radix,dx,dx,PadChar,ax>
        lea     dx,Buffer
        Pcall   rprbuf
        RestReg <dx, bx, ax>
EndProc

;***    CrLfColOne
;
;       Output CR/LF sequence if not at column one
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc CrLfColOne, NEAR, PUBLIC
        cmp     CursorColumn,0          ; if 0, we are already at column one
        je      cco1
        Pcall   crlf
cco1:
EndProc

;***    crlf
;
;       Output CR/LF sequence
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc crlf, NEAR, PUBLIC
        push    ax
        mov     al,CR
        Pcall   put_ch
        mov     al,LF
        Pcall   put_ch
        pop     ax
EndProc

;***    tab
;
;       Output the number of blanks in CX
;
;       ENTRY:  (CX) = number of blanks
;
;       EXIT:   NONE
;
;       USES:   CX
;
        assumeDATA <ds, es, ss>

DefProc tab, NEAR, PUBLIC
        Pcall   blank
        loop    tab
EndProc

;***    blank
;
;       Output one space
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc blank, NEAR, PUBLIC
        push    ax
        mov     al," "
        Pcall   put_ch
        pop     ax
EndProc

;***    put_ch
;
;       Console output of character in AL. No registers affected
;       but bit 7 is reset before output.
;
;       ENTRY:  (AL) = character to print
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc put_ch, NEAR, PUBLIC
        SaveReg <bx, cx, dx, si, di, es>
        or      al,al                   ; don't print nulls
        jz      short pchx              ;
        cmp     [fLower],0
        je      short pch10
        Pcall   lower
pch10:  push    ax
        mov     tmpByte, al
        push    1
        push    ds
        push    dataOFFSET tmpByte
        push    1
        push    ds
        push    dataOFFSET numBytes
        call    DosWrite
        pop     ax
pchx:
        RestReg <es, di, si, dx, cx, bx>
EndProc

;*** checkchr - check for input character
;
;       ENTER:     NONE
;
;       EXIT:   AL = character
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

; davidj 03/08/94 this was causing a relative fixup error in linker
;                 and the calls were being resolved as near calls
;                 instead of far calls
;DefProc checkchr, NEAR, PUBLIC

DefProc checkchr, FAR, PUBLIC
LocVar  ltmpByte, BYTE
LocVar  lnumBytes, WORD
LocVar  ltmpDword, DWORD
LocVar  lpipeState, WORD
BeginProc
        push    es
        push    bx
        push    cx
        push    dx

        test    word ptr _fFrontEnd, -1
        jz      ccr0

        ;; We are using the pipe for input, query for CTRL-C (or ESC)
        mov     ltmpByte, 0

        push    _hPipe
        push    ds
        lea     bx, ltmpByte
        push    bx
        push    1
        push    ds
        lea     bx, lnumBytes
        push    bx
        push    ds
        lea     bx, ltmpDword
        push    bx
        push    ds
        lea     bx, lpipeState
        push    bx
        call    DosPeekNmPipe

        cmp     ltmpByte, 3
        jz      @F
        cmp     ltmpByte, 27
        jnz     ccrx

;@@:     INT     3
@@:     jmp     CmdBreak

ccr0:   Pcall   inchr
        jz      ccrx

        cmp     al,27           ; is it ESC?
        jz      @F
        cmp     al,3            ; is it ctl/c
        jnz     ccrx

@@:     jmp     CmdBreak

ccrx:
        pop     dx
        pop     cx
        pop     bx
        pop     es
EndProc

;***    waitchr - wait for input character
;
;       ENTER:     NONE
;
;       EXIT:   AL = character
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc waitchr, NEAR, PUBLIC
        nop
; wcr1:   Pcall   inchr
;        jz      wcr1

;        cmp     al,CTRLC
;        jne     wcrx

;        jmp     CmdBreak
; wcrx:
EndProc

;***    inchr - input character
;
;       ENTER:  NONE
;
;       EXIT:   if ZR, no characters
;               if NZ, AL = character
;
;       USES:   AX, Flags
;
        assumeDATA <ds>
        ASSUME  ES:NOTHING, SS:NOTHING

DefProc inchr, NEAR, PUBLIC
        mov     keybuf,0            ; assume no key
        push    si
        push    di
        push    es
        push    dx
        push    bx
        push    cx

        push    ds                  ; pointer to charbuffer
        push    offset keybuf
        push    1                   ; IOWait = 1 (don't wait for a key)
        push    0                   ; KbdHandle = 0
        call    KbdCharIn           ; see if a key is waiting

        pop     cx
        pop     bx
        pop     dx
        pop     es
        pop     di
        pop     si

        mov     ax,keybuf
        or      ah,ah               ; return with key status in ZF
EndProc

;***    ungetc - put back input character
;
;       ENTER:  AL = character to put back.
;
;       EXIT:   NONE
;
;       USES:   NONE
;
        assumeDATA <ds>
        ASSUME  ES:NOTHING, SS:NOTHING

DefProc ungetc, NEAR, PUBLIC
        mov     UnGetChar,al
EndProc


;
; Segment crossing code
; written by Larry Bank
;
; call from C as SEGCROSS(dest, source, length, hugeinc);
;
; where dest and source are 16:16 far pointers and length is < 64k
; *****Note*****
; 1) Destination pointer is assumed to not be crossing a segment, only the
;    source segment crossing is handled.
; 2) hugeinc is NOT the shift value, but the increment: e.g. 8 instead of 3.
; 3) For better performance, the variables can be accessed directly instead
;    of passing them on the stack if this is coded with the inline assember.
;
; stack frame is: dest = [bp+4,6], source = [bp+8,10], length = [bp+12]
;                 hugeinc = [bp+14]
;
DefProc segcross, FAR, PUBLIC, C
ArgVar  Dest,      DWORD
ArgVar  Source,    DWORD
ArgVar  StrucSize, WORD
BeginProc
        push    si
        push    di          ; save C register vars
        push    es
        push    ds          ; save clobbered segment register

        mov     ax, Hugeinc
        lds     si,Source   ; get the source pointer
        les     di,Dest     ; get the destination pointer
        mov     cx,StrucSize; get the length of the move
        mov     bx,cx       ; are we going to cross a segment?
        add     bx,si       ; if this ADD generates a carry, handle crossing
        jnc     @F          ; we're not going to cross a segment, just do it
;
; Nasty case of segment crossing
;
        mov     cx, 0ffffh  ; set the 64k boundary
        sub     cx, si      ; subtract the part that is before the seg boundary
        inc     cx          ; add 1 to include the copy of ffff
        rep     movsb       ; copy the stuff from the first segment
        mov     cx,bx       ; now the stuff from the second segment
        mov     bx,ds       ; get the segment value
        add     bx,ax       ; add the Hugeinc value to bx
        mov     ds,bx       ; now DS points into the next segment
                            ; si should be zero here
        rep     movsb       ; copy the stuff that slopped over
        jmp short crosx     ; time to go
;
; Simple case with no segment crossing
;
@@:     rep     movsb       ; copy the bytes and leave

crosx:  pop     ds
        pop     es
        pop     di
        pop     si
EndProc

CodeSegmentEnd

        end
