title 'Executive Processor For CP/M' ; ;---------------------------------------------------------------- ; This module contains the Console I/O Drivers for CP/M as well ; as printer and AUX drivers, initialization code and the ; I/O pocessor code. ; ; These are meant to be run in conjunction with the EXEC ; module. ; ; ; Written By Richard Holmes 04-02-86 ; Last Update By Richard Holmes 11-12-87 ;---------------------------------------------------------------- ; public sys$ini public con$ini,con$inp,con$out,con$ost,con$ist,con$cmd public aux$ini,aux$inp,aux$out,aux$ost,aux$ist,aux$cmd public prn$ini,prn$out,prn$ost public lcd$ini,lcd$out,lcd$ost,lcd$cmd ; public set$led,clr$led,tog$led,zro$led public set$bel,clr$bel public clk$rd,clk$wr public ms$delay ; public clrwdt public hrs, min, sec ; maclib z80 maclib iocmd ; ; CP/M equates ; bdos equ 5 ;; Operating system entry dirio equ 6 numatt equ 6 ; 6 attributes cr equ 0dh ; Simple video definitions lf equ 0ah ; clrwdt: set$led: clr$led: tog$led: zro$led: set$bel: clr$bel: clk$rd: clk$wr: ret sys$ini: xra a sta hrs sta min sta sec ret ; lcd$ini: lcd$out: lcd$ost: lcd$cmd: xra a ora a ret ; ms$delay: mov a,e ora d rz ; push d ; save it delay2: call clr$wdt call delay3 dcx d ; one less millisecond less overhead mov a,d ora e jrnz delay2 ; keep on till DE = 0 pop d ; restore users initial value ret ; back to user ; ; Delay 1 millisecond less the overhead involved in the above code. ; ; This routine must delay 3957 t-states ; delay3: push b ; 11 mvi b,224 ; 7 delay4: ; This loop does (4 + 13) * 230 - 5 = 3905 t nop ; 4 nop djnz delay4 ; 13 ; Fudge 14 machine cycles lxi b,0 ; 10 nop ; 4 pop b ; 10 ret ; ;---------------------------------------------------------------- ; This is the console command processor. The channel routines call ; it with a function number in C and these are then performed. ;---------------------------------------------------------------- ; con$cmd: push h push b push d mvi b,0 ; Top offset = 0 lxi h,con$cmd$tbl ; -> table of routine addresses dad b dad b ; Add twice for 2 byte offsets mov e,m ; Low address inx h ; -> High address mov d,m xchg ; HL -> routine pop d ; Restore DE, it may be a parameter pop b ; BC could be someones counter xthl ; top of stack = routine, HL restored also. ret ; con$cmd$tbl: dw 0 dw clear dw cleop dw cleol dw cursor dw setatt aux$cmd: xra a ora a ret ; ;================================================================ ; ; ---- Console Output ---- ; ; On Entry ; A = character to send ;================================================================ ; con$out: push h push b push d mov e,a ; Load the ascii character mvi c,dirio ; Direct console output call bdos pop d ; Restore output pop b pop h ora a ret ; ;================================================================ ; ; Get a console character into A ; ;================================================================ ; con$inp: push h push b push d cie$wait: call con$ist jrz cie$wait ; mvi e,0fdh ; Wait and fetch mvi c,dirio call bdos pop d pop b pop h ret ; ;================================================================ ; ; Get the console status. ; ; On Exit ; A = 0 if not ready, ; 1 if got a character ;================================================================ ; con$ist: push h push b push d mvi e,0feh ; Status only code mvi c,dirio ; Direct console I/O call bdos pop d pop b pop h ora a ret ; con$ini: con$ost: xra a ora a ret ; ;---------------------------------------------------------------- ; Read a character into A and return it to the user. Wait for it ; for ever if needed. Not a smart routine at all. ; ; On Entry ; ; On Exit ; A = character read from AUX port ; All other registers preserved ;---------------------------------------------------------------- ; aux$inp: push h push b push d ; mvi c,3 ; AUX Input code call bdos ; pop d pop b pop h ret ; ;---------------------------------------------------------------- ; Write the character in A to the AUX port. ; ; On Entry ; A = character to write ; ; On Exit ; All other registers preserved ;---------------------------------------------------------------- ; aux$out: push psw push h push b push d ; mvi c,4 ; Aux out code mov e,a ; Load data call bdos ; pop d pop b pop h pop psw ret ; ;---------------------------------------------------------------- ; Return the input status. ; ; On Exit ; A = FF if a character ready ; A = 00 if no character ready ;---------------------------------------------------------------- ; aux$ist: push h push b push d ; mvi c,07 call bdos ; pop d pop b pop h ora a ; Set the zero flags ret ; ;---------------------------------------------------------------- ; Return the output status of the console port. ; ; On Exit ; A = FF if port ready for character output ; A = 00 for device busy ;---------------------------------------------------------------- ; aux$ost: push h push b push d ; mvi c,8 call bdos ; pop d pop b pop h ret ; aux$ini: xra a ora a ret ; ;---------------------------------------------------------------- ; Printer Drivers. ;---------------------------------------------------------------- ; prn$out: push h push b push d ; mov e,a ; Load character mvi c,5 ; Printer output call bdos ; pop d pop b pop h ret ; prn$ost: prn$ini: xra a ora a ret ; ; Initializations. ; ini$vid: xra a sta curatt ; Select attribute 0 sta curvid ret ; ;================================================================ ; Terminal Function Codes ;================================================================ ; ; General purpose function codes ; xyfn: db 02,01bh,'=',00,00 ; xy addressing ; rowcol: db 00 ; 00 means row first xoff: db 32 ; X offset yoff: db 32 ; Y offset cdly: db 01 ; Cursor positioning delay qty ; cpfn db 02,01bh,'*',00,00 ; erase whole page clfn db 02,01bh,'T',00,00 ; erase to end of line epfn db 02,01bh,'Y',00,00 ; erase to end of page ecfn db 03,01bh,'.','1',0 ; enable cursor function dcfn db 03,01bh,'.','0',0 ; disable cursor function ; attset: db 2,01bh,029h,00,00 ; Start half intensity db 2,01bh,05eh,00,00 ; Start blinking db 2,01bh,06ah,00,00 ; Start reverse video db 2,01bh,06ch,00,00 ; Start underline db 4,01bh,029h,01bh,05eh ; Start half-blinking db 4,01bh,06ah,01bh,05eh ; Start reverse-blinking db 0,00,00,00,00 ; Start underline ; attclr: db 02,01bh,028h,00,00 ; End half intensity db 02,01bh,071h,00,00 ; End blinking db 02,01bh,06bh,00,00 ; End reverse video db 02,01bh,06dh,00,00 ; End underline db 4,01bh,071h,01bh,028h ; End half-blinking db 4,01bh,071h,01bh,06bh ; End reverse-blinking db 0,00,00,00,00 ; end ; ; ;----------------- ; Clear the screen ;----------------- ; clear: push b push h ; save push psw lxi h,cpfn ; clear page function jmp do$fn ; send the function and return ; ;---------------------------------------------------------------- ; Clear the screen till the end of the line. This is highly ; terminal dependant and is table driven to suit. ;---------------------------------------------------------------- ; cleol: push b push h ; save push psw lxi h,clfn ; clear page function jr do$fn ; send the function and return ; ;---------------------------------------------------------------- ; Clear to the end of the page. Same comments as the above fn. ;---------------------------------------------------------------- ; cleop: push b push h ; save push psw lxi h,epfn ; clear page function jr do$fn ; end of job ; ;----------------------------------------------- ; Set a visual attribute onto the video terminal ; Attribute in register A ;----------------------------------------------- ; setatt: cpi numatt + 1 ; In range ?? rnc ; Return tail B/N legs if too big push d push b push h push psw ; Save the users attribute mov c,a ; Save locally also lda curatt ; Get the current attribute cmp c jz set$fin ; Terminate if THE SAME ora a jrz doatt ; If 00, no need to clear old one, already 0 ; ; See if the current attribute is an illegal value. This may be because of ; rom based software first time through and a silly ram value. ; cpi numatt + 1 jrnc doatt ; Silly old attribute, then set new one now. ; ; Here we must clear the old attribute before proceeding. ; clr$start: dcr a ; Make the attribute in range lxi h,attclr ; Point to the table of clear atts. call index call att$send ; Send the attributes -> by HL doatt: pop psw ; Get users attribute sta curatt ; Save in ram. ora a ; Is it attribute 00 ? jrz set$end ; Ignore if so dcr a ; Make in the correct range then ; Now index into the table of attribute setting bytes to get the req'd code. lxi h,attset ; Point to start call index ; Get hl -> start call att$send ; Send attributes -> by HL ; ; Re-load registers and return. ; set$end: pop h pop b pop d ret ; ; Restore registers when terminated ; set$fin: pop psw jr set$end ; ; This routine must use HL -> a counter byte to send ; the proceeeding bytes to the console. ; att$send: mov a,m ora a ; See if a null length ? rz ; Return if a null string mov b,a ; Else load the length sloop: inx h ; Point to next character mov a,m call con$out ; Send djnz sloop ret ; ;---------------------------------------------------------------- ; This routine that follows simply indexes into the table ; of bytes. Each table entry is assumed to be 5 bytes long. ; ; On entry hl -> start of table, on exit hl -> first element ; in line (a) ;---------------------------------------------------------------- ; index: ora a rz ; Return if no loops needed mvi d,0 ; Clear upper register mov e,a ; Load counter ; Multiply A by 5 then add to HL add a ; * 2 (double original) add a ; * 4 add e ; * 5 (add original) mov e,a ; load into indexing register dad d ; now HL -> start of this entry ret ; hl -> start of a line ; ; This routine uses HL to send the function to the screen. ; send$fn: mov a,m ; test if this is not supported ora a rz ; not supported if no bytes to send mov b,a ; load a counter send$fn2: inx h ; get next byte mov a,m call con$out djnz send$fn2 ret ; ;---------------------------------------------------------------- ; Send the function pointed to by HL to the terminal then exit ;---------------------------------------------------------------- ; do$fn: call send$fn ; send it. ; Fall through to the exit routine end$fn: pop psw pop h pop b ret ; ;---------------------------------------------------------------- ; Position the cursor to the value in DE. D = X, E = Y. ;---------------------------------------------------------------- ; cursor: push b push h push psw ; save registers push d ; save the original ; Send the lead in string lxi h,xyfn ; XY addressing lead in string call send$fn ; Now we must add the offsets to be applied to the X and Y values lda xoff add e ; Y value mov e,a ; save it ; lda yoff add d ; X value mov d,a ; save it also ; Now decode the row or column sent first decision lda rowcol ; 00 = row first ora a jrz row$first ; Here and we are sending the column first (X value first) mov a,d ; X value call con$out mov a,e ; Y next jr row$first2 ; Here and we send the row first for cursor addressing row$first: mov a,e ; Y value call con$out mov a,d ; X value next row$first2: call con$out pop d ; restore original cursor address ; Now do any delays required lda cdly cursor$delay: ora a jrz end$fn ; End of job push psw mvi a,0ffh ; Loop counter cd$loop: nop ; Extra waits dcr a jrnz cd$loop ; Loop till A = 0 pop psw dcr a ; One done. jr cursor$delay ; ;---------------------------------------------------------------- ; Write bytes at mDE till a null ;---------------------------------------------------------------- ; wrt$vid: ldax d ora a rz call con$out inx d jr wrt$vid ; dseg ; hrs ds 1 min ds 1 sec ds 1 ; curatt db 00 curvid db 00 end ; ; ;