title 'Executive Processor Hardware Interface For CORE BOARD' ;---------------------------------------------------------------- ; This module contains the Console I/O Drivers for the CORE BOARD ; card. ; ; These are meant to be run in conjunction with the EXEC ; module. ; ; ; Written By Richard Holmes 04-02-86 ; Last Update By Richard Holmes 17-10-88 ; ; Bug fix in cursor address andlatch of 4 X 40 lcd ; Made 4 by 40 lcd true 13/08/88 ; Added hrs, min, sec data 17/10/88 ;---------------------------------------------------------------- ; maclib z80 maclib core maclib iocmd ; 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 lcd$ini,lcd$out,lcd$ost,lcd$cmd public prn$ini,prn$out,prn$ost public ms$delay ; public set$led,clr$led,tog$led,zro$led public set$bel,clr$bel public clk$rd,clk$wr ; public clr$wdt ; public sch$rd public set$bel,clr$bel ; public hrs, min, sec ; ; Communications AUX port routines. public aux$ird,aux$iwr ; Initialization routines ; extrn exec ; true equ 0ffffh false equ not true ; aux$xonof equ true ; Xon/Xoff AUX control debug equ false ; numatt equ 6 ; 6 attributes cr equ 0dh ; Simple video definitions lf equ 0ah bs equ 8 ; Backspace esc equ 01bh ; Escape ; ;---------------------------------------------------------------- ; Delay the number of milliseconds in DE ;---------------------------------------------------------------- ; 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 djnz delay4 ; 13 ; Fudge 14 machine cycles lxi b,0 ; 10 nop ; 4 pop b ; 10 ; ret ; ;================================================================ ; ; LED Drivers ; ;================================================================ ; Initialize the LED drivers by clearing the data byte and hence ; the led port ;---------------------------------------------------------------- ; zro$led: mvi a,wr$en ; Memory enable only sta led$byt ; Save for masks later out @ledd ; Send to driver ret ; ;---------------------------------------------------------------- ; Or the specified bit mask in A into the LED Outputs. ; ; On Entry ; A = led mask ; ; On Exit ; Set bit leds turned on. Others preserved. ;---------------------------------------------------------------- ; ori$led: push b ani 0011$1111b ; Mask off any top bits mov c,a ; Save lda led$byt ; Load old data ora c ; OR in the bit mask ; Maskin ram enable ori 1100$0000b ; Ensure ram on. jr led$com ; ;---------------------------------------------------------------- ; Set the specified bit in A LED on. Leave others alone. ; ; On Entry ; A = 1..6 for the led number ; On Exit ; Led number turned on. Others preserved. ;---------------------------------------------------------------- ; set$led: ora a rz ; Exit if = 0 cpi 7 rnc ; NC and > 6 hence in error push b call led$msk lda led$byt ; Load old data ora c ; OR in the new bit mask led$com: ori wr$en ; Force write enable high sta led$byt out @ledd ; Write out again pop b ret ; ;---------------------------------------------------------------- ; Clear the specified bit in A. Leave others alone. ; ; On Entry ; A = 1..6 for the led number ; On Exit ; Led number cleared. Others preserved. ;---------------------------------------------------------------- ; clr$led: ora a rz ; Exit if = 0 cpi 7 rnc ; NC and > 6 hence in error push b call led$msk cma mov c,a ; Invert and save led mask lda led$byt ; Load old data ana c ; And OFF the selected bit ori wr$en ; Mask in the memory enable bit jr led$com ; ;---------------------------------------------------------------- ; Toggle the passed in LED ; ;---------------------------------------------------------------- ; tog$led: ora a rz cpi 8 rnc ; push b call led$msk lda led$byt xra c ori wr$en jmp led$com ; ; --------------------------------------------- ; -- Convert a bit number into an actual Bit -- ; --------------------------------------------- ; led$msk: mov b,a ; load counter mvi a,080h ; Seed. Must rotate at least once. led$rot: rlc ; Shift left djnz led$rot mov c,a ; Save data into C for later ret ; ;---------------------------------------------------------------- ; Turn on the system bell ;---------------------------------------------------------------- ; set$bel: push psw mvi a,1 sta bel$byt out @buz ; To bell / buzzer pop psw ret ; ;---------------------------------------------------------------- ; Turn off the system bell ;---------------------------------------------------------------- ; clr$bel: push psw xra a ; Send a 0 sta bel$byt out @buz ; To bell / buzzer pop psw ret ; ;---------------------------------------------------------------- ; Toggle the system bell bit ON/OFF ;---------------------------------------------------------------- ; tog$bel: push psw lda bel$byt xri 1 sta bel$byt out @buz ; To bell / buzzer pop psw ret ; ;---------------------------------------------------------------- ; Clear the watchdog timer by writing a 1 then 0 to toggle ; the port. ;---------------------------------------------------------------- ; clr$wdt: push psw lda wdt$flg out @wdt xri 1 ; Toggle bit 1 sta wdt$flg pop psw ret ; ;---------------------------------------------------------------- ; Read the dip switch into A for the user ;---------------------------------------------------------------- ; schrd: in @sch ret ; ;---------------------------------------------------------------- ; Read real time clock to ram at DE ; ; Memory is laid out in the following format: ; ; seconds ; minutes ; hours ; day of week ; day of month ; month ; Year. Saved in ram in the clock chip at port 01fh ; ; Note the bit of code that stops the clock from being read ; constantly if the chip is faulty. This is the count value ; in B. ;---------------------------------------------------------------- ; clk$rd: push h push b ; Save sded ?time mvi b,5 ; 5 times maximum read count ; read$clock: ; Repeat here if a rollover lhld ?time in @clk + 2 ; Seconds mov m,a inx h ; in @clk + 3 ; Minutes mov m,a inx h ; in @clk + 4 ; hours mov m,a inx h ; in @clk + 5 ; Day of week mov m,a inx h ; in @clk + 6 ; Day of month mov m,a inx h ; in @clk + 7 ; Month mov m,a inx h ; -> year register in system ram in @clk + 0fh ; Year ram in clock mov m,a ; ; Check rollover status port. Bitset = an internal ripple, re-read required. ; in @clk+20 ; Status port ani 1 ; Status bit is in D0 jz read$kend ; if 00, a good read djnz read$clock ; Read till a 0 bit. ; ; Else the time is valid ; read$kend: pop b ; Restore all pop h ret ; ;---------------------------------------------------------------- ; ; Write the real time clock from a ram store. This is expected ; to be in the same format as that for clock read. ; ; On entry ; DE -> time buffer ;---------------------------------------------------------------- ; clk$wr: ; Reset clock counters mvi a,0ffh ; All 1's for a reset counter command out @clk + 18 ; Resets all the counters ; ldax d out @clk + 2 inx d ; ldax d out @clk + 3 ; Minutes inx d ; ldax d out @clk + 4 ; hours inx d ; ldax d out @clk + 5 ; Day of week inx d ; ldax d out @clk + 6 ; Day of month inx d ; ldax d out @clk + 7 ; Month inx d ; ldax d ; Write year to clock ram out @clk + 0fh ; Write to year register ram ; ret ; ;---------------------------------------------------------------- ; Hardware system initializations. ; ; This is called by the C=0 function in the EXEC processor. ;---------------------------------------------------------------- ; Initialize the hardware on the board ; ; Initialize the SIO for channel A and channel B 8 bit, 1 stop ; no parity and 9600 baud each. ; CTC channels 0 and 1 are for SIO baud rates ; CTC channel 2 for external interrupts......... (see monitor) ; CTC channel 3 for real time clock interrupts.. (see monitor) ;---------------------------------------------------------------- ; sys$ini: call clr$wdt ; Reset the watchdog timer. Stop him. ; ; Initialize the watchdog timer clear routine that toggles the ; low order bit each time it is called. ; xra a sta wdt$flg sta bel$byt sta hrs sta min sta sec ; mvi a,wr$en sta led$byt ; Setup the LED outputs ; 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 dw con$null ; con$null: ret ; ; The AUX channel does not have any such thing as a command ; processor. It is a comms channel, not a screen. aux$cmd: xra a ora a ret ; page ;---------------------------------------------------------------- ; L C D I / O D R I V E R S ;---------------------------------------------------------------- ; ; PB 0 = Enable 1. For controller 1 ; 1 = Enable 2. For controller 2 ; 1 = R/W-. 1 = write to LCD ; 2 = Register select 1 = Data, 0 = control register ;---------------------------------------------------------------- ; lcd$440 equ true lcd$216 equ false ; ; ---- Special LCD Equates. Here to make reading easy ---- ; data equ 044h ; LCD data port cntl equ 045h ; Control port of LCD mode equ 047h ; Mode port ; if lcd$440 ; idle equ 0 ; Idle display controllers wr$cmd2 equ 1 ; Write command 1 wr$cmd1 equ 2 ; Write command 2 ; wr$dat2 equ 9 ; Write data 1 wr$dat1 equ 0ah ; Write data 2 ; rd$st2 equ 5 ; Read status 1 rd$st1 equ 6 ; Read status 2 ; lcd$line equ 4 ; 4 line screen lcd$char equ 40 ; 40 chars per line endif ; ; Or are we a 16 by 2 ?? ; if lcd$216 lcd$line equ 2 ; 2 line screen lcd$char equ 16 ; 16 chars per line endif ; ;---------------------------------------------------------------- ; Initialize the LCD. ; ; 1. Data path 8 bits, 4 line display, 40 char. ; 2. Enable cursor. Blink. Enable display ; 4. Clear and home cursor. ; 3. Display stationary, auto increment, right move. ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; lcd$ini: if lcd$440 push psw ; mvi a,1 sta err$num ; ; Select the first controller, command latching. mvi a,wr$cmd1 ; Command for controller 1 sta lc$cmd ; Save in command port. call ini$cntrlr ; Initialize the controller jrc ini$err ; Error exit ; mvi a,wr$cmd2 ; Controller 2 sta lc$cmd call ini$cntrlr jrc ini$err ; Error exit ; xra a sta lcd$x sta lcd$y ; Setup X and Y addresses call cof$lcd ; Turn off all cursors. Usually 2 actually. jrc ini$err ; call con$lcd ; Turn cursor on. Just one ay lcdX, lcdY jrc ini$err ; pop psw ora a ; No carry = no error ret ; ini$err: pop psw jmp gp$err ; ;---------------------------------------------------------------- ; ---- Initialize the contoller. ---- ;---------------------------------------------------------------- ; ini$cntrlr: ; ; 1. Enable 8 bit bus, 2 line display mvi a,038h call lch$lcd ; Latch 8 bit, 2 line display rc ; Exit on error ; ; 2. Enable display on, cursor on, blink mvi a,0eh call lch$lcd rc ; mvi a,08 call lch$lcd rc ; ; ; 3. Display stationary, Right cursor ; mvi a,2 call lch$lcd rc ; mvi a,1 call lch$lcd rc ora a ; No error exit ret ; ; gp$err: mvi a,'e' call con$out stc ret ; endif ; ; ===================================== ; ==== 2 line by 16 char interface ==== ; ===================================== ; if lcd$216 push psw ; 1. Enable 8 bit bus, 2 line display mvi a,038h call lch$cmd ; Latch 8 bit, 2 line display ; 2. Enable display on, cursor on, blink mvi a,0fh call lch$cmd ; 3. Display stationary, auto increment, right mvi a,6 call lch$cmd ; 4. Display stationary, Right cursor mvi a,014h call lch$cmd ; Done. call lcd$clr pop psw ret endif ; page ;---------------------------------------------------------------- ; L C D C O M M A N D P R O C E S S O R ; ; Impliment the LCD commands so that the channel system can ; do clearing, cursor addressing etc. ;---------------------------------------------------------------- ; lcd$cmd: push h push b push d mvi b,0 ; Top offset = 0 lxi h,lc$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 ; lc$cmd$tbl: dw 0 dw lcd$clr ; LCD clear page dw lcd$null ; No clear to end of lcd page yet dw lcd$eol ; LCD clear to end of line dw lcd$cur ; LCD cursor address dw lcd$null ; No such thing as LCD video attributes dw lcd$getxy ; Fetch current LCD x-y address ; lcd$null: ret ; ; Returns DE = XY address. ; lcd$getxy: lda lcd$y mov e,a lda lcd$x mov d,a ret ; ;---------------------------------------------------------------- ; Return LCD Status. This is tricky as it must re-initialize ; the LCD control port and read the LCD status flag BF. It ; will return a 00 if idling or an FF if busy. NOTE the ; need to re-load the CS2 line to the ram chip and clock ; and LED return when finished. ;---------------------------------------------------------------- ; lcd$ost: if lcd$440 push h ; Save register to use as a scratch pad ; ; Generate the LCD enable signals lda lcd$cmd ; Command to be used ani 0000$0011b ; Get the controller bits ori 0000$0100b ; Or in the status reading function mov l,a ; L = device image. ; di ; STOP interrupts when checking LCD status mvi a,090h ; Port A inputs out @ledmd ; LED mode port mvi a,0c0h out @ledd ; Re-assert ram as soon as possible ; nop nop nop nop ; Settling time ; ; OK, Port A inputs, B and C outputs. ; Note the need to set R/W pin high for a time before the E pin (bit 0). ; mvi a,0000$0100b ; Send the Read bit out. out cntl ; To the control port mov a,l out cntl ; Send "E" bits also ; ; This requires a 40uS delay as per the data sheet ; mvi a,15 ; Plenty of time st$dly: dcr a jnz st$dly ; in data ; Read the LCD data mov l,a ; Save here. Can't see stack can we.... ; ; Restore 8255 mode mvi a,080h ; ALL outputs out @ledmd ; LED mode port ; Re-load ram control signals mvi a,0C0h ; Bit 6 and 7 for ram and clock on out @ledd ; Sends to led port to turn on ram again. ; nop nop nop nop xra a out cntl ; Clear the port ; ei ; Restore interrupts after LCD write ; call ori$led ; Restore previous LED status ; mov a,l ; Restore data pop h ; Restore register ;Now. Determine basic state of 00 = idle or FF = busy. ani 1000$0000b ; Leave busy flag rz mvi a,0ffh ; Else load an FF for very busy still. ret ; endif ; ; ==== 2 line by 16 char interface ==== ; if lcd$216 di ; STOP interrupts when checking LCD status push h ; Save a register to use as a scratch pad mvi a,090h ; Port A inputs out @ledmd ; LED mode port mvi a,0c0h out @ledd ; Re-assert ram as soon as possible ; ; OK, Port A inputs, B and C outputs. Dangerous. No RAM now. ; Note the need to set R/W pin high for a time before the E pin (bit 0). ; mvi a,0000$0010b ; Read status. E = 0, R/W- = 1, RS = 0 out @lcdc ; LCD Control sent. mvi a,0000$0011b ; Enable high now out @lcdc ; LCD now ready to be read. nop nop ; 2uS settling time in @lcdd ; Read the LCD data mov l,a ; Save here. Can't see stack can we.... ; Restore 8255 mode mvi a,080h ; ALL outputs out @ledmd ; LED mode port ; Re-load ram control signals mvi a,0C0h ; Bit 6 and 7 for ram and clock on out @ledd ; Sends to led port to turn on ram again. ; nop nop nop nop xra a ; Load a null so that ORI will work ; ei ; Restore interrupts after LCD write ; call ori$led ; Restore previous LED status mov a,l ; Restore data pop h ; Restore register ;Now. Determine basic state of 00 = idle or FF = busy. ani 1000$0000b ; Leave busy flag rz mvi a,0ffh ; Else load an FF for very busy still. ret endif ; ;------------------------------------------------ ; Latch the command in lc$cmd and the ; data in lcd$dat into the LCD. ; ; On Exit ; CARRY Set = error. LCD did not respond. ; Clr = no error. ;------------------------------------------------ ; lch$lcd: if lcd$440 push h push psw ; Save registers ; ; Now setup timeout counter in HL and look for LCD idle lxi h,400 ; Big delay lch$cmd$loop: call clr$wdt call clr$wdt call lcd$ost ; Get LCD status ora a jrz lch$cmd$ok dcx h mov a,l ora h ; Done ? jrnz lch$cmd$loop ; Loop on till IDLE (busy flag = 0) ; ERROR here ; mvi a,'L' call con$out mvi a,'E' call con$out ; pop psw pop h stc ; Carry flag = error ret ; ; Restore registers and do job ; lch$cmd$ok: pop psw push psw ; out data lda lc$cmd out cntl xra a out cntl ; pop psw pop h ora a ; Clear carry for all ok. ret ; endif ; ; ==== 2 line by 16 char interface ==== ; if lcd$216 ; lch$dat: push h push psw ; Save registers ; Now setup timeout counter in HL and look for LCD idle lxi h,200 ; Big delay lch$dat$loop: call clrwdt call lcd$ost ; Get LCD status ora a jrz lch$dat$ok dcx h mov a,l ora h ; Done ? jrnz lch$dat$loop ; Loop on till IDLE (busy flag = 0) ; ERROR here pop psw pop h stc ; Carry flag = error ret ; ; Restore registers and do job ; lch$dat$ok: pop psw pop h ; out data push psw mvi a,5 ; Data selector jr lch$comm ; Note the use of command common code ; ; lch$cmd: push h push psw ; Save registers ; Now setup timeout counter in HL and look for LCD idle lxi h,200 ; Big delay lch$cmd$loop: call clrwdt call lcd$ost ; Get LCD status ora a jrz lch$cmd$ok dcx h mov a,l ora h ; Done ? jrnz lch$cmd$loop ; Loop on till IDLE (busy flag = 0) ; ERROR here pop psw pop h stc ; Carry flag = error ret ; ; Restore registers and do job ; lch$cmd$ok: pop psw pop h ; fast$lch$cmd: ; Here to latch a command W/O busy checking out data push psw mvi a,1 lch$comm: ; Common latching code for both command and data out cntl xra a out cntl ; Latch HIGH then low pop psw ora a ; Clear carry ret ; endif ; page ;---------------------------------------------------------------- ; Clear LCD and home cursor. ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; lcd$clr: if lcd$440 push psw ; mvi a,2 sta err$num ; Clear controller 1 mvi a,wr$cmd1 sta lc$cmd ; Select a clear command controller 1 mvi a,1 ; Clear display totally call lch$lcd jrc clr$err ; ; Clear controller 2 mvi a,wr$cmd2 sta lc$cmd ; Select a clear command controller 1 mvi a,1 ; Clear display totally call lch$lcd jrc clr$err ; xra a ; Clear cursor address sta lcd$x sta lcd$y call hom$lcd jrc clr$err ; pop psw ora a ; Clear carry - no error. ret ; clr$err: pop psw jmp gp$err ; endif ; ; ==== 2 line by 16 interface ==== ; if lcd$216 push psw mvi a,1 ; Clear display totally call lch$cmd ; xra a ; Clear cursor address sta lcd$x sta lcd$y pop psw ret endif ; ;---------------------------------------------------------------- ; Home the cursor ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; hom$lcd: if lcd$440 push d push psw ; mvi a,3 sta err$num ; lxi d,0 ; Top left hand corner of screen call lcd$cur ; Home to the top lh lcd corner jrc hom$err ; pop psw pop d ora a ret ; hom$err: pop psw pop d jmp gp$err ; else ret endif ; ; ;---------------------------------------------------------------- ; Turn all cursors off. ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; cof$lcd: if lcd$440 push psw mvi a,wr$cmd1 sta lc$cmd mvi a,0000$1100b ; Display on, cursor off call lch$lcd ; Latch the command and data in. jrc cof$err ; mvi a,wr$cmd2 sta lc$cmd mvi a,0000$1100b ; Display on, cursor off call lch$lcd ; Latch the command and data in. jrc cof$err pop psw ora a ret ; cof$err: pop psw stc endif ; ret ; ;---------------------------------------------------------------- ; Turn the current cursor at lcd$x and lcd$y on. ; ; This is decided by the current cursor address of the lcd. ;---------------------------------------------------------------- ; con$lcd: if lcd$440 push b push psw ; ; Select the controller and load the write command control word mvi c,wr$cmd1 ; Default to controller 1 lda lcd$y ; Line number cpi 2 jrc con$do ; Line 0 and 1 user controller 1 ; mvi c,wr$cmd2 ; Controller 2 if line 2 or 3 con$do: mov a,c ; get sta lc$cmd ; Load the command mvi a,0fh ; cursor on call lch$lcd ; And do it. jrc lcd$con$err ; pop psw pop b ora a ret ; lcd$con$err: pop psw pop b stc endif ; ret ; page ;---------------------------------------------------------------- ; Cursor address the lcd. ; ; On Entry D = X in the range 0..38 (characters per line) ; E = Y in the range 0..3 (lines) ; ; If out of range, no change is made. ; All registers preserved ;---------------------------------------------------------------- ; lcd$cur: if lcd$440 push b push psw ; ; Check if X > possible range. mvi b,1 ; Error location pointer mov a,d cpi 40 ; Error if > 16 jrnc cur$err ; ; Check Line number in range mvi b,2 mov a,e cpi 4 jrnc cur$err ; Error if > 3 ; mvi b,3 call cof$lcd jrc cur$err ; ; Calculate the LCD command mov a,e ; New 'y' address sta lcd$y ; Save it also. ; mvi c,wr$cmd1 ; Default to controller 1 cpi 2 jrc cur$lc$cmd mvi c,wr$cmd2 ; cur$lc$cmd: mov a,c sta lc$cmd ; ; Now generate the ram address for the LCD mvi c,080h ; Top bit = cursor address mov a,e ; Get the line number back ani 0000$0001b ; Do a modulus 2 jrz cur$not$1 ; Top row is address 80h..A7h mvi c,0C0h ; If second row the offset is 0C0h ; cur$not$1: mov a,c ; Address add d ; Add in the X address ; call lch$lcd ; Command in A built up, latch to LCD mvi b,4 ; Flag any error locations. jrc cur$err ; Save the X address also mov a,d ; X sta lcd$x ; ; Finally, turn on the cusor mvi a,0000$1111b ; Turn on cursor, display, blink. call lch$lcd mvi b,5 jrc cur$err ; pop psw pop b ora a ret ; cur$err: ; Signal the cursor addressing error mvi a,'C' call con$out mvi a,'E' call con$out mvi a,'0' add b call con$out ; pop psw pop b stc ret ; endif ; ; ==== 2 line by 16 LCD interface ==== ; if lcd$216 push psw ; Check if X > possible range. mov a,d cpi 16 ; Error if > 16 jrnc cur$err ; Check Line number mov a,e cpi 2 jrnc cur$err ; Error if > 1 ; Now do the addressing sta lcd$y ; Save LCD address ; push b ; Save all registers always, makes easy later. mvi c,080h ; Top bit = cursor address ora a ; At top row (A = 0 ?) jrz cur$not$1 ; Top row is address 80h..8fh mvi c,0C0h ; If second row the offset if 0C0h ; cur$not$1: mov a,c ; Address add d ; Add in the X address call lch$cmd ; Command in A built up ; Save the X address also mov a,d ; X sta lcd$x ; pop b cur$err: pop psw ret ; endif ; ;---------------------------------------------------------------- ; Clear to end of line. ;---------------------------------------------------------------- ; lcd$eol: push h push b push d push psw ; Get the current cursor address. lda lcd$y mov e,a lda lcd$x mov d,a ; Save X ; ; Generate a counter in B for the number of spaces to be sent mov b,a ; A counter also mvi a,39 sub b ; A = 39 - column. jrz eol$done jrc eol$done mov b,a ; B = the number of spaces ; ; B = column number. Send spaces to end of line now. ; eol$loop: mvi a,' ' ; Erase with a space. call lcd$out ; Send the character to the LCD jrc eol$err djnz eol$loop ; eol$done: call lcd$cur ; Cursor address to previous row/col position ; pop psw pop d pop b pop h ora a ; No error ret ; eol$err: pop psw pop d pop b pop h stc ; Error flag ret ; page ;---------------------------------------------------------------- ; ; Send character in A to the LCD ; ; We must decide which controller by checking the lcd$y address ; and use Y = 0,1 for controller 1, and 2,3 for controller 2. ; ; Note that the cursor is not moved. ;---------------------------------------------------------------- ; lcd$out: push psw push b ; ; Carriage return ? cpi 0dh jz put$cr cpi lf jz put$lf cpi bs ; Back space ? jz put$bs ; ; None of these. Bump the column number. lda lcd$x inr a sta lcd$x cpi lcd$char + 1 ; End of line ? jc put$lcd1 ; If not, continue. xra a ; Start of next line sta lcd$x ; ; Bump the row now. We must also cusor position. ; This is used as the line feed entry point so that the ; line number is advaced without advancing any columns. ; lda lcd$y inr a sta lcd$y cpi lcd$line ; Past last line ? jrc put$lcd$xy ; Not last row and all ok ; ; Past last row. Start at top again. xra a sta lcd$y ; put$lcd$xy: push d lda lcd$x mov d,a lda lcd$y mov e,a call lcd$cur ; Setup cursor at end of line to "wrap" pop d ; ; Now select the controller to be used (written to). ; put$lcd1: if lcd$440 mvi c,wr$dat1 ; Default to controller 1 lda lcd$y cpi 2 jrc put$lcd$do mvi c,wr$dat2 ; put$lcd$do: mov a,c sta lc$cmd ; pop b pop psw jmp lch$lcd endif ; ; ---- Else merely latch in data when a 16 char by 2 line display ---- ; if lcd$216 pop b pop psw jmp lch$dat endif ; ; ; Carriage return = home cursor to left hand column ; put$cr: xra a sta lcd$x ; ; Cursor address now - Also used by the CR handler ; put$lf$cur: push d lda lcd$x mov d,a ; D = X lda lcd$y mov e,a ; E = Y call lcd$cur pop d ; ; put$end: pop b ; Saved at routine start. pop psw ora a ; No error ret ; ; Process a line feed by incrementing the line number by 1 ; put$lf: lda lcd$y ; Line number inr a sta lcd$y cpi lcd$line ; Last line ? jc put$lf$cur ; Carry = not past end line xra a sta lcd$y jmp put$lf$cur ; ; Send a backspace to the LCD by decrementing the current column by ; one. NOTE that the backspacing will stop at left hand margin. ; put$bs: lda lcd$x ora a ; At zero ? jrz put$end dcr a sta lcd$x ; Save the new decremented column jmp put$lf$cur ; Cursor address now. ; page ;---------------------------------------------------------------- ; P R I N T E R I / O D R I V E R S ;---------------------------------------------------------------- ; prn$ini: prn$ost: prn$cmd: prn$out: ret ; page ;---------------------------------------------------------------- ; A U X P O R T R O U T I N E S ; ; Initialize the AUX Port hardware ; ; Setup AUX comms port to be ; 1200 baud ; 8 bit ; 1 stop ; NO parity ;---------------------------------------------------------------- ; aux$ini: lxi h,aux$initbl aux$initlp: mov b,m ; get counter inx h mov c,m ; get port mov a,b ora a ; check for zero jrz aux$inictc ; exit if count=0 inx h outir ; send all out jr aux$initlp ; aux$inictc: mvi a,02h ; Reset channel out @ctc1 ; nop nop ; ; mvi a,045h ; Time constant follows out @ctc1 nop mvi a,104 ; For 1200 baud out @ctc1 ; ret ; ;---------------------------------------------------------------- ; Aux output ; ; Xon/Xoff handshaking done here. Check on entry for a code back ; from printer and if so, wait for another to re-start. Also, for ; safety, have a timeout. ;---------------------------------------------------------------- ; aux$out: push psw ; if aux$xonof ; Allowed to Xon/Xoff are we?? call aux$ist ; Character there ? jrz aux$wait ; Nothing = no bother ; ; We have a character call aux$inp cpi 'S' and 01fh jrnz aux$wait ; We have an Xoff. Wait for Xon. ; push h lxi h,4000h aux$xon: call clr$wdt ; Stop watchdog. call aux$ist jz xon$wait call aux$inp jr aux$xof ; xon$wait: dcx h mov a,h ora l ; HL = 0 ? jnz aux$xon aux$xof: pop h ; Restore HL/stack ; endif ; ; -- Send character to aux port -- ; aux$wait: call clr$wdt in @stat1 ani txmsk ; See if transmitter empty. jrz aux$wait ; Ok, send the data out. pop psw out @data1 ret ; ;---------------------------------------------------------------- ; Aux input ;---------------------------------------------------------------- ; aux$inp: call clr$wdt in @stat1 ani rxmsk jrz aux$inp in @data1 ret ; Done ; ;---------------------------------------------------------------- ; Console status ;---------------------------------------------------------------- ; aux$ist: call clr$wdt ; Clear the dog in @stat1 ani rxmsk rz mvi a,0ffh ret ; ; Return the aux output status. ; aux$ost: call clr$wdt in @stat1 ani txmsk rz mvi a,0ffh ret ; aux$iwr: aux$ird: ret ; ;---------------------------------------------------------------- ; Hardware setups for the AUX port chips. ;---------------------------------------------------------------- ; aux$initbl: ; Setup the dart B channel db 01,@stat1,018h ; Channel reset db 02,@stat1,04,044h ; X1 clock, 1 stop bit, parity OFF db 02,@stat1,01,000h ; Clear internal status db 02,@stat1,03,0c1h ; Rx 8 bit. Rx enable, auto enables OFF db 02,@stat1,05,0eah ; DTR, Tx 8 bit, Tx enable, RTS, DTR of db 00 ; page ;---------------------------------------------------------------- ; C O N S O L E R O U T I N E S ; ; Initialize the console hardware ; ; Setup console serial port to be ; 9600 baud ; 8 bit ; 1 stop ; NO parity ;---------------------------------------------------------------- ; con$ini: lxi h,con$initbl con$initlp: mov b,m ; get counter inx h mov c,m ; get port mov a,b ora a ; check for zero jrz con$inictc ; exit if count=0 inx h outir ; send all out jr con$initlp ; con$inictc: mvi a,02h ; Reset channel out @ctc0 ; nop nop ; ; mvi a,045h ; Time constant follows out @ctc0 mvi a,13 out @ctc0 ret ; ;---------------------------------------------------------------- ; Hardware setups for the console system chips. ;---------------------------------------------------------------- ; con$initbl: ; Setup the dart A channel db 01,@stat0,018h ; Channel reset db 02,@stat0,04,044h ; X1 clock, 1 stop bit, parity OFF db 02,@stat0,01,000h ; Clear internal status db 02,@stat0,03,0c1h ; Rx 8 bit. Rx enable, auto enables OFF db 02,@stat0,05,0eah ; DTR, Tx 8 bit, Tx enable, RTS, DTR of db 00 ; ;---------------------------------------------------------------- ; Read the console serial port error bits. ; ; On Entry ; nothing required ; ; On Exit ; A = 0 for no errors ; else same errors as noted in data book. ;---------------------------------------------------------------- ; con$err: xra a ora a ret ; ;---------------------------------------------------------------- ; Return the console input status. ; ; The console is the first ASCI serial channel, channel 0 ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for nothing there ; A = 01 for a character waiting to be read. ;---------------------------------------------------------------- ; con$ist: call clr$wdt ; Clear the dog in @stat0 ani rxmsk rz mvi a,0ffh ret ; ;---------------------------------------------------------------- ; Return the console output status. ; ; The console is the first ASCI serial channel, channel 0 ; ; On Entry ; nothing required ; ; On Exit ; A = 00 for transmitter busy ; A = 01 for a character can be sent to transmitter ;---------------------------------------------------------------- ; con$ost: in @stat0 ani txmsk ; See if transmitter empty. rz mvi a,0ffh ret ; page ;---------------------------------------------------------------- ; Read a byte from the Console port ; ; The console is the first ASCI serial channel, channel 0 ; ; On Entry ; nothing required ; ; On Exit ; A = char for a character waiting to be read. ; ; note that NO WATCHDOG clears will be done. Careful. ;---------------------------------------------------------------- ; con$inp: call clr$wdt in @stat0 ani rxmsk jrz con$inp in @data0 ret ; Done ; ;---------------------------------------------------------------- ; Send the byte in A to the output port. Wait if necessary. ; ; On Entry ; A = data byte to send ; ; On Exit ; No changes ; ;---------------------------------------------------------------- ; con$out: push psw coe$wait: call clr$wdt in @stat0 ani txmsk ; See if transmitter empty. jrz coe$wait ; Ok, send the data out. pop psw out @data0 ret ; page ;================================================================ ; V I D E O D R I V E R R O U T I N E S ;================================================================ ; ; 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 ; curatt db 00 curvid db 00 ; ; -- LCD Data bytes -- lc$cmd: ds 1 ; Command to write to LCD ; ; err$num ds 1 lcd$x: ds 1 lcd$y: ds 1 ; bel$byt db 00 ; Last bell port write led$byt db 00 ; Last LED output byte wdt$flg db 00 ; Dog flag bit 0 ; hrs ds 1 min ds 1 sec ds 1 ; ?time: ds 8 ; Time buffer address ; end ; ;