mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-26 18:04:07 +00:00
1423 lines
34 KiB
NASM
1423 lines
34 KiB
NASM
; Figure 10-2
|
||
; Debug Subroutines
|
||
;
|
||
;<---- NOTE :
|
||
; The line numbers at the extreme left are included purely
|
||
; to reference the code from the text.
|
||
; There are deliberately induced discontinuities
|
||
; in the numbers in order to allow space for expansion.
|
||
;
|
||
; Because of the need to test these routines thoroughly,
|
||
; and in case you wish to make any changes, the testbed
|
||
; routines for the debug package itself has been left in
|
||
; in this figure.
|
||
;
|
||
; Debug Testbed.
|
||
;
|
||
ORG 100H
|
||
START:
|
||
LXI SP,Test$Stack ;Setup local stack
|
||
CALL DB$Init ;Initialize the debug package
|
||
CALL DB$On ;Enable Debug Output
|
||
;Simple test of A-register display
|
||
MVI A,0AAH ;Preset a value in the A register
|
||
LXI B,0BBCCH ;Prefill all other registers, partly
|
||
LXI D,0DDEEH ; to check the debug display, but
|
||
LXI H,0FF11H ; also to check register save/restore
|
||
;#
|
||
; Test register display
|
||
;
|
||
ORA A ;Set M-flag, Clear Z-flag, Set E-Flag
|
||
STC ;Set Carry
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$F
|
||
DB 'Flags',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$A
|
||
DB 'A Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$B
|
||
DB 'B Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$C
|
||
DB 'C Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$D
|
||
DB 'D Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$E
|
||
DB 'E Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$H
|
||
DB 'H Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$L
|
||
DB 'L Register',0
|
||
;#
|
||
; Test Memory Dump Display
|
||
;
|
||
CALL DB$Display
|
||
DB DB$M ;Dump memory
|
||
DW 108H,128H ;Check start/end at non-multiples
|
||
DB 'Memory Dump #1',0 ; of 10H.
|
||
;
|
||
CALL DB$Display
|
||
DB DB$M ;Dump memory
|
||
DW 100H,11FH ;Check start and end on displayed
|
||
DB 'Memory Dump #2',0 ; line boundaries.
|
||
;
|
||
CALL DB$Display
|
||
DB DB$M ;Dump memory
|
||
DW 101H,100H ;Check error handling where
|
||
DB 'Memory Dump #3',0 ; start > end address.
|
||
;
|
||
CALL DB$Display
|
||
DB DB$M ;Dump memory
|
||
DW 100H,100H ;Check end-case of single byte
|
||
DB 'Memory Dump #4',0 ; output.
|
||
;#
|
||
; Test register pair display
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$BC
|
||
DB 'BC Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$DE
|
||
DB 'DE Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$HL
|
||
DB 'HL Register',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$SP
|
||
DB 'SP Register',0
|
||
;
|
||
LXI B,Byte$BC ;Set up registers for byte tests
|
||
LXI D,Byte$DE
|
||
LXI H,Byte$HL
|
||
;#
|
||
; Test byte indirect display
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$B$BC
|
||
DB 'Byte at (BC)',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$B$DE
|
||
DB 'Byte at (DE)',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$B$HL
|
||
DB 'Byte at (HL)',0
|
||
;
|
||
LXI B,Word$BC ;Set up the registers for word tests
|
||
LXI D,Word$DE
|
||
LXI H,Word$HL
|
||
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$W$BC
|
||
DB 'Word at (BC)',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$W$DE
|
||
DB 'Word at (DE)',0
|
||
;
|
||
CALL DB$Display ;Call the Debug Routine
|
||
DB DB$W$HL
|
||
DB 'Word at (HL)',0
|
||
;#
|
||
; Test DB$On/Off
|
||
;
|
||
CALL DB$Off ;Disable Debug Output
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB 0DH,0AH,'This message should NOT appear',0
|
||
|
||
CALL DB$On
|
||
CALL DB$MSGI
|
||
DB 0DH,0AH,'Debug output has been re-enabled.',0
|
||
;#
|
||
; Test pass count logic
|
||
;
|
||
CALL DB$Off ;Disable debug output
|
||
CALL DB$Set$Pass ;Set Pass count
|
||
DW 30
|
||
;
|
||
MVI A,34 ;Set loop counter greater than pass
|
||
; counter.
|
||
Test$Pass$Loop:
|
||
CALL DB$Pass ;Decrement pass count
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB 0DH,0AH,'This message should display 5 times',0
|
||
DCR A
|
||
JNZ Test$Pass$Loop
|
||
;#
|
||
; Test debug input/output
|
||
;
|
||
CALL DB$Off ;Check that debug IN/OUT
|
||
; must still occur when debug
|
||
; output is disabled.
|
||
RST 4 ;Debug input
|
||
DB 11H ;Port number
|
||
RST 5 ;Debug output (value return from input)
|
||
DB 22H ;Port number
|
||
|
||
JMP 0 ;Warm Boot at end of testbed
|
||
;
|
||
;
|
||
; Dummy values for Byte and Word displays
|
||
Byte$BC: DB 0BCH
|
||
Byte$DE: DB 0DEH
|
||
Byte$HL: DB 0F1H
|
||
;
|
||
Word$BC: DW 0B0CH
|
||
Word$DE: DW 0D0EH
|
||
Word$HL: DW 0F01H
|
||
;
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
Test$Stack:
|
||
;
|
||
;
|
||
;
|
||
ORG 400H ;To avoid unnecessary listings
|
||
; when only the testbed changes
|
||
;-----------------------------------------------------------------------
|
||
;#
|
||
;
|
||
; Debug Subroutines
|
||
;
|
||
;
|
||
; Equates for DB$Display Codes
|
||
; These equates are the offsets down the table of addresses
|
||
; for various subroutines to be used.
|
||
;
|
||
DB$F EQU 00 ;Flags
|
||
DB$A EQU 02 ;A register
|
||
DB$B EQU 04 ;B
|
||
DB$C EQU 06 ;C
|
||
DB$D EQU 08 ;D
|
||
DB$E EQU 10 ;E
|
||
DB$H EQU 12 ;H
|
||
DB$L EQU 14 ;L
|
||
DB$BC EQU 16 ;BC
|
||
DB$DE EQU 18 ;DE
|
||
DB$HL EQU 20 ;HL
|
||
DB$SP EQU 22 ;Stack Pointer
|
||
DB$M EQU 24 ;Memory
|
||
DB$B$BC EQU 26 ;(BC)
|
||
DB$B$DE EQU 28 ;(DE)
|
||
DB$B$HL EQU 30 ;(HL)
|
||
DB$W$BC EQU 32 ;(BC+1),(BC)
|
||
DB$W$DE EQU 34 ;(DE+1),(DE)
|
||
DB$W$HL EQU 36 ;(HL+1),(HL)
|
||
;
|
||
;
|
||
; Equates
|
||
RST4 EQU 20H ;Address for RST 4 - IN instruction
|
||
RST5 EQU 28H ;Address for RST 5 - OUT instruction
|
||
;
|
||
B$CONIN EQU 1 ;BDOS CONIN Function Code
|
||
B$CONOUT EQU 2 ;BDOS CONOUT Function Code
|
||
B$READCONS EQU 10 ;BDOS Read Console Function Code
|
||
BDOS EQU 5 ;BDOS Entry Point
|
||
;
|
||
False EQU 0
|
||
True EQU NOT False
|
||
;
|
||
;Equates to specify how DB$CONOUT
|
||
; and DB$CONIN should perform
|
||
; their Input/Output.
|
||
DB$Polled$IO EQU False ;)
|
||
DB$BIOS$IO EQU False ;) Only one MUST be true
|
||
DB$BDOS$IO EQU True ;)
|
||
;
|
||
;Equates for Polled I/O
|
||
DB$Status$Port EQU 01H ;Console status port
|
||
DB$Data$Port EQU 02H ;Console data port
|
||
;
|
||
DB$Input$Ready EQU 0000$0010B ;Incoming data ready
|
||
DB$Output$Ready EQU 0000$0001B ;Ready for output
|
||
;
|
||
;Data for BIOS I/O
|
||
BIOS$CONIN: DB JMP ;The initialization routine sets these
|
||
DW 0 ; two JMP addresses into the BIOS
|
||
BIOS$CONOUT: DB JMP
|
||
DW 0
|
||
;
|
||
; Main debug variables and constants
|
||
;
|
||
DB$Flag: DB 0 ;Main Debug Control Flag.
|
||
; When this flag is non-zero, all debug
|
||
; output will be made. When zero, all
|
||
; debug output will be suppressed.
|
||
; It is altered either directly by the user
|
||
; or using the routines DB$On, DB$Off and
|
||
; DB$Pass.
|
||
;
|
||
DB$Pass$Count: DW 0 ;Pass counter.
|
||
; When this is non-zero, calls to DB$Pass
|
||
; decrement it by one. When it reaches
|
||
; zero, the debug control flag, DB$Flag,
|
||
; is set non-zero - thereby enabling
|
||
; debug output.
|
||
;
|
||
DB$Save$HL: ;Save area for HL
|
||
DB$Save$L: DB 0
|
||
DB$Save$H: DB 0
|
||
|
||
DB$Save$SP: DW 0 ;Save area for Stack Pointer
|
||
DB$Save$RA: DW 0 ;Save area for Return Address
|
||
DB$Call$Address: DW 0 ;Starts out the same as DB$Save$RA
|
||
; but DB$Save$RA gets updated during
|
||
; debug processing. This value is
|
||
; output ahead of the caption.
|
||
DB$Start$Address: ;Start address for memory display
|
||
DW 0
|
||
DB$End$Address: ;End address for memory display
|
||
DW 0
|
||
DB$Display$Code: ;Display code requested
|
||
DB 0
|
||
;
|
||
;
|
||
;Stack Area
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||
DB$Save$E: DB 0 ;E Register
|
||
DB$Save$D: DB 0 ;D Register
|
||
DB$Save$C: DB 0 ;C Register
|
||
DB$Save$B: DB 0 ;B Register
|
||
DB$Save$F: DB 0 ;Flags
|
||
DB$Save$A: DB 0 ;A Register
|
||
DB$Stack: ;Debug stack area
|
||
; The registers in the stack area are PUSHed
|
||
; onto the stack and accessed directly.
|
||
;
|
||
; Register Caption Messages
|
||
;
|
||
; The table below, indexed by the Display$Code is used to access
|
||
; the Register Caption String.
|
||
;
|
||
DB$Register$Captions:
|
||
DW DB$F$RC ;Flags
|
||
DW DB$A$RC ;A register
|
||
DW DB$B$RC ;B
|
||
DW DB$C$RC ;C
|
||
DW DB$D$RC ;D
|
||
DW DB$E$RC ;E
|
||
DW DB$H$RC ;H
|
||
DW DB$L$RC ;L
|
||
DW DB$BC$RC ;BC
|
||
DW DB$DE$RC ;DE
|
||
DW DB$HL$RC ;HL
|
||
DW DB$SP$RC ;Stack Pointer
|
||
DW DB$M$RC ;Memory
|
||
DW DB$B$BC$RC ;(BC)
|
||
DW DB$B$DE$RC ;(DE)
|
||
DW DB$B$HL$RC ;(HL)
|
||
DW DB$W$BC$RC ;(BC+1),(BC)
|
||
DW DB$W$DE$RC ;(DE+1),(DE)
|
||
DW DB$W$HL$RC ;(HL+1),(HL)
|
||
;
|
||
DB$F$RC: DB 'Flags',0 ;Flags
|
||
DB$A$RC: DB 'A',0 ;A register
|
||
DB$B$RC: DB 'B',0 ;B
|
||
DB$C$RC: DB 'C',0 ;C
|
||
DB$D$RC: DB 'D',0 ;D
|
||
DB$E$RC: DB 'E',0 ;E
|
||
DB$H$RC: DB 'H',0 ;H
|
||
DB$L$RC: DB 'L',0 ;L
|
||
DB$BC$RC: DB 'BC',0 ;BC
|
||
DB$DE$RC: DB 'DE',0 ;DE
|
||
DB$HL$RC: DB 'HL',0 ;HL
|
||
DB$SP$RC: DB 'SP',0 ;Stack Pointer
|
||
DB$M$RC: DB 'Start, End Address ',0 ;Memory
|
||
DB$B$BC$RC: DB '(BC)',0 ;(BC)
|
||
DB$B$DE$RC: DB '(DE)',0 ;(DE)
|
||
DB$B$HL$RC: DB '(HL)',0 ;(HL)
|
||
DB$W$BC$RC: DB '(BC+1),(BC)',0 ;(BC+1),(BC)
|
||
DB$W$DE$RC: DB '(DE+1),(DE)',0 ;(DE+1),(DE)
|
||
DB$W$HL$RC: DB '(HL+1),(HL)',0 ;(HL+1),(HL)
|
||
;
|
||
; Flags Message
|
||
;
|
||
DB$Flags$Msg: DB 'CxZxMxExIx',0 ;Compatible with DDT's display
|
||
;
|
||
; Flags Masks used to test user's flag byte
|
||
;
|
||
DB$Flag$Masks:
|
||
DB 0000$0001B ;Carry
|
||
DB 0100$0000B ;Zero
|
||
DB 1000$0000B ;Minus
|
||
DB 0000$0100B ;Even Parity
|
||
DB 0001$0000B ;Interdigit Carry (Aux Carry)
|
||
DB 0 ;Terminator
|
||
;#
|
||
; DB$Init
|
||
; This routine initializes the debug package.
|
||
;
|
||
DB$Init:
|
||
IF DB$BIOS$IO ;Use BIOS for CONIN/CONOUT
|
||
LHLD 1 ;Get Warm Boot address from base
|
||
; page. H = BIOS Jump Vector page
|
||
MVI L,09H ;Get CONIN offset in Jump Vector
|
||
SHLD BIOS$CONIN + 1 ;Setup address
|
||
MVI L,0CH ;Get CONOUT offset in Jump Vector
|
||
SHLD BIOS$CONOUT + 1
|
||
ENDIF
|
||
|
||
;Setup JMP instructions to receive control
|
||
; when an RST instruction is executed
|
||
MVI A,JMP ;Set JMP instructions at RST points
|
||
STA RST4
|
||
STA RST5
|
||
LXI H,DB$Input ;Address of Fake Input Routine
|
||
SHLD RST4 + 1
|
||
LXI H,DB$Output ;Address of Fake Output Routine
|
||
SHLD RST5 + 1
|
||
|
||
RET
|
||
;#
|
||
; DB$CONINU
|
||
; This routine returns the next character from the console,
|
||
; but converting 'a' to 'z' to UPPER CASE letters.
|
||
;
|
||
DB$CONINU:
|
||
CALL DB$CONIN ;Get character from keyboard
|
||
JMP DB$A$To$Upper ;Fold to upper and return
|
||
;#
|
||
; DB$CONIN
|
||
; This routine returns the next character from the console.
|
||
; According to the setting of EQUates, it uses either simple
|
||
; polled I/O, the BDOS (function 2) or the BIOS.
|
||
;
|
||
; Exit Parameters
|
||
;
|
||
; A = Character from console
|
||
;
|
||
DB$CONIN:
|
||
IF DB$Polled$IO ;Simple polled input
|
||
IN DB$Status$Port ;Check if incoming data
|
||
ANI DB$Input$Ready
|
||
JZ DB$CONIN ;No
|
||
IN DB$Data$Port ;Input data character
|
||
PUSH PSW ;Save data character
|
||
MOV C,A ;Ready for output
|
||
CALL DB$CONOUT ;Echo it back
|
||
POP PSW ;Recover data character
|
||
RET
|
||
ENDIF
|
||
|
||
IF DB$BDOS$IO ;Use BDOS for input
|
||
MVI C,B$CONIN ;Read console
|
||
JMP BDOS ;BDOS returns to our caller
|
||
ENDIF
|
||
|
||
IF DB$BIOS$IO ;Use BIOS for input
|
||
JMP BIOS$CONIN ;This was setup during BIOS
|
||
; initialization
|
||
ENDIF
|
||
|
||
;#
|
||
; DB$CONOUT
|
||
; This routine outputs the character in the C register to the
|
||
; Console, either using simple polled I/O, the BDOS or the BIOS.
|
||
;
|
||
; Entry Parameters
|
||
; A = Byte to be output
|
||
;
|
||
DB$CONOUT:
|
||
LDA DB$Flag ;Check if debug output enabled
|
||
ORA A
|
||
RZ ;Ignore output if disabled
|
||
|
||
IF DB$Polled$IO ;Use simple polled output
|
||
IN DB$Status$Port ;Check if ready for output
|
||
ANI DB$Output$Ready
|
||
JZ DB$CONOUT ;No
|
||
MOV A,C ;Get data byte
|
||
OUT DB$Data$Port
|
||
RET
|
||
ENDIF
|
||
|
||
IF DB$BDOS$IO ;Use BDOS for output
|
||
MOV E,C ;Move into correct register
|
||
MVI C,B$CONOUT
|
||
JMP BDOS ;BDOS returns to our caller
|
||
ENDIF
|
||
|
||
IF DB$BIOS$IO ;Use BIOS for output
|
||
MOV A,C ;Move into correct register
|
||
JMP BIOS$CONOUT ;Setup during debug initialization
|
||
ENDIF
|
||
;#
|
||
;
|
||
; DB$On
|
||
; This routine enables all debug output by setting the
|
||
; DB$Flag non-zero.
|
||
;
|
||
DB$On:
|
||
PUSH PSW ;Preserve registers
|
||
MVI A,0FFH
|
||
STA DB$Flag ;Set control flag on
|
||
POP PSW
|
||
RET
|
||
;#
|
||
;
|
||
; DB$Off
|
||
; This routine disables all debug output by setting the
|
||
; DB$Flag to zero.
|
||
;
|
||
DB$Off:
|
||
PUSH PSW ;Preserve registers
|
||
XRA A
|
||
STA DB$Flag ;Clear control flag
|
||
POP PSW
|
||
RET
|
||
;#
|
||
;
|
||
; DB$Set$Pass
|
||
; This routine sets the Pass Counter. Subsequent calls to DB$Pass
|
||
; decrement the count, and when it reaches 0, debug output
|
||
; is enabled.
|
||
;
|
||
; Calling sequence
|
||
;
|
||
; CALL DB$Set$Pass
|
||
; DW Pass$Count$Value
|
||
;
|
||
DB$Set$Pass:
|
||
SHLD DB$Save$HL ;Preserve user's HL
|
||
POP H ;Recover return address
|
||
PUSH D ;Preserve user's DE
|
||
MOV E,M ;Get LS byte of count
|
||
INX H ;Update pointer
|
||
MOV D,M ;Get MS byte
|
||
INX H ;HL points to return address
|
||
XCHG ;HL = pass counter
|
||
SHLD DB$Pass$Count ;Set Debug pass counter
|
||
XCHG ;HL points to return address
|
||
POP D ;Recover user's DE
|
||
XTHL ;Recover user's HL and set
|
||
; return address on top of stack
|
||
RET
|
||
;#
|
||
;
|
||
; DB$Pass
|
||
; This routine decrements the Debug pass counter -
|
||
; if the result is negative, it takes no further action.
|
||
; If the result is zero, it sets the Debug control flag non-zero
|
||
; to enable debug output.
|
||
;
|
||
DB$Pass:
|
||
PUSH PSW ;Save user's registers
|
||
PUSH H
|
||
LHLD DB$Pass$Count ;Get pass count
|
||
DCX H
|
||
MOV A,H ;Check if count now negative
|
||
ORA A
|
||
JM DB$Pass$x ;Yes - take no further action
|
||
SHLD DB$Pass$Count ;Save downdated count
|
||
ORA L ;Check if count now zero
|
||
JZ DB$Pass$ED ;Yes - enable debug
|
||
DB$Pass$x: ;
|
||
POP H ;Recover user's registers
|
||
POP PSW
|
||
RET
|
||
;
|
||
DB$Pass$Ed: ;Enable debug
|
||
MVI A,0FFH
|
||
STA DB$Flag ;Set debug control flag
|
||
JMP DB$Pass$x
|
||
;#
|
||
;
|
||
; DB$Display
|
||
; This is the primary debug display routine.
|
||
;
|
||
; Calling sequence
|
||
;
|
||
; CALL DB$Display
|
||
; DB Display$Code
|
||
; DB 'Caption String',0
|
||
;
|
||
; Display code identifies which register(s) are to be
|
||
; displayed.
|
||
;
|
||
; When the display code specifies a block of memory
|
||
; the sequence is:
|
||
;
|
||
; CALL DB$Display
|
||
; DB Display$Code
|
||
; DW Start$Address,End$Address
|
||
; DB 'Caption String',0
|
||
;
|
||
DB$Display:
|
||
;
|
||
DB$Display$Enabled:
|
||
SHLD DB$Save$HL ;Save user's HL
|
||
|
||
XTHL ;Get return address from stack
|
||
SHLD DB$Save$RA ;This gets updated by debug code
|
||
PUSH H ;Save return address temporarily
|
||
DCX H ;Subtract 3 to address CALL instruction
|
||
DCX H ; itself
|
||
DCX H
|
||
SHLD DB$Call$Address ;Save actual address of CALL
|
||
POP H ;Recover return address
|
||
|
||
PUSH PSW ;Temporarily save flags to avoid
|
||
; them being changed by DAD SP
|
||
LXI H,0 ;Preserve Stack Pointer
|
||
DAD SP
|
||
INX H ;Correct for extra PUSH PSW needed
|
||
INX H ; to save the flags
|
||
SHLD DB$Save$SP
|
||
POP PSW ;Recover flags
|
||
|
||
LXI SP,DB$Stack ;Switch to local stack
|
||
|
||
PUSH PSW ;Save other user's registers
|
||
PUSH B ;The stack area is specially laid
|
||
PUSH D ; out to access these registers
|
||
|
||
LHLD DB$Save$RA ;Get return address
|
||
MOV A,M ;Get display code
|
||
STA DB$Display$Code
|
||
INX H ;Update return address
|
||
|
||
CPI DB$M ;Check if memory to be displayed
|
||
JNZ DB$Not$Memory
|
||
MOV E,M ;Get DE = start address
|
||
INX H
|
||
MOV D,M
|
||
INX H
|
||
XCHG ;HL = start address
|
||
SHLD DB$Start$Address
|
||
XCHG ;HL -> end address
|
||
MOV E,M ;Get DE = end address
|
||
INX H
|
||
MOV D,M
|
||
INX H
|
||
XCHG ;HL = end address, DE -> Caption
|
||
SHLD DB$End$Address
|
||
XCHG ;HL -> caption string
|
||
DB$Not$Memory:
|
||
;
|
||
; Output Preamble and Caption String
|
||
; The format for everything except memory display is:
|
||
;
|
||
; nnnn : Caption String : RC = vvvv
|
||
; ^ ^ ^
|
||
; Call Address | Value
|
||
; Register Caption (A, B, C...)
|
||
;
|
||
; A Carriage Return, Line Feed is output at the start of the
|
||
; message - but NOT at the end.
|
||
;
|
||
; Memory displays look like :
|
||
;
|
||
; nnnn : Caption String : Start, End ssss, eeee
|
||
; ssss : hh hh hh hh hh hh hh hh hh hh hh hh hh hh hh hh : cccc cccc cccc cccc
|
||
;
|
||
|
||
PUSH H ;Save pointer to caption string
|
||
CALL DB$CRLF ;Display Carriage Return, Line Feed
|
||
CALL DB$Display$CALLA ;Display DB$Call$Address in hex.
|
||
|
||
POP H ;Recover pointer to caption string
|
||
DB$Display$Caption: ;HL -> caption string
|
||
MOV A,M ;Get character
|
||
INX H
|
||
ORA A ;Check if end of string
|
||
JZ DB$End$Caption ;Yes
|
||
|
||
PUSH H ;Save string pointer
|
||
MOV C,A ;Ready for output
|
||
CALL DB$CONOUT ;Display character
|
||
POP H ;Recover string pointer
|
||
JMP DB$Display$Caption ;Go back for next character
|
||
;
|
||
DB$End$Caption:
|
||
SHLD DB$Save$RA ;Save updated return address
|
||
|
||
CALL DB$Colon ;Display ' : '
|
||
|
||
;Display register caption
|
||
LDA DB$Display$Code ;Get user's display code
|
||
MOV E,A ;Make display code into word
|
||
MVI D,0
|
||
PUSH D ;Save word value for later
|
||
|
||
CPI DB$M ;Memory display is a special case
|
||
JZ DB$Display$Mem$Caption ;Yes
|
||
|
||
LXI H,DB$Register$Captions ;Make pointer to address in table
|
||
DAD D ;HL -> Word containing address of
|
||
; register caption
|
||
MOV E,M ;Get LS byte of address
|
||
INX H
|
||
MOV D,M ;DE -> register caption string
|
||
XCHG ;HL -> register caption string
|
||
CALL DB$MSG ;Display message addressed by HL
|
||
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB ' = ',0
|
||
JMP DB$Select$Routine ;Go to correct processor
|
||
;
|
||
DB$Display$Mem$Caption: ;The Memory Display requires a special
|
||
; caption with the start and end
|
||
; addresses
|
||
LXI H,DB$M$RC ;Display specific caption
|
||
CALL DB$MSG
|
||
CALL DB$Colon ;Display ' : '
|
||
|
||
LHLD DB$Start$Address ;Display start address
|
||
CALL DB$DHLH ;Display HL in Hex.
|
||
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB ', ',0
|
||
|
||
LHLD DB$End$Address ;Get end address
|
||
CALL DB$DHLH ;Display HL in Hex.
|
||
CALL DB$CRLF ;Display Carriage Return, Line Feed
|
||
;Drop into select routine
|
||
DB$Select$Routine:
|
||
POP D ;Recover word value Display$Code
|
||
LXI H,DB$Display$Table
|
||
DAD D ;HL -> Address of code to process
|
||
; display requirements
|
||
MOV E,M ;Get LS byte of address
|
||
INX H ;Update pointer
|
||
MOV D,M ;Get MS byte of address
|
||
XCHG ;HL -> code
|
||
|
||
LXI D,DB$Exit ;Fake link on stack
|
||
PUSH D
|
||
PCHL ;"CALL" display processor
|
||
;
|
||
DB$Exit: ;Return to the user
|
||
POP D ;Recover user's registers saved
|
||
POP B ; on local debug stack
|
||
POP PSW
|
||
LHLD DB$Save$SP ;Revert to user's stack
|
||
SPHL
|
||
LHLD DB$Save$RA ;Get updated return address (bypasses
|
||
; in-line parameters
|
||
XTHL ;Replace on top of user's stack
|
||
LHLD DB$Save$HL ;Get user's HL
|
||
RET ;Transfer to correct return address
|
||
|
||
|
||
DB$Display$Table:
|
||
DW DP$F ;Flags
|
||
DW DP$A ;A register
|
||
DW DP$B ;B
|
||
DW DP$C ;C
|
||
DW DP$D ;D
|
||
DW DP$E ;E
|
||
DW DP$H ;H
|
||
DW DP$L ;L
|
||
DW DP$BC ;BC
|
||
DW DP$DE ;DE
|
||
DW DP$HL ;HL
|
||
DW DP$SP ;Stack Pointer
|
||
DW DP$M ;Memory
|
||
DW DP$B$BC ;(BC)
|
||
DW DP$B$DE ;(DE)
|
||
DW DP$B$HL ;(HL)
|
||
DW DP$W$BC ;(BC+1),(BC)
|
||
DW DP$W$DE ;(DE+1),(DE)
|
||
DW DP$W$HL ;(HL+1),(HL)
|
||
;#
|
||
; Debug display processing routines
|
||
;
|
||
DP$F: ;Flags
|
||
;The Flags are displayed in the same way that
|
||
; DDT uses : C1Z0M0E0I0
|
||
LDA DB$Save$F ;Get Flags
|
||
MOV B,A ;Preserve copy
|
||
LXI H,DB$Flags$Msg + 1 ;HL -> first 0/1 in message
|
||
LXI D,DB$Flag$Masks ;DE -> Table of flag mask values
|
||
DB$F$Next:
|
||
LDAX D ;Get next flag mask
|
||
ORA A ;Check if end of table
|
||
JZ DB$F$Display ;Yes, display the results
|
||
|
||
ANA B ;Check if this flag is set
|
||
MVI A,'1' ;Assume yes
|
||
JNZ DB$F$NZ ;Yes it is set
|
||
MVI A,'0' ;No it is clear
|
||
DB$F$NZ:
|
||
MOV M,A ;Store '0' or '1' in message text
|
||
INX H ;Update pointer to next 0/1
|
||
INX H
|
||
INX D ;Update flag mask pointer
|
||
JMP DB$F$Next
|
||
DB$F$Display: ;Display results
|
||
LXI H,DB$Flags$Msg
|
||
JMP DB$MSG ;Display message and return
|
||
;
|
||
DP$A: ;A register
|
||
LDA DB$Save$A ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$B: ;B
|
||
LDA DB$Save$B ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$C: ;C
|
||
LDA DB$Save$C ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$D: ;D
|
||
LDA DB$Save$D ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$E: ;E
|
||
LDA DB$Save$E ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$H: ;H
|
||
LDA DB$Save$H ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$L: ;L
|
||
LDA DB$Save$L ;Get saved value
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$BC: ;BC
|
||
LHLD DB$Save$C ;Get saved WORD value
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$DE: ;DE
|
||
LHLD DB$Save$E ;Get saved WORD value
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$HL: ;HL
|
||
LHLD DB$Save$HL ;Get saved WORD value
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$SP: ;Stack Pointer
|
||
LHLD DB$Save$SP ;Get saved WORD value
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$M: ;Memory
|
||
LHLD DB$End$Address ;Increment end address to make
|
||
INX H ; arithmetic easier
|
||
SHLD DB$End$Address
|
||
|
||
LHLD DB$Start$Address
|
||
CALL DB$M$Check$End ;Compare HL to End$Address
|
||
JC DB$M$Address$OK ;End > Start
|
||
CALL DB$MSGI ;Error Start > End
|
||
DB 0DH,0AH,'** ERROR - Start Address > End **',0
|
||
RET
|
||
;
|
||
DB$M$Next$Line:
|
||
CALL DB$CRLF ;Output Carriage Return, Line Feed
|
||
DB$M$Address$OK: ;Bypass CR,LF for first line
|
||
CALL DB$MSGI ;Indent line
|
||
DB ' ',0
|
||
LHLD DB$Start$Address ;Get start of line address
|
||
CALL DB$DHLH ;Display in hex
|
||
|
||
CALL DB$Colon ;Display ' : '
|
||
|
||
LHLD DB$Start$Address
|
||
DB$M$Next$Hex$Byte:
|
||
PUSH H ;Save memory address
|
||
CALL DB$Blank ;Output a blank
|
||
POP H ;Recover current byte address
|
||
MOV A,M ;Get byte from memory
|
||
INX H ;Update memory pointer
|
||
PUSH H ;Save for later
|
||
CALL DB$DAH ;Display in hex
|
||
POP H ;Recover memory updated address
|
||
CALL DB$M$Check$End ;Compare HL vs End Address
|
||
JZ DB$M$Display$ASCII ;Yes - End of area
|
||
MOV A,L ;Check if at start of new line
|
||
ANI 0000$1111B ; by check if address is XXX0H
|
||
JZ DB$M$Display$ASCII ;Yes
|
||
JMP DB$M$Next$Hex$Byte ;No - loop back for another
|
||
;
|
||
DB$M$Display$ASCII: ;Display bytes in ASCII
|
||
CALL DB$Colon ;Display ' : '
|
||
LHLD DB$Start$Address ;Start ASCII as beginning of line
|
||
DB$M$Next$ASCII$Byte:
|
||
MOV A,M ;Get byte from memory
|
||
PUSH H ;Save memory address
|
||
ANI 0111$1111B ;Remove parity
|
||
MOV C,A ;Prepare for output
|
||
CPI ' ' ;Check if non-graphic
|
||
JNC DB$M$Display$Char ;Char >= Space
|
||
MVI C,'.' ;Display non-graphic as '.'
|
||
DB$M$Display$Char:
|
||
CPI 7FH ;Check if DEL (may be non-graphic)
|
||
JNZ DB$M$Not$DEL ;No - it is graphic
|
||
MVI C,'.' ;Force to '.'
|
||
;
|
||
DB$M$Not$DEL:
|
||
CALL DB$CONOUT ;Display character
|
||
POP H ;Recover memory address
|
||
INX H ;Update memory pointer
|
||
SHLD DB$Start$Address ;Update memory copy
|
||
CALL DB$M$Check$End ;Check if end of memory dump
|
||
JZ DB$M$Exit ;Yes - Done
|
||
MOV A,L ;Check if end of line
|
||
ANI 0000$1111B ; by checking address = XXX0H
|
||
JZ DB$M$Next$Line ;Yes - start next line
|
||
MOV A,L ;Check if extra blank needed
|
||
ANI 0000$0011B ; if address is multiple of 4
|
||
JNZ DB$M$Next$ASCII$Byte ;No - go back for next character
|
||
CALL DB$Blank ;Yes, output blank
|
||
JMP DB$M$Next$ASCII$Byte ;Go back for next character
|
||
|
||
;
|
||
DB$M$Exit:
|
||
JMP DB$CRLF ;Output Carriage Return, Line Feed
|
||
; and Return
|
||
;
|
||
DB$M$Check$End: ;Compares HL vs End$Address
|
||
PUSH D ;Save DE (Defensive programming)
|
||
XCHG ;DE = Current Address
|
||
LHLD DB$End$Address ;Get end address
|
||
MOV A,D ;Compare MS bytes
|
||
CMP H
|
||
JNZ DB$M$Check$End$X ;Exit now as they are unequal
|
||
MOV A,E ;Compare LS bytes
|
||
CMP L
|
||
DB$M$Check$End$X:
|
||
XCHG ;HL = Current address
|
||
POP D ;Recover DE
|
||
RET ;Return with condition flags set
|
||
;
|
||
DP$B$BC: ;(BC)
|
||
LHLD DB$Save$C ;Get saved WORD value
|
||
MOV A,M ;Get byte addressed by it
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$B$DE: ;(DE)
|
||
LHLD DB$Save$E ;Get saved WORD value
|
||
MOV A,M ;Get byte addressed by it
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$B$HL: ;(HL)
|
||
LHLD DB$Save$HL ;Get saved WORD value
|
||
MOV A,M ;Get byte addressed by it
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
DP$W$BC: ;(BC+1),(BC)
|
||
LHLD DB$Save$C ;Get saved WORD value
|
||
MOV E,M ;Get WORD addresses by it
|
||
INX H
|
||
MOV D,M
|
||
XCHG ;HL = WORD to be displayed
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$W$DE: ;(DE+1),(DE)
|
||
LHLD DB$Save$E ;Get saved WORD value
|
||
MOV E,M ;Get WORD addresses by it
|
||
INX H
|
||
MOV D,M
|
||
XCHG ;HL = WORD to be displayed
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
DP$W$HL: ;(HL+1),(HL)
|
||
LHLD DB$Save$HL ;Get saved WORD value
|
||
MOV E,M ;Get WORD addresses by it
|
||
INX H
|
||
MOV D,M
|
||
XCHG ;HL = WORD to be displayed
|
||
JMP DB$DHLH ;Display it and return
|
||
;
|
||
;#
|
||
; DB$Display$CALLA
|
||
; This routine displays the DB$Call$Address in hexadecimal,
|
||
; followed by " : ".
|
||
;
|
||
DB$Display$CALLA:
|
||
PUSH H ;Save caller's HL
|
||
LHLD DB$Call$Address ;Get the call address
|
||
CALL DB$DHLH ;Display HL in hex.
|
||
POP H ;Recover caller's HL
|
||
JMP DB$Colon ;Display " : " and return
|
||
;
|
||
;#
|
||
;
|
||
; DB$DHLH
|
||
; Display HL in Hex.
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; HL = Value to be displayed
|
||
;
|
||
DB$DHLH:
|
||
PUSH H ;Save input value
|
||
MOV A,H ;Get MS byte first
|
||
CALL DB$DAH ;Display A in Hex
|
||
POP H ;Recover input value
|
||
MOV A,L ;Get LS byte
|
||
JMP DB$DAH ;Display it and return
|
||
;
|
||
;#
|
||
;
|
||
; DB$DAH
|
||
; Display A register in Hexadecimal
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; A = Value to be converted and output
|
||
;
|
||
DB$DAH:
|
||
PUSH PSW ;Take a copy of the value to be converted
|
||
RRC ;Shift A right four places
|
||
RRC
|
||
RRC
|
||
RRC
|
||
CALL DB$Nibble$To$Hex ;Convert LS 4 bits to ASCII
|
||
CALL DB$CONOUT ;Display the character
|
||
POP PSW ;Get original value again
|
||
CALL DB$Nibble$To$Hex ;Convert LS 4 bits to ASCII
|
||
JMP DB$CONOUT ;Display and return to caller
|
||
;#
|
||
;
|
||
; DB$CAH
|
||
; Convert A register to Hexadecimal ASCII and store in
|
||
; specified address.
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; A = Value to be converted and output
|
||
; HL -> Buffer area to receive two characters of output
|
||
;
|
||
; Exit Parameters
|
||
;
|
||
; HL -> Byte following last hex byte output
|
||
;
|
||
DB$CAH:
|
||
PUSH PSW ;Take a copy of the value to be converted
|
||
RRC ;Shift A right four places
|
||
RRC
|
||
RRC
|
||
RRC
|
||
CALL DB$Nibble$To$Hex ;Convert to ASCII Hex.
|
||
MOV M,A ;Save in memory
|
||
INX H ;Update pointer
|
||
POP PSW ;Get original value again
|
||
CALL DB$Nibble$To$Hex ;Convert to ASCII Hex.
|
||
MOV M,A ;Save in memory
|
||
INX H ;Update pointer
|
||
RET
|
||
;#
|
||
;
|
||
; Minor subroutines.
|
||
;
|
||
;
|
||
; DB$Nibble$To$Hex
|
||
; This is a minor subroutine that converts the least
|
||
; significant four bits of the A register into an ASCII
|
||
; Hex. character in A and C
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; A = Nibble to be converted in LS 4 bits
|
||
;
|
||
; Exit Parameters
|
||
;
|
||
; A,C = ASCII Hex. character
|
||
;
|
||
DB$Nibble$To$Hex:
|
||
ANI 0000$1111B ;Isolate LS four bits
|
||
ADI '0' ;Convert to ASCII
|
||
CPI '9' + 1 ;Compare to maximum
|
||
JC DB$NTH$Numeric ;No need to convert to A -> F
|
||
ADI 7 ;Convert to a letter
|
||
DB$NTH$Numeric:
|
||
MOV C,A ;For convenience of other routines
|
||
RET
|
||
|
||
|
||
;
|
||
; DB$CRLF
|
||
; Simple routine to display Carriage Return, Line Feed.
|
||
;
|
||
DB$CRLF:
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB 0DH,0AH,0
|
||
RET
|
||
;
|
||
; DB$Colon
|
||
; Simple routine to display ' : '.
|
||
;
|
||
DB$Colon:
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB ' : ',0
|
||
RET
|
||
;
|
||
; DB$Blank
|
||
; Simple routine to display ' '.
|
||
;
|
||
DB$Blank:
|
||
CALL DB$MSGI ;Display in-line message
|
||
DB ' ',0
|
||
RET
|
||
;#
|
||
;
|
||
; Message Processing subroutines.
|
||
;
|
||
; DB$MSGI (Message In-line)
|
||
; Output null-byte terminated message that follows the
|
||
; CALL to MSGOUTI.
|
||
;
|
||
; Calling sequence
|
||
;
|
||
; CALL DB$MSGI
|
||
; DB 'Message',0
|
||
; ... next instruction
|
||
;
|
||
; Exit Parameters
|
||
; HL -> instruction following message
|
||
;
|
||
;
|
||
DB$MSGI:
|
||
;Get return address of stack, save
|
||
; user's HL on top of stack
|
||
XTHL ;HL -> Message
|
||
|
||
PUSH PSW ;Save all user's registers
|
||
PUSH B
|
||
PUSH D
|
||
DB$MSGI$Next:
|
||
MOV A,M ;Get next data byte
|
||
INX H ;Update message pointer
|
||
ORA A ;Check if null byte
|
||
JNZ DB$MSGIC ;No, continue
|
||
|
||
POP D ;Recover user's registers
|
||
POP B
|
||
POP PSW
|
||
XTHL ;Recover user's HL from stack, replacing
|
||
; it with updated return address
|
||
RET ;Return to address after 00-byte
|
||
;after in-line message
|
||
DB$MSGIC:
|
||
PUSH H ;Save message pointer
|
||
MOV C,A ;Ready for output
|
||
CALL DB$CONOUT
|
||
POP H ;Recover message pointer
|
||
JMP DB$MSGI$Next ;Go back for next char.
|
||
|
||
;
|
||
; DB$MSG
|
||
; Output null-byte terminated message.
|
||
;
|
||
; Calling sequence
|
||
;
|
||
; MESSAGE: DB 'Message',0
|
||
; :
|
||
; LXI H,MESSAGE
|
||
; CALL DB$MSG
|
||
;
|
||
; Exit Parameters
|
||
; HL -> Null byte terminator
|
||
;
|
||
;
|
||
DB$MSG:
|
||
PUSH PSW ;Save user's registers
|
||
PUSH B
|
||
PUSH D
|
||
DB$MSG$Next:
|
||
MOV A,M ;Get next byte for output
|
||
ORA A ;Check if 00-byte terminator
|
||
JZ DB$MSG$X ;Exit
|
||
INX H ;Update message pointer
|
||
PUSH H ;Save updated pointer
|
||
MOV C,A ;Ready for output
|
||
CALL DB$CONOUT
|
||
POP H ;Recover message pointer
|
||
JMP DB$MSG$Next ;Go back for next character
|
||
;
|
||
DB$MSG$X:
|
||
POP D ;Recover user's registers
|
||
POP B
|
||
POP PSW
|
||
RET
|
||
;#
|
||
;
|
||
; Debug Input Routine
|
||
;
|
||
; This routine helps debug code in which INput instructions
|
||
; would normally occur. The Opcode of the IN instruction
|
||
; must be replaced by a value of 0E7H (RST 4).
|
||
;
|
||
; This routine picks up the port number contained in the byte
|
||
; following the RST 4, converts it to Hexadecimal, and
|
||
; displays the message :
|
||
;
|
||
; Input from Port XX :
|
||
;
|
||
; It then accepts two characters (in Hex) from the keyboard,
|
||
; converts these to binary in A, and then returns control
|
||
; to the byte following the port number.
|
||
;
|
||
; *******
|
||
; WARNING - This routine uses both DB$CONOUT and BDOS calls
|
||
; *******
|
||
;
|
||
DBIN$Message: DB 'Input from Port '
|
||
DBIN$Port: DB 'XX : ',0
|
||
;
|
||
;
|
||
DB$Input:
|
||
SHLD DB$Save$HL ;Save user's HL
|
||
POP H ;Recover address of port number
|
||
DCX H ;Backup to point to RST
|
||
SHLD DB$Call$Address ;Save for later display
|
||
INX H ;Restore to point to port number
|
||
;Note : A need not be preserved
|
||
MOV A,M ;Get Port number
|
||
INX H ;Update return address to bypass port number
|
||
SHLD DB$Save$RA ;Save return address
|
||
PUSH B ;Save remaining registers
|
||
PUSH D
|
||
PUSH PSW ;Save port number for later
|
||
|
||
|
||
CALL DB$Flag$Save$On ;Save current state of debug flag
|
||
; and enable debug output
|
||
|
||
CALL DB$CRLF ;Display Carriage Return, Line Feed
|
||
CALL DB$Display$CALLA;Display call address
|
||
POP PSW ;Recover port number
|
||
LXI H,DBIN$Port
|
||
CALL DB$CAH ;Convert to Hex and store in message
|
||
LXI H,DBIN$Message ;Output Prompting Message
|
||
CALL DB$MSG
|
||
MVI C,2 ;Get 2 digit hex value
|
||
CALL DB$GHV ;Returns value in HL
|
||
MOV A,L ;Get just single byte
|
||
|
||
CALL DB$Flag$Restore ;Restore debug output to previous state
|
||
|
||
POP D ;Recover registers
|
||
POP B
|
||
LHLD DB$Save$HL ;Get previous HL
|
||
PUSH H ;Put on top of stack
|
||
LHLD DB$Save$RA ;Get return address
|
||
XTHL ;TOS = Return Address, HL = Previous value
|
||
RET
|
||
;#
|
||
;
|
||
; Debug Output Routine
|
||
;
|
||
; This routine helps debug code in which OUTput instructions
|
||
; would normally occur. The Opcode of the OUT instruction
|
||
; must be replaced by a value of 0EFH (RST 5).
|
||
;
|
||
; This routine picks up the port number contained in the byte
|
||
; following the RST 5, converts it to Hexadecimal, and
|
||
; displays the message :
|
||
;
|
||
; Output to Port XX : AA
|
||
;
|
||
; Where AA is the contents of the A register prior to the
|
||
; RST 5 being executed.
|
||
; Control is then returned to the byte following the port number.
|
||
;
|
||
; *******
|
||
; WARNING - This routine uses both DB$CONOUT and BDOS calls
|
||
; *******
|
||
;
|
||
;
|
||
DBO$Message: DB 'Output to Port '
|
||
DBO$Port: DB 'XX : '
|
||
DBO$Value: DB 'AA',0
|
||
;
|
||
;
|
||
DB$Output:
|
||
SHLD DB$Save$HL ;Save user's HL
|
||
POP H ;Recover address of port number
|
||
DCX H ;Backup to point to RST
|
||
SHLD DB$Call$Address ;Save for later display
|
||
INX H ;Restore to point at port number
|
||
STA DB$Save$A ;Preserve value to be output
|
||
MOV A,M ;Get Port number
|
||
INX H ;Update return address to bypass port number
|
||
SHLD DB$Save$RA ;Save return address
|
||
PUSH B ;Save remaining registers
|
||
PUSH D
|
||
PUSH PSW ;Save port number for later
|
||
|
||
CALL DB$Flag$Save$On ;Save current state of debug flag
|
||
; and enable debug output
|
||
|
||
CALL DB$CRLF ;Display Carriage Return, Line Feed
|
||
CALL DB$Display$CALLA;Display call address
|
||
POP PSW ;Recover port number
|
||
LXI H,DBO$Port
|
||
CALL DB$CAH ;Convert to Hex and store in message
|
||
|
||
LDA DB$Save$A
|
||
LXI H,DBO$Value ;Convert value to be output
|
||
CALL DB$CAH ;Convert to Hex and store in message
|
||
|
||
LXI H,DBO$Message ;Output Prompting Message
|
||
CALL DB$MSG
|
||
|
||
CALL DB$Flag$Restore ;Restore debug flag to previous state
|
||
|
||
POP D ;Recover registers
|
||
POP B
|
||
LHLD DB$Save$HL ;Get previous HL
|
||
PUSH H ;Put on top of stack
|
||
LHLD DB$Save$RA ;Get return address
|
||
XTHL ;TOS = Return Address, HL = Previous value
|
||
LDA DB$Save$A ;Recover A (NOTE : FLAG NOT RESTORED)
|
||
RET
|
||
;#
|
||
;
|
||
; DB$Flag$Save$On
|
||
; This routine is only used for DB$IN/OUT.
|
||
; It saves the current state of the debug control flag,
|
||
; D$Flag, and then enables it so as to make sure that
|
||
; DB$IN/OUT output always goes out.
|
||
;
|
||
DB$Flag$Previous: DB 0 ;Previous flag value
|
||
;
|
||
DB$Flag$Save$On:
|
||
PUSH PSW ;Save caller's registers
|
||
LDA DB$Flag ;Get current value
|
||
STA DB$Flag$Previous ;Save it
|
||
MVI A,0FFH ;Set flag
|
||
STA DB$Flag
|
||
POP PSW
|
||
RET
|
||
;#
|
||
;
|
||
; DB$Flag$Restore
|
||
; This routine is only used for DB$IN/OUT.
|
||
; It restores the debug control flag, DB$Flag, to
|
||
; its former state.
|
||
;
|
||
DB$Flag$Restore:
|
||
PUSH PSW
|
||
LDA DB$Flag$Previous ;Get previous setting
|
||
STA DB$Flag ;Set debug control flag
|
||
POP PSW
|
||
RET
|
||
|
||
;
|
||
;#
|
||
;
|
||
; Get Hex Value
|
||
;
|
||
; This subroutine outputs a prompting message, and then reads
|
||
; the keyboard in order to get a hexadecimal value.
|
||
; It is somewhat simplistic in that the first non-hex value
|
||
; terminates the input. The maximum number of digits to be
|
||
; converted is specified as an input parameter.
|
||
; entered, only the last four are significant.
|
||
;
|
||
;****************************************************************
|
||
; W A R N I N G
|
||
; DB$GHV will ALWAYS use the BDOS to perform a Read Console
|
||
; Function (#10). Be CAREFUL if you use this routine from
|
||
; within an executing BIOS.
|
||
;****************************************************************
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; HL -> 00-byte terminated message to be output
|
||
; C = number of hexadecimal digits to be input
|
||
;
|
||
;
|
||
DB$GHV$Buffer: ;Input buffer for console characters
|
||
DB$GHV$Max$Count:
|
||
DB 0 ;Set to the maximum number of chars
|
||
; to be input
|
||
DB$GHV$Input$Count:
|
||
DB 0 ;Set by the BDOS to the actual number
|
||
; or chars entered
|
||
DB$GHV$Data$Bytes
|
||
DS 5 ;Buffer space for the characters
|
||
;
|
||
;
|
||
DB$GHV:
|
||
MOV A,C ;Get maximum characters to be input
|
||
CPI 5 ;Check against maximum count
|
||
JC DB$GHV$Count$OK ;Carry set if A < 5
|
||
MVI A,4 ;Force to only four characters
|
||
DB$GHV$Count$OK:
|
||
STA DB$GHV$Max$Count ;Setup maximum count in input buffer
|
||
CALL DB$MSG ;Output Prompting Message
|
||
LXI D,DB$GHV$Buffer ;Accept characters from console
|
||
MVI C,B$READCONS ;Function Code
|
||
CALL BDOS
|
||
|
||
MVI C,B$CONOUT ;Output a Line Feed
|
||
MVI E,0AH
|
||
CALL BDOS
|
||
|
||
LXI H,0 ;Initial value
|
||
LXI D,DB$GHV$Data$Bytes ;DE -> Data characters
|
||
LDA DB$GHV$Input$Count ;Get count of characters input
|
||
MOV C,A ;Keep count in C
|
||
DB$GHV$Loop:
|
||
DCR C ;Downdate count
|
||
RM ;Return when all done (HL has value)
|
||
LDAX D ;Get next character from buffer
|
||
INX D ;Update buffer pointer
|
||
CALL DB$A$To$Upper ;Convert A to upper case if need be
|
||
CPI '0' ;Check if less than 0
|
||
RC ;Yes, terminate
|
||
CPI '9' + 1 ;Check if > 9
|
||
JC DB$GHV$Hex$Digit ;No - it must be numeric
|
||
CPI 'A' ;Check if < 'A'
|
||
RC ;Yes - terminate
|
||
CPI 'F' + 1 ;Check if > 'F'
|
||
RNC ;Yes - terminate
|
||
SUI 'A' - 10 ;Convert A through F to numeric
|
||
JMP DB$GHV$Shift$Left$4 ;Combine with current result
|
||
;
|
||
DB$GHV$Hex$Digit:
|
||
SUI '0' ;Convert to binary
|
||
DB$GHV$Shift$Left$4:
|
||
DAD H ;Shift HL left four bits
|
||
DAD H
|
||
DAD H
|
||
DAD H
|
||
ADD L ;Add binary value in LS 4 bits of A
|
||
MOV L,A ;Put back into HL total
|
||
JMP DB$GHV$Loop ;Loop back for next character
|
||
;#
|
||
;
|
||
; A to Upper
|
||
; Converts the contents of the A register to an upper
|
||
; case letter if it is currently a lower case letter.
|
||
;
|
||
; Entry Parameters
|
||
;
|
||
; A = Character to be converted
|
||
;
|
||
; Exit Parameters
|
||
;
|
||
; A = Converted character
|
||
;
|
||
DB$A$To$Upper:
|
||
CPI 'a' ;Compare to lower limit
|
||
RC ;No need to convert
|
||
CPI 'z' + 1 ;Compare to upper limit
|
||
RNC ;No need to convert
|
||
ANI 5FH ;Convert to upper case
|
||
RET
|
||
|