; YKEY21.AZM A function key handler designed to be an overlay ; to a specified program. Each program with which ; it is to be used must be patched by DDT. ; vers 2.1 added Z80DOS create-time modification ; Assemble with Z80MR. R.Gaspari 12-12-87 PROGEND EQU 2D00h ; original end of the program being modified KBA EQU 0D700H ; base addr below which code is loaded (ccp) KKS EQU 280H ; how far below kba code will go NO EQU 0 YES EQU .NOT.NO SMTCLK EQU YES ; using SMT no-slot clock? @S ; (for pop-up clock display) Z80DOS EQU YES ; using Z80DOS? @Z ; (to trap make-file bdos calls) TYPE1 EQU YES ; Type 1 function key (one char over 80h) TYPE2 EQU NO ; Type 2 funct key (2 char: escape + ascii) ORG 0100h ; overwrite first 3 bytes of PBM JP LOADCD ; (but note what it had at addr 0100 first) ORG PROGEND ; point to end of PBM LOADCD: LD HL,0 ; * prevent soft return to ccp EX (SP),HL ; * retn to ccp becomes JP 0 CALL SETUP ; load CCP or BDOS overlays LD A,31H LD (0100H),A ; replace orig contents of 0100h LD A,0EAH LD (0101H),A ; replace orig contents of 0101h LD A,01H LD (0102H),A ; and 0102h JP 0100H ; jump to original program & run ; ************************************ ; PART I - SETUP JUMP VECTORS ; ************************************ M6 EQU -6 ; a constant SETUP: LD HL,(6) ; get bdos vector (DF06) LD (KREM6),HL ; store it for future use LD DE,M6 ; get -6 ADD HL,DE ; HL now has DF00 (source) LD DE,KBA-KKS ; DE now has D300 (destination) LD BC,9 ; move 9 bytes (6 ID bytes + jmp) LDIR LD HL,(1) ; get warm boot vector ED03 (source) LD DE,KBA-KKS+9 ; DE now has D309 (destination) LD BC,12 ; move 12 bytes LDIR LD DE,KBA-KKS+21 ; DE has D315 (destination) LD HL,KKODE+21 ; HL points to code below LD BC,KSIZE ; move bytes LDIR LD HL,KBDOS ; get new bdos vector LD (6),HL ; put it in place LD HL,(1) ; point to actual table (dest) LD DE,KBOOT ; point to replace table (source) EX DE,HL ; LD BC,12 ; move 12 bytes LDIR if z80dos ; @Z1 LD HL,(KBA-KKS+7) LD (Z8ENTRY+1),HL LD HL,Z8NEW LD (KBA-KKS+7),HL endif ; @Z1 RET ; done with SETUP ; ********************************************************* ; PART II - NEW VECTOR CODE (moved to high memory) ; ********************************************************* KKODE: ; start of code to be moved OK EQU (KBA-KKS)-KKODE ; offset KSER EQU $+OK DS 6 ; space for serial number KBDOS EQU $+OK DS 3 ; space for bdos jump BBOOT EQU $+OK DS 3 ; orig warm boot jmp vector BSTAT EQU $+OK DS 3 ; orig console status BCONIN EQU $+OK DS 3 ; orig console input BCONOUT EQU $+OK DS 3 ; orig console output KBOOT EQU $+OK JP KBT ; new warm boot vector KCSTAT EQU $+OK JP KST ; new console status KCONIN EQU $+OK JP KIN ; new console input KCONOUT EQU $+OK JP BCONOUT ; new console output ; *********************************************************** ; REPLACEMENT WARM BOOT ROUTINE KBT EQU $+OK ; remove key code each wm boot CALL KREM ; put the 4 bios vectors back JP 0 ; exit to cpm KREM EQU $+OK LD HL,(KREM6) ; get orig bdos jmp vector LD (6),HL ; put it back LD DE,(1) ; point to ED03 LD HL,BBOOT ; get orig actual data LD BC,12 ; move 12 bytes LDIR XOR A RET ; ********************************************************* ; REPLACEMENT CONSOLE STATUS ROUTINE KST EQU $+OK ; NEW CONSOLE STATUS TEST FOR DATA READY IF SMTCLK ; @C1 CALL CLKPOP ; if pop-up clk in effect, display it ENDIF ; @C1 LD A,(KEYINF) ; now look for more funct key replacements OR A ; zero = no more JP Z,BSTAT ; no more, so go do orig bios cons status LD A,0FFH ; is more, so return with FF RET IF SMTCLK ; @C2 CLKPOP EQU $+OK LD A,(CLKFLG) ; see if pop-up flag is set OR A RET Z ; if no, return to call clkpop LD A,(CLKCTR) ; if yes, get the delay counter INC A ; display clk only every 256 iterations LD (CLKCTR),A ; update the counter RET NZ ; if NZ, go back to call clkpop JP CDSP ; otherwise, display clock CLKFLG EQU $+OK DB 0 ; set 0FFh for continuous pop-up display CLKCTR EQU $+OK DB 0 ; temp storage for 256 state counter ENDIF ; @C2 ; ************************************************************ ; REPLACEMENT CONSOLE CHARACTER INPUT ROUTINE KIN EQU $+OK ; NEW CONSOLE INPUT ROUTINE LD HL,KEYINF ; get the counter of keys waiting LD A,(HL) OR A JR NZ,KEYWAT ; go process the keys waiting KIN2: CALL KST ; no more keys waiting, look for new keys OR A JR Z,KIN2 ; if no new keys, go back and wait CALL BCONIN ; if yes, get the byte using actual bios IF SMTCLK ; @C3 CP HKEY1 ; see if it's HOTKEY1 JP Z,CDSP ; (single pop-up clock request) CP HKEY2 ; see if it's HOTKEY2 JR NZ,KIN4 ; (continuous pop-up clk display) CLKREQ: LD A,(CLKFLG) ; prepare to toggle the pop-up flag CPL ; complement the accumulator LD (CLKFLG),A ; store it back again XOR A ; put 0 in accum before return RET ; retn from conin KIN4: ENDIF ; @C3 IF TYPE1 ; @T1 CP 7FH ; see if it's a "delete" RET C ; if less than 7F, return with it JR NZ,KEYFNI ; if greater than 7F, prep for funct key LD A,(KEYDEL) ; if equal to 7F, replace with a ^H RET ; KEYFNI: LD B,A ; store the function key LD A,(KEYLEN) ; get length of replacement byte string OR A ; see if zero LD A,B ; get the function key back RET Z ; if len=0, don't process function keys KEYFN EQU $+OK CP 0DBH ; now see if its F1 - F15 or if a cursor JR NC,KEY2 ; if no carry, its greater (F1 - F15) SBC A,7FH ; subtract to point to offset in table LD HL,(KEYNMP) ; get address of the cursor replacements JR KEYX ; go to the exchange routine KEY2: SBC A,0DBH ; subtract to get the offset for our table LD HL,(KEYFNP) ; get address of the fn key replacements ENDIF ; @T1 IF TYPE2 ; @T2 CP 7FH ; see if it's a "delete" JR NZ,KEYFNI ; if not, skip the next 2 lines LD A,(KEYDEL) ; if equal to 7F, replace with a ^H RET ; return from KIN conin routine KEYFNI: LD B,A ; store the key input temporarily LD A,(KEYLEN) ; get length of replacement byte string OR A ; see if zero LD A,B ; get the input key back RET Z ; if len=0, don't process function keys KEYFN EQU $+OK LD A,(ESCFLG) ; see if previous char was an escape OR A JR NZ,KEY2 ; if yes, go key2 to process funct key LD A,B ; if not, this is normal key input CP 1BH ; see if this normal key is escape RET NZ ; if not, retn from conin with key LD (ESCFLG),A ; if yes, set the escape flag XOR A ; put zero in accum before return RET ; return from KIN conin routine KEY2: XOR A ; process funct key replacement LD (ESCFLG),A ; but first, put a zero in flag LD A,B ; get the orig input key back CP 05BH ; now see if its F1 - F15 or if a cursor JR NC,KEY3 ; if no carry, its greater (F1 - F15) LD HL,(KEYNMP) ; get address of the cursor replacements JR KEYX ; go to the exchange routine KEY3: SBC A,05BH ; subtract to get the offset for our table LD HL,(KEYFNP) ; get address of the fn key replacements ENDIF ; @T2 KEYX: LD C,A ; enter exch routine with offset in A LD A,(KEYLEN) ; get the length of each replacement str DEC A ; LD (KEYINF),A ; store the number of remaining bytes LD D,A ; the number of times to add A to itself LD A,C ; get the offset back from C KEYX2: ADD A,c ; by adding N times, adjust offset for DEC D ; the number of bytes in KEYLEN JR NZ,KEYX2 ; repeat till D=0 LD E,A ; put the offset in DE, prep for DAD D LD D,0 ; ditto ADD HL,DE ; HL now points to the replacement bytes LD D,(HL) ; get first replacement byte INC HL ; point to next byte LD (KEYPTR),HL ; store pointer to the next byte LD A,(HL) ; get next byte OR A JR NZ,KNXTZ ; if next byte NZ skip, otherwise... LD (KEYINF),A ; notify keyinf that there's no more KNXTZ: LD A,D ; get the replacement key back OR A ; check that it's not a null RET NZ ; if no null, return with it LD (KEYINF),A ; otherwise notify keyinf that no more RET ; return with function key replacement KEYWAT: DEC A ; decrement count of keys waiting LD (HL),A ; store for future use LD HL,(KEYPTR) ; get address of next byte waiting LD A,(HL) ; get the byte there INC HL ; update the pointer to the next byte LD (KEYPTR),HL ; and store it RET ; return with the byte that was waiting KREMC EQU $+OK DW 0 ; temp storage for CMND-F4 counter KEYPTR EQU $+OK DEFW 0 ; temp pointer to next replacement byte KEYINF EQU $+OK DEFB 0 ; temp storage for number of keys waiting KREM6 EQU $+OK DW 0 ; temp storage for orig bdos (DF06) ESCFLG EQU $+OK DB 0 ; temp storage for last key = escape KEYDEL EQU $+OK ; permanent storage for delete key DEFB 08H ; replacement (set delete = ^H) KEYLEN EQU $+OK ; permanent storage for length of DEFB 2 ; each replacement string KEYFNP EQU $+OK dw KFNTBL ; pointer to KFNTBL KEYNMP EQU $+OK dw KNMTBL ; pointer to KNMTBL ; ***************************************************** ; PART III - FUNCTION KEY REPLACEMENT DATA ; ***************************************************** KNMTBL EQU $+OK db 0,0 ; 80h cmnd - db 0,0 ; 81h cmnd . db 0Bh,0 ; 82h up arrow db 0Ah,0Ch ; 83h 3 arrow db 0Ah,08h ; 84h 1 arrow db 0,0 ; 85h 5 arrow (center) db 0Ch,0 ; 86h rt arrow db 08h,0Bh ; 87h 7 arrow db 08h,0 ; 88h lf arrow db 0Bh,0Ch ; 89h 9 arrow db 0Ah,0 ; 8Ah dn arrow db 0,0 ; 8Bh undefined db 0,0 ; 8Ch cntl , db 0,0 ; 8Dh SHFT ENTER db 0,0 ; 8Eh cntl . db 0,0 ; 8Fh cntl - db 0,0 ; 90h cntl 0 db 0,0 ; 91h cntl 1 arrow db 0,0 ; 92h cntl 2 arrow db 0,0 ; 93h cntl 3 arrow db 0,0 ; 94h cntl 4 arrow db 0,0 ; 95h cntl 5 arrow db 0,0 ; 96h cntl 6 arrow db 0,0 ; 97h cntl 7 arrow db 0,0 ; 98h cntl 8 arrow db 0,0 ; 99h cntl 9 arrow KFNTBL EQU $+OK db 17h,0 ; dbh F1 key db 17h,0 ; dch F2 db 0,0 ; ddh F3 db 0,0 ; deh F4 db 0,0 ; dfh F5 db 0,0 ; e0h F6 db 0,0 ; e1h F7 db 0,0 ; e2h F8 db 0,0 ; e3h F9 db 0,0 ; e4h F10 db 0,0 ; e5h F11 db 0,0 ; e6h F12 db 0,0 ; e7h F13 db 0,0 ; e8h F14 db 0,0 ; e9h F15 db 18h,0 ; EAh shft-F1 key db 0,0 ; EBh shft-F2 db 0,0 ; ECh shft-F3 db 0,0 ; EDh shft-F4 db 0,0 ; EEh shft-F5 db 0,0 ; EFh shft-F6 db 0,0 ; F0h shft-F7 db 0,0 ; F1h shft-F8 db 0,0 ; F2h shft-F9 db 0,0 ; F3h shft-F10 db 0,0 ; F4h shft-F11 db 0,0 ; F5h shft-F12 db 0,0 ; F6h shft-F13 db 0,0 ; F7h shft-F14 db 0,0 ; F8h shft-F15 ; ********************************* ; PART IV - Z80DOS TRAP ; ********************************* if z80dos ; @Z2 Z8NEW EQU $+OK LD A,C ; move bdos funct number into A CP 0FH ; see if it's the first open-file call JR Z,Z8GET ; if yes, GET the time stamp CP 16H ; see if this is make-file call JR Z,Z8USE ; if yes, USE the time stamp JR Z8ENTX ; all else, go do real bdos jump Z8GET: PUSH DE ; preserve the fcb for bdos 54 PUSH DE CALL Z8ENTRY ; go to bdos & return POP DE ; get the fcb back again PUSH AF ; preserve open-file bdos status INC A ; see if it's 0ffh (no file found) LD (Z8NFF),A ; store results for use later JR Z,Z8SKP ; if no file found, skip get stamp LD C,54 ; 54 = get stamp CALL 5 ; Z8SKP: POP AF ; get orig open-file status back POP DE ; and orig fcb RET Z8USE: LD A,(Z8NFF) ; see if open-file was successful OR A ; a=0 if unsuccessful JR Z,Z8ENTX ; if zero, skip use-stamp PUSH BC ; save bdos funct number (c=16h) PUSH DE ; save fcb LD C,55 ; 55 = use stamp CALL 5 POP DE POP BC Z8ENTRY EQU $+OK Z8ENTX: JP 0 ; do the JP into real bdos Z8NFF EQU $+OK DB 0 ; flag for no-file-found endif ; @Z2 IF SMTCLK ; @C4 ; ********************************************************* ; PART Va - HARDWARE EQUATES FOR POP-UP CLOCK ; ********************************************************* ROW EQU 0 ; row and column where pop-up clock COL EQU 50 ; display will be seen on crt CA1 EQU 1BH ; cursor addressing sequence CA2 EQU '=' CA3 EQU ROW+20H CA4 EQU COL+20H CV1 EQU 18H ; make cursor visible CV2 EQU 0 ; (2 bytes avail) CI1 EQU 19H ; make cursor invisible CI2 EQU 0 ; (2 bytes avail) CURPTR EQU 0F400H+21EH ; location in your bios where the pointer ; to current cursor location is stored HKEY1 EQU 01H ; hot-key 1 = single display of clock (^A) HKEY2 EQU 02H ; hot-key 2 = continuous clk display (^B) ; (for 2 key "esc-key" sequences, use byte 2) ; **************************************** ; PART Vb - CLOCK READ ROUTINE ; **************************************** ; see NOSLT20.LBR for more info TIME EQU $+OK ; 1. - CLOCK ENABLE routine CALL TIXR ; READ with addr bit A2 high LD HL,TIMREG ; point to bit stream LD A,(HL) ; get first byte TIBYT: LD B,A ; b contains byte LD C,8 ; do this for 8 bits TIBIT: SRL B ; 0->b7...b0->cy TIWR: DI LD A,084H ; select PROM memory OUT 0A1H,A ; map it to 4000h to 8000h JR NC,TIX1 LD A,(04003H) ; WRITE with bit A2 low & bit A0 high JR TIX2 TIX1: LD A,(04002H) ; WRITE with bit A2 low & bit A0 low TIX2: LD A,1 ; select normal RAM memory OUT 0A1H,A ; map it onto 4000h to 8000h EI ; done with TIWR DEC C JR NZ,TIBIT ; do all 8 bits INC HL ; point to next byte LD A,(HL) ; get next byte OR A ; set zero flag JR NZ,TIBYT ; go do next byte ; code enable complete... TIREAD: ; 2. - CLOCK READ routine LD HL,TIBUF ; set up buffer LD E,8 ; do 8 bytes TIRBYT: LD D,8 ; do 8 bits TIRBIT: CALL TIXR SRL A ; shift bit into cy RR (HL) ; shift bit into (hl) DEC D ; dec bit counter JR NZ,TIRBIT ; read next bit INC HL ; point to next buf location DEC E ; dec byte counter JR NZ,TIRBYT ; read next byte LD HL,TIBUF+1 ; point to data loc before RET RET ; and return from TIME TIXR EQU $+OK DI ; subroutine to enable PROM LD A,084H ; select PROM memory OUT 0A1H,A ; map it onto 4000h - 8000h LD A,(04004H) ; READ with bit A2 high LD B,A ; store in B for the return LD A,1 ; select normal RAM memory OUT 0A1H,A ; map it onto 4000h - 8000h LD A,B EI RET ; normal return from TIME TIMREG EQU $+OK DB 0c5h,3ah,0a3h,5ch ; comparison reg DB 0c5h,3ah,0a3h,5ch,0 ; definition TIBUF EQU $+OK ; (example) Tues 10/20/87 5:15pm DB 0,0 ; seconds (0000) DB 0 ; minutes (15h) DB 0 ; hour (17h) DB 0 ; day (03h) DB 0 ; date (20h) DB 0 ; month (10h) DB 0 ; year (87h) ; **************************************** ; PART Vc - POP-UP CLOCK DISPLAY ; **************************************** CDSP EQU $+OK ; clock display code LD HL,(CURPTR) ; get current cursor location PUSH HL ; save it (@1) CALL TIME ; call TIME & retn with pointer LD A,(TIBUF+7) ; point to year LD (TIBUF+4),A ; overwrite year onto weekday LD B,6 ; convert 6 binary bytes to ascii LD DE,CDABUF+23 ; point to end of ascii data buffer CDS2: LD A,(HL) ; get the binary data CALL CDASC ; convert it & store at HL & HL-1 INC HL ; point to next binary data DEC DE ; point to next ascii buffer loc DEC B ; countdown 6 binary bytes JR NZ,CDS2 ; LD HL,CDABUF ; point HL to start of cdabuf LD B,25 ; count 25 bytes to send out CDS4: LD C,(HL) ; get the byte to send to console CALL BCONOUT ; N* Adv bios conout INC HL ; point to next loc DEC B ; continue for all 25 bytes JR NZ,CDS4 POP HL ; get orig cursor location LD (CURPTR),HL ; put cursor back in bios LD C,CV1 ; make the cursor visible again CALL BCONOUT ; (use bios conout) LD C,CV2 ; byte 2 of cursor visible CALL BCONOUT ; XOR A ; load 0 into A before ret RET ; normal retn from CDSP CDABUF EQU $+OK DB CI1,CI2 ; make cursor display invisible DB CA1,CA2,CA3,CA4 ; move to location row & col DB ' ' DB 0,0,'/' ; month ascii DB 0,0,'/' ; day ascii DB 0,0,' ' ; year ascii DB 0,0,':' ; hour ascii DB 0,0,':' ; min ascii DB 0,0,' ' ; sec ascii CDASC EQU $+OK LD C,A ; binary to ascii conversion AND 0FH ; mask lower nibble CALL CDAS2 ; LD A,C ; get byte back AND 0F0H ; mask upper nibble RLCA ; shift right 4 times RLCA ; (or left 4 times) RLCA ; RLCA ; CDAS2 EQU $+OK OR 30H ; convert to ascii LD (DE),A ; store it DEC DE ; move to prev loc for upper nibble RET ; retn CDAS2 & retn CDASC ENDIF ; @C4 KREF1 EQU $ ; location of end of code (local) KREF2 EQU $+OK ; location of end (after it's moved) KSIZE EQU $-KKODE ; the size of code to be moved IF (KSIZE).GT.(KKS) * ERROR - allow more size for KKS ENDIF end