; Miscellaneous DOS I/O macros

StdIn = 0000h
StdOut = 0001h


macro DosCall Function
if Function LT 100h
mov ah,Function
else
mov ax,Function
endif
int 21h
endm

;
; Set character device in binary ("raw") mode
; - disables recognition of ctrl-c, ctrl-p, ctrl-s, ctrl-z
; - disables DOS's editing keys during input from StdIn
; Entry : bx = handle of character device (e.g. StdOut)
; Exit : cf = 1: error on DOS call
; cf = 0: char. device in binary mode
; Used : ax,dx,flags
; Note : Use SetCooked before program termination to
; restore the I/O mode of a standard device
; (handle 0..4) to the default ascii mode.
;
macro SetRaw
local ahead
DosCall 4400h ; Get Device Data (into dx)
jc ahead
or dl,20h ; Bit 5 = 1 sets binary mode
sub dh,dh ; Clear bits 8-15
DosCall 4401h ; Set Device Data
ahead:
endm

;
; Set character device in ascii ("cooked") mode
; - enables recognition of ctrl-c, ctrl-p, ctrl-s, ctrl-z
; - enables DOS's editing keys during input from StdIn
; Entry : bx = handle of character device (e.g. StdIn)
; Exit : cf = 1: error on DOS call
; cf = 0: char. device in ascii mode
; Used : ax,dx,flags
;
macro SetCooked
local ahead
DosCall 4400h ; Get Device Data (into dx)
jc ahead
and dx,11011111b ; Clear bits 5, 8-15
DosCall 4401h ; Set Device Data
ahead:
endm

;
; Test redirection of standard input/output
; Entry : Handle = 0 (StdIn) or 1 (StdOut)
; Exit : cf = 1 Error on DOS call
; else zf=0 Device is redirected
; zf=1 Device is not redirected
; Used : ax,bx,dx,flags
;
macro isDeviceRedirected Handle
local ahead
mov bx,Handle ; StdIn (Stdout)
DosCall 4400h ; Get Device Data (into dx)
jc ahead ; cf set on error
mov al,81h + Handle ; 81h (82h)
and dl,al ; Isolate bits 7,0 (7,1)
cmp al,dl ; zf=1 if original device
ahead: ; attributes, cf=0
endm

;
; Has a key been pressed?
; Entry : N/A
; Exit : zf = 1 if no key available
; Used : flags
;
macro isKey
push ax
DosCall 0Bh ; Check StdIn status
test al,al
pop ax
endm

;
; (Wait for and) Read key from keyboard
; Entry : N/A
; Exit : zf=0: normal key : ah = 0, al = ASCII code
; zf=1: extended key: ah = extended code, al = 0
; Used : ax,flags
; Note : ax = 0003h if Ctrl-C/Ctrl-Break pressed.
; Doesn't display the character read.
;
macro ReadKey
local ahead
DosCall 07h ; Direct console input
sub ah,ah
cmp al,ah
jnz ahead
DosCall 07h ; Extended: get 2nd code
mov ah,al
sub al,al
ahead:
endm

;
; Same as ReadKey, but uses fnx 3fh
; Used : ax,bx,cx,dx,flags
;
macro ReadKey3F
local ahead
push ds
mov bx,StdIn
SetRaw ; Set keyboard in binary mode
mov dx,ss
mov ds,dx
push dx
mov dx,sp ; ds:dx -> 1-byte buffer
mov cx,1 ; on the stack
DosCall 3fh ; Read Device
xchg bx,dx
mov al,[bx]
xchg bx,dx
sub ah,ah ; Assume normal key code
cmp al,ah
jnz ahead
DosCall 3fh ; Extended: get 2nd code
xchg bx,dx
mov ah,[bx]
xchg bx,dx
sub al,al
ahead:
pop dx ; Clean up stack
push ax ; Save key pressed
pushf ; and flags
SetCooked ; Restore keyboard
popf ; to ascii mode
pop ax
pop ds
endm

;
; Read one line from keyboard (StdIn) into memory buffer,
; permitting the use of DOS's editing keys.
; Entry : ds = segment of Buffer
; The byte size of Buffer must be >= BufLen
; Exit : Buffer updated, ax = no. of characters typed
; Used : ax,bx,cx,dx,flags
; Note :
; DOS's line editor is active during input:
; - F1..F6, left, right, backspace, ins, del, esc, enter
; available for editing
; - responds to ctrl-c, ctrl-p, ctrl-s, ctrl-z
; - pressing F5 or Esc, or typing more characters than can
; fit within one line will cause line wrap (scroll down)
; - pressing Enter completes the function
; - max. 127 characters can be input
; Input is echoed to StdOut during editing.
;
; If BufLen = 0, no input is read. If the no. of characters
; typed is less than BufLen, a CR/LF pair (a CR character,
; if one less) is returned as the last byte(s) and included
; in the count. If the no. of characters typed is equal to
; or greater than BufLen, AX is returned = BufLen, and the
; rest of the characters (plus a CR/LF pair) remain in the
; input and must be removed, for example, by flushing the
; keyboard.
;
macro ReadLine Buffer, BufLen
mov bx,StdIn
SetCooked ; Set keyboard in ascii mode
lea dx,[&Buffer]
mov cx,BufLen
DosCall 3Fh ; Read Device
endm

;
; Write one character to StdOut
; Entry : al = character
; Used : bx,cx,dx,flags
;
macro WriteCh
push ds
push ax ; Store character on stack
mov dx,ss
mov ds,dx
mov dx,sp
mov cx,1
mov bx,StdOut
DosCall 40h ; Write to Handle
pop ax
pop ds
endm

;
; Write one CR/LF pair to StdOut
; Entry : N/A
; Used : al and registers used by WriteCh
;
macro WriteCRLF
mov al,0dh
WriteCh
mov al,0ah
WriteCh
endm

;
; Write zero-terminated string to StdOut
; Entry : ds = segment of string (AsciizID)
; Exit : cx = string length
; Used : ax,bx,cx,dx,flags
;
EOS = 00h ; End-of-string marker
macro WriteZ AsciizID
local count, done
lea dx,[&AsciizID]
mov al,EOS
mov bx,dx
dec bx
count:
inc bx
cmp al,[bx]
jnz count
mov cx,bx
sub cx,dx
jz done
mov bx,StdOut
DosCall 40h ; Write to Handle
done:
endm

;
; Convert binary byte value to hex ascii
; Entry : al = binary value
; Exit : al = low nibble, ah = high nibble
; Used : ax,flags
;
macro HexB
mov ah,al
if @Cpu and 2
shr al,4
else
shr al,1
shr al,1
shr al,1
shr al,1
endif
cmp al,10d ; Convert to hex ascii
sbb al,69h
das
xchg ah,al ; ah = converted high nibble
and al,0fh ; Repeat for low nibble
cmp al,10d
sbb al,69h
das ; al = converted low nibble
endm

;
; Write binary byte value to StdOut as hex ascii
; Entry : al = binary value
; Used : ax and registers used by WriteCh
;
macro WriteHexB
HexB
push ax
mov al,ah ; Output high nibble
WriteCh
pop ax ; Output low nibble
WriteCh
endm Me