; ;======================================================================= ; Zilog PIO Monitor & Hardware Testing Application ;======================================================================= ; iodef .equ $B8 ; Default base I/O port address ; ; ; ; Port address offsets from base address iodata .equ 0 ; Channel A Data iodatb .equ 1 ; Channel B Data ioctla .equ 2 ; Channel A Control ioctlb .equ 3 ; Channel B Control ; intveca .equ 0 ; Channel A interrupt vector intvecb .equ 1 ; Channel B interrupt vector ; iocmd .equ $E3 ; PS/2 controller command port address iodat .equ $E2 ; PS/2 controller data port address ; cpumhz .equ 8 ; for time delay calculations (not critical) ; ; General operational equates (should not requre adjustment) ; stksiz .equ $40 ; Working stack size buflen .equ $80 ; Command buffer length ; ltimout .equ 0 ; 256*10ms = 2.56s stimout .equ 10 ; 10*10ms = 100ms ; 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 ; bel .equ 7 ; ASCII bell bs .equ 8 ; ASCII backspace lf .equ 10 ; ASCII linefeed cr .equ 13 ; ASCII carriage return ; ;======================================================================= ; .org $100 ; standard CP/M executable ; ; ; setup stack (save old value) ld (stksav),sp ; save stack ld sp,stack ; set new stack ; call nl ld hl,str_banner ; banner call pstr ; getport1: ld hl,str_port1 call pstr ld a,(iobase) call prthexbyte ld hl,str_port2 call pstr call rdln ld ix,cmdbuf call skipws or a jr z,getport2 call ishex jr nz,getport1 call gethex ; get port value jp c,getport1 ; handle overflow ld (iobase),a ; save value ; getport2: call init ; call main ; do the real work ; exit: call deinit ; call nl2 ld hl,str_exit call pstr ; ; clean up and return to command processor call nl ; formatting ld sp,(stksav) ; restore stack jp restart ; return to CP/M via restart ; ;======================================================================= ; Initialize ;======================================================================= ; init: ; Install interrupt handler in upper mem ld hl,reladr ld de,$A000 ld bc,hsiz ldir ; ; Install interrupt vectors (RomWBW specific!!!) ld hl,inta ; pointer to my interrupt handler ld b,bf_sysint ld c,bf_sysintset ; set new vector ld e,intveca ; vector idx di rst 08 ; do it ld (orgveca),hl ; save the original vector ei ; interrupts back on ld hl,intb ; pointer to my interrupt handler ld b,bf_sysint ld c,bf_sysintset ; set new vector ld e,intvecb ; vector idx di rst 08 ; do it ld (orgvecb),hl ; save the original vector ei ; interrupts back on ; ; Load the interrupt vectors ld a,(iobase) add a,ioctla ld c,a ld a,intveca * 2 out (c),a ld a,(iobase) add a,ioctlb ld c,a ld a,intvecb * 2 out (c),a ; ; Set the interrupt control words ld a,(iobase) add a,ioctla ld c,a ld a,%10000111 out (c),a ; int enab, no mask follows ;ld a,%11111111 ;out (ioctla),a ; no ints in control mode ld a,(iobase) add a,ioctlb ld c,a ld a,%10000111 out (c),a ; int enab, no mask follows ;ld a,%11111111 ;out (ioctlb),a ; no ints in control mode ; ret ; deinit: call reset ; ld a,(iobase) add a,ioctla ld c,a ld a,%00010111 ; clear interrupt ctl word out (c),a ld a,%11111111 ; clear mask out (c),a ld a,%00000000 ; clear interrupt vector out (c),a ; ld a,(iobase) add a,ioctlb ld c,a ld a,%00010111 ; clear interrupt ctl word out (c),a ld a,%11111111 ; clear mask out (c),a ld a,%00000000 ; clear interrupt vector out (c),a ; ; Deinstall interrupt vectors ld hl,(orgveca) ; original vector ld b,bf_sysint ld c,bf_sysintset ; set new vector ld e,intveca ; vector idx di rst 08 ; do it ei ; interrupts back on ld hl,(orgvecb) ; original vector ld b,bf_sysint ld c,bf_sysintset ; set new vector ld e,intvecb ; vector idx di rst 08 ; do it ei ; interrupts back on ret ; ;======================================================================= ; Main Program ;======================================================================= ; main: ; Prompt call nl2 ld hl,str_pre call pstr ld a,(iobase) call prthexbyte ; ld hl,str_int1 call pstr ld hl,(intcnta) call prtdec ld hl,str_int2 call pstr ld hl,(intcntb) call prtdec ; ld hl,str_pre2 call pstr ; ; Read command line call rdln ld ix,cmdbuf ; main1: ;;;; Upper case the entire command line ;;;ld a,(ix) ;;;or a ;;;jr z,main2 ;;;call upcase ;;;ld (ix),a ;;;inc ix ;;;jr main1 ; main2: ld ix,cmdbuf call skipws or a ; check for eol call nz,runcmd ; run command if not eol jr main ; loop ; ; Run the command line pointed to by IX ; runcmd: ld ix,cmdbuf ; point to cmd line call skipws or a ; check for eol ret z ; return if nothing there ; ld a,(ix) ; get character ; ; Dispatch cp '?' ; Help jp z,help cp 'H' ; Help jp z,help cp 'X' ; Exit jp z,exit cp 'P' ; PIO Base Port jp z,setport cp 'Z' ; Reset Chip jp z,reschip cp 'W' ; Watch pins jp z,watch cp 'I' ; Input pins on channel jp z,input cp 'O' ; Output pins on channel jp z,output cp 'S' ; Send strobed byte to channel jp z,send cp 'R' ; Read strobed byte from channel jp z,receive cp 'T' ; Test jp z,test jp err_invcmd ; Invalid command ; help: ld hl,str_usage call pstr ret ; setport: call findws ; skip command call skipws ; skip white space call ishex ; do we have a number jp nz,err_invcmd ; handle invalid command call gethex jp c,err_invcmd ; handle overflow error push af call deinit pop af ld (iobase),a ; set new port value call init ret ; and done ; reschip: ld hl,str_reschip1 call pstr call reset ld hl,str_reschip2 call pstr ret ; watch: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; ld hl,str_watch1 call pstr ld a,(channel) add a,'A' call cout ld hl,str_watch2 call pstr call nl2 ; formatting call ctlport ; set c to ctl port of channel ld a,%11001111 ; bit control mode out (c),a ; do it ld a,%11111111 ; set all pins to input out (c),a ; do it call dataport ; set c to data port ld a,0 ld b,0 jr watch2 watch1: call keychk ; key pressed? ret nz ; return if so in a,(c) ; read data port cp b ; same as before jr z,watch1 ; loop watch2: ld b,a ; save in B ld hl,str_watchtag call pstr ld a,b ; restore value read call prthexbyte ; print new value jr watch1 ; input: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; ld hl,str_input1 call pstr ld a,(channel) add a,'A' call cout ld hl,str_input2 call pstr call ctlport ; set c to ctl port of channel ld a,%11001111 ; bit control mode out (c),a ; do it ld a,%11111111 ; set all pins to input out (c),a ; do it call dataport ; set c to data port in a,(c) call prthexbyte ret ; output: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; call findws ; skip command call skipws ; skip white space call ishex ; do we have a number jp nz,err_invcmd ; handle invalid command call gethex jp c,err_invcmd ; handle overflow error push af ; ld hl,str_output1 call pstr ld a,(channel) add a,'A' call cout ld hl,str_output2 call pstr call ctlport ; set c to ctl port of channel ld a,%11001111 ; bit control mode out (c),a ; do it ld a,%00000000 ; set all pins to output out (c),a ; do it call dataport ; set c to data port pop af out (c),a call prthexbyte ret ; send: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; call findws ; skip command call skipws ; skip white space call ishex ; do we have a number jp nz,err_invcmd ; handle invalid command call gethex jp c,err_invcmd ; handle overflow error push af ; ld hl,str_send1 call pstr ld a,(channel) add a,'A' call cout ld hl,str_send2 call pstr call ctlport ; set c to ctl port of channel ld a,%00001111 ; strobed output mode out (c),a ; do it call dataport ; set c to data port pop af out (c),a call prthexbyte ret ; receive: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; ld hl,str_receive1 call pstr ld a,(channel) add a,'A' call cout ld hl,str_receive2 call pstr call ctlport ; set c to ctl port of channel ld a,%01001111 ; strobed input mode out (c),a ; do it call dataport ; set c to data port in a,(c) call prthexbyte ret ; test: inc ix ; skip command byte ld a,(ix) ; get character ; ; Dispatch cp 'R' jp z,test_rdbk ; Readback Test cp 'L' jp z,test_lpbk ; Loopback Test cp 'S' jp z,test_stlp ; Strobed Loopback Test jp err_invcmd ret ; test_rdbk: inc ix ; skip command byte call getchan ; get channel jp nz,err_invcmd ; handle invalid channel ; ld hl,str_rdbk call pstr ld a,(channel) add a,'A' call cout call nl ; formatting call ctlport ; set c to ctl port of channel ld a,%00001111 ; mode 0 (output mode) out (c),a ; do it call dataport ; set c to data port ; ld hl,vallist ld b,vallen test_rdbk0: push hl push bc ld a,(hl) call test_rdbk1 pop bc pop hl jp nz,err_fail inc hl djnz test_rdbk0 ret ; test_rdbk1: ld b,a call nl ld a,b call prthexbyte out (c),a ld hl,str_arrow call pstr in a,(c) call prthexbyte cp b ret ; test_lpbk: ld hl,str_lpbkAB call pstr ld a,(iobase) ; Test from A add a,ioctla ld d,a ld a,(iobase) ; ... to B add a,ioctlb ld e,a push de call reset call test_lpbk1 ; avoid output on both channels pop de jp nz,err_fail ld hl,str_lpbkBA call pstr ld a,d ; switch direction ld d,e ld e,a call reset ; avoid output on both channels call test_lpbk1 jp nz,err_fail ret ; test_lpbk1: ; Setup output channel ld c,d ld a,%11001111 ; bit control mode out (c),a ; do it ld a,%00000000 ; set all pins to output out (c),a ; do it ; ; Setup input channel ld c,e ld a,%11001111 ; bit control mode out (c),a ; do it ld a,%11111111 ; set all pins to input out (c),a ; do it ; ; Loop through test values dec d ; point to data port (output) dec d dec e ; point to data port (input) dec e ld hl,vallist ld b,vallen ; test_lpbk2: call test_lpbk3 ret nz inc hl djnz test_lpbk2 ret ; test_lpbk3: call nl ld c,d ld a,(hl) call prthexbyte out (c),a push hl ld hl,str_arrow call pstr pop hl ld c,e in a,(c) call prthexbyte cp (hl) ret ; test_stlp: ld hl,str_stlpAB call pstr ld a,(iobase) ; Test from A add a,ioctla ld d,a ld a,(iobase) ; ... to B add a,ioctlb ld e,a push de call reset call test_stlp1 ; avoid output on both channels pop de jp nz,err_fail ld hl,str_stlpBA call pstr ld a,d ; switch direction ld d,e ld e,a call reset ; avoid output on both channels call test_stlp1 jp nz,err_fail ret ; test_stlp1: ; Setup output channel ld c,d ld a,%00001111 ; strobed output mode out (c),a ; do it ; ; Setup input channel ld c,e ld a,%01001111 ; strobed input mode out (c),a ; do it ; ; Loop through test values dec d ; point to data port (output) dec d dec e ; point to data port (input) dec e ld hl,vallist ld b,vallen ; test_stlp2: call test_stlp3 ret nz inc hl djnz test_stlp2 ret ; test_stlp3: call nl ld c,d ld a,(hl) call prthexbyte out (c),a push hl ld hl,str_arrow call pstr pop hl ld c,e in a,(c) call prthexbyte cp (hl) ret ; ; ; getchan: ld a,(ix) ; get byte sub 'A' ; convert to binary cp 2 ; check for max value ret nc ; return with NZ if too high ld (channel),a ; save new value cp a ; set ZF ret ; done ; ; ; reset: ld a,(iobase) add a,ioctla ld c,a call reset1 ld a,(iobase) add a,ioctlb ld c,a jr reset1 ; reset1: ld a,%01001111 ; set mode 1 (input) out (c),a ;ld a,%00010111 ; clear interrupt ctl word ;out (c),a ;ld a,%11111111 ; clear mask ;out (c),a ;ld a,%00000000 ; clear interrupt vector ;out (c),a ret ; ; ; ctlport: ld a,(iobase) ; base port add a,ioctla ; offset to control ports ld c,a ; put in c ld a,(channel) ; get channel add a,c ; combine ld c,a ret ; ; ; dataport: ld a,(iobase) ; base port add a,iodata ; offset to data ports ld c,a ; put in c ld a,(channel) ; get channel add a,c ; combine ld c,a ret ; ; Error Handlers ; err_abort: ld hl,str_err_abort jr err_ret err_invcmd: ld hl,str_err_invcmd jr err_ret err_fail: ld hl,str_err_fail jr err_ret ; err_ret: push hl ld hl,str_err_prefix call pstr pop hl jp pstr ; str_err_prefix .db bel,"\r\n\r\n*** ",0 str_err_abort .db "User Aborted",0 str_err_invcmd .db "Invalid command, press '?' for help",0 str_err_fail .db "Test failed!",0 ; ;======================================================================= ; Utility functions ;======================================================================= ; ; Print string at HL on console, null terminated ; pstr: push af push hl pstr1: ld a,(hl) ; get next character or a ; set flags inc hl ; bump pointer regardless jr z,pstr2 ; done if null call cout ; display character jr pstr1 ; loop till done pstr2: pop hl pop af ret ; ; Print volume label string at HL, '$' terminated, 16 chars max ; pvol: ld b,16 ; init max char downcounter pvol1: ld a,(hl) ; get next character cp '$' ; set flags inc hl ; bump pointer regardless ret z ; done if null call cout ; display character djnz pvol1 ; loop till done ret ; hit max of 16 chars ; ; Start a newline on console (cr/lf) ; nl2: call nl ; double newline nl: ld a,cr ; cr call cout ; send it ld a,lf ; lf jp cout ; send it and return ; ; Print a dot on console ; pdot: push af ld a,'.' call cout pop af ret ; ; ; keychk: call cst or a ret z call cin or $FF ret ; ; Read a string on the console to cmdbuf ; ; Input is zero terminated ; rdln: ld de,cmdbuf ; init buffer address ptr rdln_nxt: call cin ; get a character cp bs ; backspace? jr z,rdln_bs ; handle it if so cp cr ; return? jr z,rdln_cr ; handle it if so ; ; check for non-printing characters cp ' ' ; first printable is space char jr c,rdln_bel ; too low, beep and loop cp '~'+1 ; last printable char jr nc,rdln_bel ; too high, beep and loop ; ; need to check for buffer overflow here!!! ld hl,cmdbuf+buflen ; max cmd length or a ; clear carry sbc hl,de ; test for max jr z,rdln_bel ; at max, beep and loop ; ; good to go, echo and store character call upcase call cout ; echo character input ld (de),a ; save in buffer inc de ; inc buffer ptr jr rdln_nxt ; loop till done ; rdln_bs: ld hl,cmdbuf ; start of buffer or a ; clear carry sbc hl,de ; subtract from cur buf ptr jr z,rdln_bel ; at buf start, just beep ld hl,str_bs ; backspace sequence call pstr ; send it dec de ; backup buffer pointer jr rdln_nxt ; and loop ; rdln_bel: ld a,bel ; Bell characters call cout ; send it jr rdln_nxt ; and loop ; rdln_cr: xor a ; null to A ld (de),a ; store terminator ret ; and return ; ; Find next whitespace character at buffer adr in DE, returns with first ; whitespace character in A. ; findws: ld a,(ix) ; get next char or a ; check for eol ret z ; done if so cp ' ' ; blank? ret z ; nope, done inc ix ; bump buffer pointer jr findws ; and loop ; ; Skip whitespace at buffer adr in DE, returns with first ; non-whitespace character in A. ; skipws: ld a,(ix) ; get next char or a ; check for eol ret z ; done if so cp ' ' ; blank? ret nz ; nope, done inc ix ; bump buffer pointer jr skipws ; and loop ; ; Uppercase character in A ; upcase: cp 'a' ; below 'a'? ret c ; if so, nothing to do cp 'z'+1 ; above 'z'? ret nc ; if so, nothing to do and ~$20 ; convert character to lower ret ; done ; ; Get numeric chars at IX and convert to number returned in A ; Carry flag set on overflow ; getnum: ld c,0 ; C is working register getnum1: ld a,(ix) ; get the active char cp '0' ; compare to ascii '0' jr c,getnum2 ; abort if below cp '9' + 1 ; compare to ascii '9' jr nc,getnum2 ; abort if above ; ; valid digit, add new digit to C ld a,c ; get working value to A rlca ; multiply by 10 ret c ; overflow, return with carry set rlca ; ... ret c ; overflow, return with carry set add a,c ; ... ret c ; overflow, return with carry set rlca ; ... ret c ; overflow, return with carry set ld c,a ; back to C ld a,(ix) ; get new digit sub '0' ; make binary add a,c ; add in working value ret c ; overflow, return with carry set ld c,a ; back to C ; inc ix ; bump to next char jr getnum1 ; loop ; getnum2: ; return result ld a,c ; return result in A or a ; with flags set, CF is cleared ret ; ; Get hex chars at IX and convert to binary number returned in A ; Carry flag set on overflow ; gethex: ld c,0 ; C is working register gethex1: ld a,(ix) ; get the active char call ishex ; is it a hex char? jr nz,gethex9 ; abort if not ; ; valid digit, add new digit to C ld a,c ; get working value to A rlca ; multiply by 16 ret c ; overflow, return with carry set rlca ; ... ret c ; overflow, return with carry set rlca ; ... ret c ; overflow, return with carry set rlca ; ... ret c ; overflow, return with carry set ld c,a ; back to C ld a,(ix) ; get new hex digit call isnum ; regular number? jr z,gethex2 ; if so, handle it, else hex char sub 'A'-10 ; convert to binary jr gethex3 ; and continue gethex2: sub '0' ; convert to binary gethex3: add a,c ; add in working value ret c ; overflow, return with carry set ld c,a ; back to C ; inc ix ; bump to next char jr gethex1 ; loop ; gethex9: ; return result ld a,c ; return result in A or a ; with flags set, CF is cleared ret ; ; Is character in A numberic? NZ if not ; isnum: cp '0' ; compare to ascii '0' jr c,isnum1 ; abort if below cp '9' + 1 ; compare to ascii '9' jr nc,isnum1 ; abort if above cp a ; set Z ret isnum1: cp '0' ; set NZ w/o changing value ret ; and done ; ; Is character in A hex? NZ if not ; ishex: call isnum ; is it a numeric? ret z ; if so, all done cp 'A' ; first hex char jr c,ishex1 ; abort if below cp 'F' + 1 ; last hex char jr nc,ishex1 ; abort if above cp a ; set Z ret ; done ishex1: cp '0' ; set NZ w/o changing value ret ; done ; ; Delay 16us (cpu speed compensated) incuding call/ret invocation ; Register A and flags destroyed ; No compensation for z180 memory wait states ; There is an overhead of 3ts per invocation ; Impact of overhead diminishes as cpu speed increases ; ; cpu scaler (cpuscl) = (cpuhmz - 2) for 16us + 3ts delay ; note: cpuscl must be >= 1! ; ; example: 8mhz cpu (delay goal is 16us) ; loop = ((6 * 16) - 5) = 91ts ; total cost = (91 + 40) = 131ts ; actual delay = (131 / 8) = 16.375us ; ; --- total cost = (loop cost + 40) ts -----------------+ delay: ; 17ts (from invoking call) | ld a,(cpuscl) ; 13ts | ; | delay1: ; | ; --- loop = ((cpuscl * 16) - 5) ts ------------+ | dec a ; 4ts | | jr nz,delay1 ; 12ts (nz) / 7ts (z) | | ; ----------------------------------------------+ | ; | ret ; 10ts (return) | ;-------------------------------------------------------+ ; ; Delay 16us * DE (cpu speed compensated) ; Register DE, A, and flags destroyed ; No compensation for z180 memory wait states ; There is a 27ts overhead for call/ret per invocation ; Impact of overhead diminishes as DE and/or cpu speed increases ; ; cpu scaler (cpuscl) = (cpuhmz - 2) for 16us outer loop cost ; note: cpuscl must be > 0! ; ; Example: 8MHz cpu, DE=6250 (delay goal is .1 sec or 100,000us) ; inner loop = ((16 * 6) - 5) = 91ts ; outer loop = ((91 + 37) * 6250) = 800,000ts ; actual delay = ((800,000 + 27) / 8) = 100,003us ; ; --- total cost = (outer loop + 27) ts ------------------------+ vdelay: ; 17ts (from invoking call) | ; | ; --- outer loop = ((inner loop + 37) * de) ts ---------+ | ld a,(cpuscl) ; 13ts | | ; | | vdelay1: ; | | ; --- inner loop = ((cpuscl * 16) - 5) ts ------+ | | dec a ; 4ts | | | jr nz,vdelay1 ; 12ts (nz) / 7ts (z) | | | ; ----------------------------------------------+ | | ; | | dec de ; 6ts | | ld a,d ; 4ts | | or e ; 4ts | | jp nz,vdelay ; 10ts | | ;-------------------------------------------------------+ | ; | ret ; 10ts (final return) | ;---------------------------------------------------------------+ ; ; Delay about 0.5 seconds ; 500000us / 16us = 31250 ; ldelay: push af push de ld de,31250 call vdelay pop de pop af ret ; #if (cpumhz < 3) cpuscl .db 1 ; cpu scaler must be > 0 #else cpuscl .db cpumhz - 2 ; otherwise 2 less than phi mhz #endif ; ; Print value of a in decimal with leading zero suppression ; prtdecb: push hl push af ld l,a ld h,0 call prtdec pop af pop hl ret ; ; Print value of HL in decimal with leading zero suppression ; prtdec: push bc push de push hl 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 call prtdec1 pop hl pop de pop bc ret prtdec1: ld a,'0' - 1 prtdec2: inc a add hl,bc jr c,prtdec2 sbc hl,bc cp e jr z,prtdec3 ld e,0 call cout prtdec3: ret ; ; Short delay functions. No clock speed compensation, so they ; will run longer on slower systems. The number indicates the ; number of call/ret invocations. A single call/ret is ; 27 t-states on a z80, 25 t-states on a z180. ; ; ; z80 z180 ; ; ---- ---- dly64: call dly32 ; 1728 1600 dly32: call dly16 ; 864 800 dly16: call dly8 ; 432 400 dly8: call dly4 ; 216 200 dly4: call dly2 ; 108 100 dly2: call dly1 ; 54 50 dly1: ret ; 27 25 ; ; Add hl,a ; ; A register is destroyed! ; addhla: add a,l ld l,a ret nc inc h ret ; ; ; prtdot: push af ld a,'.' call cout pop af ret ; ; Print the hex byte value in A ; prthexbyte: push af push de call hexascii ld a,d call cout ld a,e call cout pop de pop af ret ; ; Print the hex word value in BC ; prthexword: push af ld a,b call prthexbyte ld a,c call prthexbyte 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 call hexconv ld e,a ld a,d rlca rlca rlca rlca call hexconv ld d,a ret ; ; Convert low nibble of A to ASCII hex ; hexconv: and 0Fh ; low nibble only add a,90h daa adc a,40h daa ret ; ; Output character from A ; cout: ; Save all incoming registers push af push bc push de push hl ; ; Output character to console via BDOS ld e,a ; output char to E ld c,6 ; BDOS direct console I/O call bdos ; output character ; ; Restore all registers pop hl pop de pop bc pop af ret ; ; Input character to A ; cin: ; Save incoming registers (AF is output) push bc push de push hl ; ; Input character from console via BDOS cin1: ld e,$FF ; input request ld c,6 ; BDOS direct console I/O call bdos ; input character to A or a ; test for zero (no input) jr z,cin1 ; loop till we have a char ; ; Restore registers (AF is output) pop hl pop de pop bc ret ; ; Return input status in A (0 = no char, != 0 char waiting) ; cst: ; Save incoming registers (AF is output) push bc push de push hl ; ; Get console input status via BDOS ld e,$FE ; status ld c,6 ; BDOS direct console I/O call bdos ; input status to A ; ; Restore registers (AF is output) pop hl pop de pop bc ret ; ; ; ;======================================================================= ; Constants ;======================================================================= ; str_banner .db "Zilog PIO Monitor v0.1, 14-Mar-2022\r\n" .db "Press ? for help$" str_bs .db bs,' ',bs,0 str_port1 .db "\r\n\r\nEnter PIO port in hex [",0 str_port2 .db "]:",0 str_pre .db "Zilog PIO @ Port 0x",0 str_pre2 .db "\r\n\r\n>",0 str_exit .db "Done, Thank you for using Zilog PIO Monitor!",0 str_reschip1 .db "\r\n\r\nFull Reset of PIO Chip... ",0 str_reschip2 .db "Done",0 str_int1 .db "\r\nChannel A Interrupts=",0 str_int2 .db ", Channel B Interrupts=",0 str_watch1 .db "\r\n\r\nWatching Channel ",0 str_watch2 .db ", press any to end...",0 str_watchtag .db "\rPort Value=0x",0 str_input1 .db "\r\n\r\nValue of Pins on Channel ",0 str_input2 .db " = 0x",0 str_output1 .db "\r\n\r\nSetting Value of Pins on Channel ",0 str_output2 .db " = 0x",0 str_receive1 .db "\r\n\r\nReceived from Channel ",0 str_receive2 .db " = 0x",0 str_send1 .db "\r\n\r\nSent to Channel ",0 str_send2 .db " = 0x",0 str_rdbk .db "\r\n\r\nReadback Test on Channel ",0 str_arrow .db " --> ",0 str_lpbkAB .db "\r\n\r\nLoopback Test in Bit Control Mode A->B...\r\n",0 str_lpbkBA .db "\r\n\r\nLoopback Test in Bit Control Mode B->A...\r\n",0 str_stlpAB .db "\r\n\r\nLoopback Test in Strobed Mode A->B...\r\n",0 str_stlpBA .db "\r\n\r\nLoopback Test in Strobed Mode B->A...\r\n",0 str_usage .db "\r\n" .db "\r\n P n - Set Current PIO Base Port" .db "\r\n Z - Reset PIO (both channels)" .db "\r\n Wc - Watch Channel c Pin Values (bit control)" .db "\r\n Oc n - Output Channel c Pin Values (bit control)" .db "\r\n Ic - Input Channel c Pin Values (bit control)" .db "\r\n Sc n - Send Value to Channel c (strobed)" .db "\r\n Rc - Receive Value from Channel c (strobed)" .db "\r\n TRc - Test Readback on Channel c (bit control)" .db "\r\n TL - Test Loopback (bit control)" .db "\r\n TS - Test Loopback (strobed)" .db "\r\n H,? - Help" .db "\r\n X - Exit" .db "\r\n " .db "\r\n Loopback tests require hardware loopback (see readme.txt)" .db 0 ; ;======================================================================= ; Working data ;======================================================================= ; stksav .dw 0 ; stack pointer saved at start .fill stksiz,0 ; stack stack .equ $ ; stack top ; iobase .db iodef ; current I/O base address channel .db 0 ; current channel wrkval .db 0 ; cmdbuf .fill buflen+1,0 ; vallist: .db $00,$FF,$AA,$55,$A5,$5A,$FF,$00 vallen .equ $ - vallist ; orgveca .dw 0 ; saved int vector, channel A orgvecb .dw 0 ; saved int vector, channel B ; ;=============================================================================== ; Interrupt Handler ;=============================================================================== ; reladr .equ $ ; relocation start adr ; .org $A000 ; code will run here ; inta: ; ld hl,(intcnta) inc hl ld (intcnta),hl ; or $ff ; signal int handled ret ; intb: ; ld hl,(intcntb) inc hl ld (intcntb),hl ; or $ff ; signal int handled ret ; intcnta .dw 0 intcntb .dw 0 ; hsiz .equ $ - $A000 ; size of handler to relocate ; .org reladr + hsiz ; .end