;=============================================================================== ; ; 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 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 ; 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 ; call prtchr ; print it ld a,10 ; 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.2, 15-May-2019",13,10 .db "Copyright (C) 2019, 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 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