mirror of https://github.com/wwarthen/RomWBW.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
588 lines
11 KiB
588 lines
11 KiB
;===============================================================================
|
|
;
|
|
; INTTEST - Test HBIOS interrupt API functions
|
|
;
|
|
;===============================================================================
|
|
;
|
|
; Author: Wayne Warthen (wwarthen@gmail.com)
|
|
;_______________________________________________________________________________
|
|
;
|
|
; Usage:
|
|
; INTTEST
|
|
;_______________________________________________________________________________
|
|
;
|
|
;===============================================================================
|
|
; Definitions
|
|
;===============================================================================
|
|
;
|
|
stksiz .equ $40 ; Working stack size
|
|
;
|
|
restart .equ $0000 ; CP/M restart vector
|
|
bdos .equ $0005 ; BDOS invocation vector
|
|
;
|
|
bf_sysint .equ $FC ; INT function
|
|
;
|
|
bf_sysintinfo .equ $00 ; INT INFO subfunction
|
|
bf_sysintget .equ $10 ; INT GET subfunction
|
|
bf_sysintset .equ $20 ; INT SET subfunction
|
|
;
|
|
;===============================================================================
|
|
; Code Section
|
|
;===============================================================================
|
|
;
|
|
.org $100
|
|
;
|
|
; setup stack (save old value)
|
|
ld (stksav),sp ; save stack
|
|
ld sp,stack ; set new stack
|
|
;
|
|
; initialization
|
|
call init ; initialize
|
|
jr nz,exit ; abort if init fails
|
|
;
|
|
; process
|
|
call process ; do main processing
|
|
jr nz,exit ; abort on error
|
|
;
|
|
exit: ; clean up and return to command processor
|
|
call crlf ; formatting
|
|
ld sp,(stksav) ; restore stack
|
|
;jp restart ; return to CP/M via restart
|
|
ret ; return to CP/M w/o restart
|
|
;
|
|
; Initialization
|
|
;
|
|
init:
|
|
call crlf ; formatting
|
|
ld de,msgban ; point to version message part 1
|
|
call prtstr ; print it
|
|
;
|
|
; relocate handler
|
|
ld hl,reladr
|
|
ld de,$8000
|
|
ld bc,hsiz
|
|
ldir
|
|
;
|
|
initx
|
|
; initialization complete
|
|
xor a ; signal success
|
|
ret ; return
|
|
;
|
|
; Process
|
|
;
|
|
process:
|
|
;
|
|
; Get info
|
|
;
|
|
call crlf2
|
|
ld de,msginfo ; message
|
|
call prtstr
|
|
;
|
|
ld b,bf_sysint ; INT function
|
|
ld c,bf_sysintinfo ; INFO subfunction
|
|
rst 08
|
|
ld a,d
|
|
ld (intmod),a ; save int mode
|
|
ld a,e
|
|
ld (veccnt),a ; save vector count
|
|
;
|
|
push de
|
|
call crlf
|
|
ld de,msgmode ; mode
|
|
call prtstr
|
|
pop de
|
|
push de
|
|
ld a,d ; interrupt mode
|
|
call prtdecb
|
|
call crlf
|
|
ld de,msgcnt ; count of vectors
|
|
call prtstr
|
|
pop de
|
|
ld a,e
|
|
call prtdecb
|
|
;
|
|
; Done if int mode is 0
|
|
;
|
|
ld a,(intmod)
|
|
or a
|
|
ret z
|
|
;
|
|
; List vectors
|
|
;
|
|
call crlf2
|
|
ld de,msglst
|
|
call prtstr
|
|
ld a,(veccnt) ; get count of vectors
|
|
or a
|
|
jr z,estidx ; bypass if nothing to list
|
|
ld b,a ; make it the loop counter
|
|
ld c,0 ; vector entry index
|
|
;
|
|
lstlp:
|
|
push bc
|
|
call crlf
|
|
ld a,' '
|
|
call prtchr
|
|
call prtchr
|
|
ld a,c
|
|
call prthex
|
|
ld a,':'
|
|
call prtchr
|
|
ld e,c
|
|
ld b,bf_sysint
|
|
ld c,bf_sysintget
|
|
rst 08
|
|
push hl
|
|
pop bc
|
|
call prthexword
|
|
pop bc
|
|
inc c
|
|
djnz lstlp
|
|
;
|
|
; Establish interrupt vector index to hook
|
|
;
|
|
estidx:
|
|
call crlf2
|
|
ld de,msgvec
|
|
call prtstr
|
|
call getbuf
|
|
ld hl,lnbuf+1
|
|
ld a,(hl) ; get line length
|
|
or a ; set flags
|
|
jr z,estidx ; nothing entered
|
|
ld b,a ; set b to line length
|
|
inc hl
|
|
call hexbyt
|
|
jr c,estidx
|
|
ld (vecidx),a
|
|
ld hl,veccnt ; check index against valid range
|
|
cp (hl)
|
|
jr nc,estidx ; vector index out of bounds
|
|
;
|
|
; Hook vector
|
|
;
|
|
call crlf2
|
|
ld de,msghk1
|
|
call prtstr
|
|
ld a,(vecidx)
|
|
call prthex
|
|
ld de,msghk2
|
|
call prtstr
|
|
ld a,$ff
|
|
ld (count),a ; set counter to max value
|
|
;
|
|
ld a,(intmod)
|
|
cp 1
|
|
jr z,hkim
|
|
cp 2
|
|
jr z,hkim
|
|
cp 3
|
|
jr z,hkim
|
|
ret
|
|
;
|
|
; Setup interrupt handler
|
|
;
|
|
hkim:
|
|
ld hl,int ; pointer to my interrupt handler
|
|
ld b,bf_sysint
|
|
ld c,bf_sysintset ; set new vector
|
|
ld a,(vecidx) ; get vector idx
|
|
ld e,a ; put in E
|
|
di
|
|
rst 08 ; do it
|
|
ld (chain),hl ; save the chain address
|
|
ei ; interrupts back on
|
|
jr start
|
|
;
|
|
; Wait for counter to countdown to zero
|
|
;
|
|
start:
|
|
call crlf2
|
|
ld de,msgrun ; message
|
|
call prtstr
|
|
call crlf2
|
|
ld a,(count)
|
|
ld e,a
|
|
call prthex ; print it
|
|
ld a,13
|
|
call prtchr
|
|
loop:
|
|
call getchr ; check console
|
|
cp $1B ; <esc> to exit
|
|
jr z,unhook ; if so, bail out
|
|
ld a,(count) ; get current count value
|
|
cp e
|
|
jr z,loop
|
|
ld e,a
|
|
push af
|
|
call prthex ; print it
|
|
ld a,13
|
|
call prtchr
|
|
pop af
|
|
or a ; set flags
|
|
jr z,unhook ; done
|
|
jr loop ; and loop
|
|
;
|
|
; Unhook
|
|
;
|
|
unhook:
|
|
call crlf2
|
|
ld de,msgunhk
|
|
call prtstr
|
|
ld hl,(chain) ; original vector
|
|
ld b,bf_sysint
|
|
ld c,bf_sysintset ; set new vector
|
|
ld a,(vecidx) ; get vector idx
|
|
ld e,a ; put in E
|
|
di
|
|
rst 08 ; do it
|
|
ei ; interrupts back on
|
|
;
|
|
xor a ; signal success
|
|
ret ; done
|
|
;
|
|
; Get a character from console, return in A
|
|
; returs zero if nothing available
|
|
;
|
|
getchr:
|
|
push bc ; save registers
|
|
push de
|
|
push hl
|
|
ld e,$FF ; read a character
|
|
ld c,$06 ; BDOS direct i/o function
|
|
call bdos ; do it
|
|
pop hl ; restore registers
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
; Get console buffer
|
|
;
|
|
getbuf:
|
|
push bc
|
|
push de
|
|
push hl
|
|
ld de,lnbuf
|
|
ld c,$0A
|
|
call bdos
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
; Print character in A without destroying any registers
|
|
;
|
|
prtchr:
|
|
push bc ; save registers
|
|
push de
|
|
push hl
|
|
ld e,a ; character to print in E
|
|
ld c,$02 ; BDOS function to output a character
|
|
call bdos ; do it
|
|
pop hl ; restore registers
|
|
pop de
|
|
pop bc
|
|
ret
|
|
;
|
|
prtdot:
|
|
;
|
|
; shortcut to print a dot preserving all regs
|
|
push af ; save af
|
|
ld a,'.' ; load dot char
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
prtcr:
|
|
;
|
|
; shortcut to print a dot preserving all regs
|
|
push af ; save af
|
|
ld a,13 ; load CR value
|
|
call prtchr ; print it
|
|
pop af ; restore af
|
|
ret ; done
|
|
;
|
|
; Print a zero terminated string at (DE) without destroying any registers
|
|
;
|
|
prtstr:
|
|
push de
|
|
;
|
|
prtstr1:
|
|
ld a,(de) ; get next char
|
|
or a
|
|
jr z,prtstr2
|
|
call prtchr
|
|
inc de
|
|
jr prtstr1
|
|
;
|
|
prtstr2:
|
|
pop de ; restore registers
|
|
ret
|
|
;
|
|
; Print the value in A in hex without destroying any registers
|
|
;
|
|
prthex:
|
|
push af ; save AF
|
|
push de ; save DE
|
|
call hexascii ; convert value in A to hex chars in DE
|
|
ld a,d ; get the high order hex char
|
|
call prtchr ; print it
|
|
ld a,e ; get the low order hex char
|
|
call prtchr ; print it
|
|
pop de ; restore DE
|
|
pop af ; restore AF
|
|
ret ; done
|
|
;
|
|
; print the hex word value in bc
|
|
;
|
|
prthexword:
|
|
push af
|
|
ld a,b
|
|
call prthex
|
|
ld a,c
|
|
call prthex
|
|
pop af
|
|
ret
|
|
;
|
|
; print the hex dword value in de:hl
|
|
;
|
|
prthex32:
|
|
push bc
|
|
push de
|
|
pop bc
|
|
call prthexword
|
|
push hl
|
|
pop bc
|
|
call prthexword
|
|
pop bc
|
|
ret
|
|
;
|
|
; Convert binary value in A to ascii hex characters in DE
|
|
;
|
|
hexascii:
|
|
ld d,a ; save A in D
|
|
call hexconv ; convert low nibble of A to hex
|
|
ld e,a ; save it in E
|
|
ld a,d ; get original value back
|
|
rlca ; rotate high order nibble to low bits
|
|
rlca
|
|
rlca
|
|
rlca
|
|
call hexconv ; convert nibble
|
|
ld d,a ; save it in D
|
|
ret ; done
|
|
;
|
|
; Convert low nibble of A to ascii hex
|
|
;
|
|
hexconv:
|
|
and $0F ; low nibble only
|
|
add a,$90
|
|
daa
|
|
adc a,$40
|
|
daa
|
|
ret
|
|
;
|
|
; Print value of A or HL in decimal with leading zero suppression
|
|
; Use prtdecb for A or prtdecw for HL
|
|
;
|
|
prtdecb:
|
|
push hl
|
|
ld h,0
|
|
ld l,a
|
|
call prtdecw ; print it
|
|
pop hl
|
|
ret
|
|
;
|
|
prtdecw:
|
|
push af
|
|
push bc
|
|
push de
|
|
push hl
|
|
call prtdec0
|
|
pop hl
|
|
pop de
|
|
pop bc
|
|
pop af
|
|
ret
|
|
;
|
|
prtdec0:
|
|
ld e,'0'
|
|
ld bc,-10000
|
|
call prtdec1
|
|
ld bc,-1000
|
|
call prtdec1
|
|
ld bc,-100
|
|
call prtdec1
|
|
ld c,-10
|
|
call prtdec1
|
|
ld e,0
|
|
ld c,-1
|
|
prtdec1:
|
|
ld a,'0' - 1
|
|
prtdec2:
|
|
inc a
|
|
add hl,bc
|
|
jr c,prtdec2
|
|
sbc hl,bc
|
|
cp e
|
|
ret z
|
|
ld e,0
|
|
call prtchr
|
|
ret
|
|
;
|
|
; Start a new line
|
|
;
|
|
crlf2:
|
|
call crlf ; two of them
|
|
crlf:
|
|
push af ; preserve AF
|
|
ld a,13 ; <CR>
|
|
call prtchr ; print it
|
|
ld a,10 ; <LF>
|
|
call prtchr ; print it
|
|
pop af ; restore AF
|
|
ret
|
|
;
|
|
; Get the next non-blank character from (HL).
|
|
;
|
|
nonblank:
|
|
ld a,(hl) ; load next character
|
|
or a ; string ends with a null
|
|
ret z ; if null, return pointing to null
|
|
cp ' ' ; check for blank
|
|
ret nz ; return if not blank
|
|
inc hl ; if blank, increment character pointer
|
|
jr nonblank ; and loop
|
|
;
|
|
; Convert character in A to uppercase
|
|
;
|
|
ucase:
|
|
cp 'a' ; if below 'a'
|
|
ret c ; ... do nothing and return
|
|
cp 'z' + 1 ; if above 'z'
|
|
ret nc ; ... do nothing and return
|
|
res 5,a ; clear bit 5 to make lower case -> upper case
|
|
ret ; and return
|
|
;
|
|
; Add the value in A to HL (HL := HL + A)
|
|
;
|
|
addhl:
|
|
add a,l ; A := A + L
|
|
ld l,a ; Put result back in L
|
|
ret nc ; if no carry, we are done
|
|
inc h ; if carry, increment H
|
|
ret ; and return
|
|
;
|
|
; Jump indirect to address in HL
|
|
;
|
|
jphl:
|
|
jp (hl)
|
|
;
|
|
; Convert hex chars to a byte value.
|
|
; Enter with HL pointing to buffer with chars to convert
|
|
; and B containing number of chars. Returns value in A.
|
|
; CF set to indicate error (overflow or invalid char).
|
|
;
|
|
hexbyt:
|
|
ld c,0 ; working value starts at zero
|
|
hexbyt1:
|
|
sla c ; rotate low nibble to high
|
|
ret c
|
|
sla c
|
|
ret c
|
|
sla c
|
|
ret c
|
|
sla c
|
|
ret c
|
|
ld a,(hl) ; load next char
|
|
call hexnibl ; convert to binary
|
|
ret c ; abort on error
|
|
or c ; combine w/ working value
|
|
ld c,a ; and put back in c
|
|
inc hl ; next position
|
|
djnz hexbyt1 ; next character
|
|
ld a,c ; ret val to a
|
|
or a ; clear carry
|
|
ret
|
|
;
|
|
; Convert hex character at (HL) to binary value
|
|
; Set CF to indicate invalid character
|
|
;
|
|
hexnibl:
|
|
; handle '0'-'9'
|
|
cp '0'
|
|
jr c,hexnibl1 ; if < '0', out of range
|
|
cp '9'+1
|
|
jr nc,hexnibl1 ; if > '9', out of range
|
|
and $0F ; convert to binary value
|
|
ret ; and done
|
|
hexnibl1:
|
|
; handle 'A'-'F'
|
|
call ucase ; force upper case
|
|
cp 'A'
|
|
jr c,hexnibl2 ; if < 'A', out of range
|
|
cp 'F'+1
|
|
jr nc,hexnibl2 ; if > 'F', out of range
|
|
and $0F ; convert to binary value
|
|
add a,9 ;
|
|
ret ; and done
|
|
hexnibl2:
|
|
; invalid character
|
|
scf ; signal error
|
|
ret
|
|
;
|
|
;===============================================================================
|
|
; Storage Section
|
|
;===============================================================================
|
|
;
|
|
intmod .db 0 ; active interrupt mode
|
|
veccnt .db 0 ; count of ingterrupt vectors
|
|
vecidx .db 0 ; vector index to hook
|
|
lnbuf .db 10,0 ; 10 char BDOS line input buffer
|
|
.fill 10
|
|
;
|
|
stksav .dw 0 ; stack pointer saved at start
|
|
.fill stksiz,0 ; stack
|
|
stack .equ $ ; stack top
|
|
;
|
|
; Messages
|
|
;
|
|
msgban .db "INTTEST v1.3, 16-May-2024",13,10
|
|
.db "Copyright (C) 2024, Wayne Warthen, GNU GPL v3",0
|
|
msginfo .db "Interrupt information request...",0
|
|
msgmode .db " Active interrupt mode: ",0
|
|
msgcnt .db " Vector entries in use: ",0
|
|
msglst .db "Interrupt vector address list:",0
|
|
msghk1 .db "Hooking vector ",0
|
|
msghk2 .db "...",0
|
|
msgunhk .db "Unhooking vector...",0
|
|
msgrun .db "Interrupt countdown (press <esc> to exit)...",0
|
|
msgvec .db "Enter vector to hook (hex): ",0
|
|
;
|
|
;===============================================================================
|
|
; Interrupt Handler
|
|
;===============================================================================
|
|
;
|
|
reladr .equ $ ; relocation start adr
|
|
;
|
|
.org $8000 ; code will run here
|
|
;
|
|
int:
|
|
; count down to zero
|
|
ld a,(count) ; get current count value
|
|
or a ; test for zero
|
|
jr z,int1 ; if zero, leave it alone
|
|
dec a ; decrement
|
|
ld (count),a ; and save
|
|
int1:
|
|
xor a ; signal int has NOT been handled
|
|
; follow the chain...
|
|
ld hl,(chain) ; get chain adr
|
|
jp (hl) ; go there
|
|
; ret
|
|
;
|
|
chain .dw $0000 ; chain address
|
|
count .db 0 ; counter
|
|
;
|
|
hsiz .equ $ - $8000 ; size of handler to relocate
|
|
.end
|
|
|