mirror of
https://github.com/microsoft/MS-DOS.git
synced 2024-11-25 23:25:47 +00:00
554 lines
11 KiB
Plaintext
554 lines
11 KiB
Plaintext
|
;*** Bugcode.inc - Debug code for including into sysini.asm and ibmbio.asm
|
||
|
;
|
||
|
; Can't link in via buglib due to memory and relocation games played
|
||
|
; by these modules. Each gets a private, local-only copy of these
|
||
|
; modules.
|
||
|
|
||
|
|
||
|
IFDEF DEBUGFLG
|
||
|
|
||
|
|
||
|
;** DPRINTF _ Debug Printf
|
||
|
;
|
||
|
; Dprintf is a kernel debug print formatting package. It is intended
|
||
|
; to produce conviently formatted output.
|
||
|
;
|
||
|
; Dprintf is called, indirectly, by a macro:
|
||
|
;
|
||
|
; DEBUG n,m,"string",<a1,...,an>
|
||
|
;
|
||
|
; string = format string
|
||
|
; a1 = first argument
|
||
|
; an = last argument
|
||
|
;
|
||
|
; The format string is an ASCIZ string which can contain 2 types of
|
||
|
; specifications: data-format specifications and literal characters.
|
||
|
; Data format specifications always begin with a '$' character; all
|
||
|
; characters not part of a data format specification are treated as
|
||
|
; literal characters.
|
||
|
;
|
||
|
; Literal characters
|
||
|
; - any character not part of a format specification. Special
|
||
|
; non-printing characters are:
|
||
|
; \n - CRLF
|
||
|
; \t - tab
|
||
|
; \b - bell
|
||
|
; \\ - \
|
||
|
; \$ - $
|
||
|
;
|
||
|
; Format Specifications
|
||
|
;
|
||
|
; A format specification takes the form:
|
||
|
; $ [@] <char>
|
||
|
;
|
||
|
; where <char> =
|
||
|
;
|
||
|
; x - print argument as a hex word
|
||
|
; d - print argument as decimal word
|
||
|
; c - print argument as ascii character
|
||
|
; b - print argument as hex byte
|
||
|
; For each of the above formats, the supplied argument
|
||
|
; is a 16-bit word - the value to be printed. The optional @
|
||
|
; (described below) allows a segmented address to be supplied,
|
||
|
; instead.
|
||
|
;
|
||
|
; s[nn] - print argument as asciz string; if optional decimal
|
||
|
; argument follows the format character this specifys
|
||
|
; a maximum string length. Non printing characters are
|
||
|
; printed in the form \nnn where "nnn" is the octal byte
|
||
|
; value.
|
||
|
; Note that this format character cannot be directly
|
||
|
; followed by a digit unless that digit is to be taken
|
||
|
; as the start of a length argument.
|
||
|
;
|
||
|
; Bnn - print argument as hex bytes. The required following
|
||
|
; decimal argument is the number of bytes to print.
|
||
|
;
|
||
|
; Both of these formats take a long address as their argument.
|
||
|
; The '@' character is thus invalid for these formats.
|
||
|
;
|
||
|
; WARNINGS
|
||
|
; As befitting a debug routine, DPRINTF does not have a whole lot
|
||
|
; of "failsafe" code in it. Supplying screwed up formats can
|
||
|
; muck things up. Specifically:
|
||
|
; The @ argument must NOT be specified with the 's' or 'B'
|
||
|
; format
|
||
|
; A string/byte-length argument of 0 is taken as 65536
|
||
|
; The string "%% BAD FMT %%" appears in the output when
|
||
|
; 1) an illegal format specifier is given, or
|
||
|
; 2) the B format is given a 0 or missing length
|
||
|
;
|
||
|
; ENTRY (sp+n ) = address of format string (offset from return cs value)
|
||
|
; (sp+n-2) = first argument word
|
||
|
; (sp+n-4) = second argument word
|
||
|
; .
|
||
|
; (sp+4 ) = last argument word
|
||
|
; (sp+2 ) = seg of return address
|
||
|
; (sp ) = offset of return address
|
||
|
; (bp) = offset of format string on the stack
|
||
|
; EXIT none
|
||
|
; USES flags
|
||
|
|
||
|
PUBLIC DPRINTF
|
||
|
DPRINTF PROC near
|
||
|
|
||
|
push ds
|
||
|
push es
|
||
|
push bp
|
||
|
push di
|
||
|
push si
|
||
|
push dx
|
||
|
push cx
|
||
|
push bx
|
||
|
push ax ; save registers
|
||
|
cld
|
||
|
|
||
|
mov si,[bp] ; get address of format string
|
||
|
sub bp,2
|
||
|
mov bx,sp
|
||
|
mov ds,ss:20[bx] ; (ds:si) = address of format string
|
||
|
push cs
|
||
|
pop ds
|
||
|
|
||
|
; Scan format string for next character
|
||
|
;
|
||
|
; (ds:si) = address of format string
|
||
|
; (ss:bp) = address of next argument
|
||
|
|
||
|
dpf1: lodsb ; (al) = format string byte
|
||
|
and al,al
|
||
|
je dpf3 ; all done
|
||
|
cmp al,'$'
|
||
|
je dpf4 ; is data escape
|
||
|
cmp al,'\'
|
||
|
jnz dpf2 ; got the character
|
||
|
|
||
|
; it's an "\" escape code - crack the argument character
|
||
|
|
||
|
lodsb
|
||
|
and al,al
|
||
|
je dpf3 ; all done, ignore hanging \
|
||
|
xchg ah,al
|
||
|
mov al,0Ch
|
||
|
cmp ah,'n'
|
||
|
jne dpf1$5 ; not \n
|
||
|
mov al,0dH
|
||
|
call putchar
|
||
|
mov al,0aH
|
||
|
jmp SHORT dpf2 ; print LF
|
||
|
|
||
|
dpf1$5: cmp ah,'t'
|
||
|
mov al,9
|
||
|
je dpf2 ; is \t
|
||
|
cmp ah,'b'
|
||
|
mov al,7
|
||
|
je dpf2 ; is \b
|
||
|
xchg ah,al
|
||
|
dpf2: call putchar
|
||
|
jmp dpf1
|
||
|
|
||
|
; have the end of the format string - exit
|
||
|
|
||
|
dpf3: pop ax
|
||
|
pop bx
|
||
|
pop cx
|
||
|
pop dx
|
||
|
pop si
|
||
|
pop di
|
||
|
pop bp
|
||
|
pop es
|
||
|
pop ds
|
||
|
ret
|
||
|
|
||
|
|
||
|
;* Have a '$' character - is data format escape
|
||
|
;
|
||
|
; Get address of data into es:di
|
||
|
;
|
||
|
; (bp) = address of data value
|
||
|
|
||
|
dpf4: mov di,bp
|
||
|
push ss
|
||
|
pop es ; (es:di) = address of data value
|
||
|
sub bp,2 ; point to next argument
|
||
|
lodsb ; (al) = format specifier
|
||
|
cmp al,'@'
|
||
|
jne dpf5 ; not an indirect flag
|
||
|
les di,[bp]
|
||
|
sub bp,2 ; have an extra 2 for @
|
||
|
lodsb
|
||
|
dpf5: cmp al,'x'
|
||
|
jne dpfd1 ; not 'x'
|
||
|
|
||
|
; is 'x' format - print hex word
|
||
|
|
||
|
mov ax,es:[di]
|
||
|
call THW ; type hex word
|
||
|
jmp dpf1
|
||
|
|
||
|
dpfd1: cmp al,'d'
|
||
|
jnz dpfc1 ; not 'd'
|
||
|
|
||
|
; is 'd' format - print decimal word
|
||
|
|
||
|
mov ax,es:[di]
|
||
|
call TDW ; type decimal word
|
||
|
jmp dpf1
|
||
|
|
||
|
dpfc1: cmp al,'c'
|
||
|
jne dpfb1
|
||
|
|
||
|
; is 'c' format - print character
|
||
|
|
||
|
mov al,es:[di]
|
||
|
call putchar
|
||
|
jmp dpf1
|
||
|
|
||
|
dpfb1: cmp al,'b'
|
||
|
jne dpfs1
|
||
|
|
||
|
; is 'b' format - print hex byte
|
||
|
|
||
|
mov al,es:[di]
|
||
|
call THB ; type hex byte
|
||
|
jmp dpf1
|
||
|
|
||
|
dpfs1: cmp al,'s'
|
||
|
jne dpfbb1
|
||
|
|
||
|
; is 's' format - print ASCIZ string. First, check for
|
||
|
; optional decimal limit
|
||
|
|
||
|
public SSB
|
||
|
SSB: sub cx,cx ; set 65536 limit
|
||
|
les di,[bp] ; (es:DI) = fwa of string
|
||
|
sub bp,2 ; argument to 's' was two words
|
||
|
mov al,[si]
|
||
|
cmp al,'0'
|
||
|
jb dpfs2 ; not decimal
|
||
|
cmp al,'9'
|
||
|
ja dpfs2 ; not decimal
|
||
|
call atod ; (ax) = decimal value, (ds:si) updated
|
||
|
xchg cx,ax
|
||
|
|
||
|
; print asciz string at es:di, max of (cx) characters
|
||
|
; (cx) = 0 means max of 65536
|
||
|
;
|
||
|
; Other sections of code in dpf jump here to print strings
|
||
|
|
||
|
dpfs2: mov al,es:[di]
|
||
|
inc di
|
||
|
and al,al
|
||
|
je dpfs3
|
||
|
call putchar
|
||
|
loop dpfs2 ; continue if not at limit
|
||
|
dpfs3: jmp dpf1
|
||
|
|
||
|
dpfbb1: cmp al,'B'
|
||
|
je dpfbb2 ; is 'B' format
|
||
|
|
||
|
; error in format code - print message
|
||
|
|
||
|
dpferr: push cs
|
||
|
pop es
|
||
|
mov di,OFFSET dpfa ; (es:di) = error message
|
||
|
sub cx,cx
|
||
|
jmp dpfs2
|
||
|
|
||
|
dpfa: DB '%% BAD FMT %%',0
|
||
|
|
||
|
; have B format
|
||
|
|
||
|
dpfbb2: call atod ; (ax) = length specifier
|
||
|
jc dpferr ; number not there - error
|
||
|
xchg cx,ax
|
||
|
jcxz dpferr ; number is 0 - error
|
||
|
les di,[bp] ; (es:DI) = fwa of string
|
||
|
sub bp,2 ; argument to 's' was two words
|
||
|
dpfbb3: mov al,es:[di]
|
||
|
call THB ; type hex byte
|
||
|
mov al,' '
|
||
|
call putchar ; space em out
|
||
|
inc di
|
||
|
loop dpfbb3 ; do em all
|
||
|
jmp dpf1
|
||
|
|
||
|
DPRINTF ENDP
|
||
|
|
||
|
|
||
|
;** THB - Type Hex Byte
|
||
|
;
|
||
|
; THB types a hex byte (via "putchar")
|
||
|
;
|
||
|
; ENTRY (AL) = byte
|
||
|
; EXIT none
|
||
|
; USES ax, flags
|
||
|
|
||
|
THBA DB '0123456789abcdef'
|
||
|
|
||
|
PUBLIC THB
|
||
|
THB PROC near
|
||
|
|
||
|
push ax
|
||
|
shr al,1
|
||
|
shr al,1
|
||
|
shr al,1
|
||
|
shr al,1
|
||
|
and ax,0fH
|
||
|
xchg bx,ax
|
||
|
mov bl,CS:THBA[bx]
|
||
|
xchg ax,bx
|
||
|
call putchar ; put first character
|
||
|
pop ax
|
||
|
and ax,0fH
|
||
|
xchg bx,ax
|
||
|
mov bl,CS:THBA[bx]
|
||
|
xchg ax,bx
|
||
|
call putchar
|
||
|
ret
|
||
|
|
||
|
THB ENDP
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
;** THW - Type Hex Word
|
||
|
;
|
||
|
; THW types a word in hex (via "putchar")
|
||
|
;
|
||
|
; ENTRY (AX) = word
|
||
|
; EXIT none
|
||
|
; USES AX, flags
|
||
|
|
||
|
PUBLIC THW
|
||
|
THW PROC near
|
||
|
|
||
|
push ax
|
||
|
xchg ah,al
|
||
|
call THB
|
||
|
pop ax
|
||
|
call THB
|
||
|
ret
|
||
|
|
||
|
THW ENDP
|
||
|
|
||
|
|
||
|
|
||
|
;** TDW - Type Decimal Word
|
||
|
;
|
||
|
; TDW types (via "putchar") the unsigned decimal representation
|
||
|
; of a 16-bit unsigned integer. Only significant digits are
|
||
|
; printed; if the number is 0 a "0" is printed.
|
||
|
;
|
||
|
; ENTRY (AX) = number
|
||
|
; EXIT none
|
||
|
; USES AX, flags
|
||
|
|
||
|
PUBLIC TDW
|
||
|
TDW PROC near
|
||
|
|
||
|
push cx ; preserve registers
|
||
|
push dx
|
||
|
mov cx,10
|
||
|
call tdw$ ; recurse cracking digits
|
||
|
pop dx
|
||
|
pop cx
|
||
|
ret
|
||
|
|
||
|
TDW ENDP
|
||
|
|
||
|
|
||
|
;* tdw$ - crack number recursively
|
||
|
;
|
||
|
; tdw$ cracks the least significant decimal digit. If there
|
||
|
; are no higher-significant digits, print and return.
|
||
|
; else, recurse for higher digits
|
||
|
;
|
||
|
; (AX) = value
|
||
|
; (CX) = 10
|
||
|
|
||
|
tdw$ PROC NEAR
|
||
|
|
||
|
sub dx,dx
|
||
|
div cx ; (ax) = quotient, (dx) = remainder
|
||
|
and ax,ax
|
||
|
jz tdw$1 ; this is highest-order, do it
|
||
|
push dx
|
||
|
call tdw$
|
||
|
pop dx
|
||
|
tdw$1: xchg ax,dx
|
||
|
add al,'0'
|
||
|
call putchar
|
||
|
ret
|
||
|
|
||
|
TDW$ ENDP
|
||
|
|
||
|
|
||
|
|
||
|
;** ATOD - Convert ASCII string to decimal number
|
||
|
;
|
||
|
; ATOD is called to convert an ascii string of digits to a
|
||
|
; decimal number. Digits are converted until we run out of them.
|
||
|
;
|
||
|
; ENTRY (DS:SI) = address of first digit
|
||
|
; EXIT 'C' clear if OK
|
||
|
; (AX) = value
|
||
|
; (SI) updated to first non-digit
|
||
|
; 'C' set if error - no digits, or result >65535
|
||
|
; (DS:SI) points to error character
|
||
|
; USES AX, SI, FLAGS
|
||
|
|
||
|
PUBLIC ATOD
|
||
|
ATOD PROC near
|
||
|
|
||
|
push dx
|
||
|
push cx ; save registers
|
||
|
mov al,[si]
|
||
|
sub al,'0'
|
||
|
jc atod9 ; error - no digits
|
||
|
cmp al,10
|
||
|
cmc
|
||
|
jc atod9 ; error - no digits
|
||
|
sub ax,ax ; clear accumulator
|
||
|
mov cx,10 ; base 10
|
||
|
|
||
|
; crack next digit
|
||
|
;
|
||
|
; (AX) = number accumulated so near
|
||
|
; (CX) = 10
|
||
|
; (DS:SI) = next character
|
||
|
|
||
|
atod1: xchg dx,ax ; keep accum in dx for a while
|
||
|
lodsb ; (al) = character
|
||
|
sub al,'0'
|
||
|
jc atod7 ; not digit - all done
|
||
|
cmp al,9
|
||
|
ja atod7 ; not digit - all done
|
||
|
sub ah,ah ; (ax) = digit value (0 - 9)
|
||
|
push ax
|
||
|
xchg ax,dx
|
||
|
mul cx ; (ax) = 10*accum
|
||
|
pop dx ; (dx) = digit to add
|
||
|
jo atod8 ; overflow
|
||
|
add ax,dx
|
||
|
jmp atod1 ; go back for more
|
||
|
|
||
|
; Done with number, all OK
|
||
|
;
|
||
|
; (dx) = number
|
||
|
; (ds:si) = address+1 of first unused character
|
||
|
|
||
|
atod7: clc
|
||
|
|
||
|
; Done with number, error
|
||
|
; 'C' set
|
||
|
|
||
|
atod8: dec si ; backup over non-decimal (or error) char
|
||
|
atod9: pop cx
|
||
|
xchg ax,dx ; (ax) = number iff no error
|
||
|
pop dx ; restore registers
|
||
|
ret ; exit
|
||
|
|
||
|
ATOD ENDP
|
||
|
|
||
|
;** putchar - put a character on the console
|
||
|
;
|
||
|
; ENTRY (al) = character
|
||
|
; EXIT none
|
||
|
; USES ax,flags
|
||
|
|
||
|
|
||
|
UR_DAT = 02f8H ; COM1 = 03f8H, COM2 = 02f8H
|
||
|
UR_IEN = UR_DAT+1 ; Interrupt enable
|
||
|
UR_IER = UR_DAT+2 ; interrupt ID
|
||
|
UR_LCR = UR_DAT+3 ; line control registers
|
||
|
UR_MCR = UR_DAT+4 ; modem control register
|
||
|
UR_LSR = UR_DAT+5 ; line status register
|
||
|
UR_MSR = UR_DAT+6 ; modem status regiser
|
||
|
UR_DLL = UR_DAT ; divisor latch least sig
|
||
|
UR_DLM = UR_DAT+1 ; divisor latch most sig
|
||
|
|
||
|
iflag DB 0 ; != 0 when initialized 8250
|
||
|
|
||
|
;* inchr - input character
|
||
|
;
|
||
|
; EXIT 'z' set if no character
|
||
|
; 'z' clear if char
|
||
|
; (al) = char
|
||
|
|
||
|
inchr: mov dx,UR_LSR
|
||
|
in al,dx
|
||
|
and al,1
|
||
|
jz inchr1
|
||
|
mov dx,UR_DAT
|
||
|
in al,dx
|
||
|
and al,07fh
|
||
|
inchr1: ret
|
||
|
|
||
|
|
||
|
PUBLIC putchar
|
||
|
putchar PROC NEAR
|
||
|
pushf
|
||
|
cli
|
||
|
push dx
|
||
|
push cx
|
||
|
push bx
|
||
|
push ax ; (al) = character
|
||
|
test iflag,255
|
||
|
jnz putc1 ; is initialized
|
||
|
inc iflag
|
||
|
|
||
|
; program the usart
|
||
|
|
||
|
mov dx,UR_LCR
|
||
|
mov al,80h
|
||
|
out dx,al ; command it
|
||
|
sub al,al
|
||
|
mov dx,UR_DLM
|
||
|
out dx,al
|
||
|
mov dx,UR_DLL
|
||
|
mov al,12 ; 9600 baud = 12, 19.2 Kbaud = 6
|
||
|
out dx,al
|
||
|
mov al,3
|
||
|
mov dx,UR_LCR
|
||
|
out dx,al ; command normal mode
|
||
|
|
||
|
; see if CTL-Q or CTL-S
|
||
|
|
||
|
putc1: pushf
|
||
|
cli
|
||
|
call inchr
|
||
|
jz putc3 ; no characters incomming
|
||
|
cmp al,19 ; ctl-S?
|
||
|
jnz putc3 ; no, ignore
|
||
|
|
||
|
; have ctl-s. wait till we see ctl-Q
|
||
|
|
||
|
putc2: call inchr
|
||
|
jz putc2
|
||
|
cmp al,17
|
||
|
jnz putc2
|
||
|
|
||
|
putc3: popf
|
||
|
mov dx,UR_LSR
|
||
|
putc4: in al,dx
|
||
|
test al,020h
|
||
|
jz putc4
|
||
|
|
||
|
; ready. crank it out!
|
||
|
|
||
|
mov dx,UR_DAT
|
||
|
|
||
|
pop ax
|
||
|
out dx,al
|
||
|
|
||
|
pop bx
|
||
|
pop cx
|
||
|
pop dx
|
||
|
popf
|
||
|
ret
|
||
|
|
||
|
putchar ENDP
|
||
|
|
||
|
ENDIF
|