title 'Executive Processor Hardware Interface For Dinos' ; ;---------------------------------------------------------------- ; This module contains the Console and other general purpose ; I/O Drivers for the Dinos CPU card. ; ; These are meant to be run in conjunction with the EXEC ; module which provides a sort of "CP/M - like" interface for ; programming in assembler on the hardware. ; ; Written By Richard Holmes 04-02-86 ; Last Update By Richard Holmes 21-05-88 ; ; Added cash drawer set/clr on bit 7 of gp$out 21/05/1988 ; ;---------------------------------------------------------------- ; maclib hdz80 maclib iomap ; 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 set$led,clr$led,tog$led,zro$led public int$led ; public ini$clk,int$clk ; Clock code. public clk$rd,clk$wr ; ; public set$bel,clr$bel public set$csh,clr$csh ; public ms$delay,clr$wdt ; public sch$rd public set$bel,clr$bel public gp$dat ; public hrs,min,sec ; ; Communications AUX port routines. public aux$ird,aux$iwr ; Initialization routines ; extrn exec ; true equ 0ffffh false equ not true ; debug equ false ; hd64180 equ true ; cp$stb equ 0100$0000b ; Strobe of centronic printer ; led$psV equ 25 ; Led in call prescaler in units of int time def$tik equ 100 ; 100 ticks per second. 10ms per tick. ; numatt equ 6 ; 6 attributes cr equ 0dh ; Simple video definitions lf equ 0ah bs equ 8 ; Backspace esc equ 01bh ; Escape ; page ;---------------------------------------------------------------- ; Interrupt driven real time clock ; ; This code uses the periodic 10ms interrupt and increments the ; real time hours, minutes and seconds counters. ; ;---------------------------------------------------------------- ; int$clk: lda ticks ; Count ticks to get a second. dcr a sta ticks rnz ; Exit if not a 00 ; mvi a,def$tik sta ticks ; Reset the ticker ; lda sec inr a ; One more second sta sec cpi 60 ; 60 per minute rc ; Exit if <60 seconds ; xra a sta sec ; Reset the seconds lda min ; One more minute inr a sta min ; Save cpi 60 rc ; Exit if not end of minute ; xra a sta min ; Clear the minute lda hrs inr a sta hrs cpi 24 rc ; Exit if not end of day xra a sta hrs ret ; ;---------------------------------------------------------------- ; Initialize the real time clock. ; ; Load the tick counter with the number of counts in a second ; Clear the hour, minute and seconds registers ;---------------------------------------------------------------- ; ini$clk: mvi a,def$tik sta ticks ; xra a sta hrs sta min sta sec ; sta day ; sta mth ; sta year ; ret ; page ;---------------------------------------------------------------- ; Intelligent LED Driver code. ; ; These routines are meant to intelligently control the LED ; array by providing interrupt service routines to handle ; flashing and on/off routines. ; ; The format of the led buffer is ; ; bit 7 = On/OFF bit. Set = on ; 6 = Flash bit. Set = led will be flashed. ; 5..0 = not used ; ; Leds are interfaced via a pair of MM5486 chips which take in ; clock, data and load signals to clock in their 33 outputs. ; For DINOS, this has been done via the port at "led$data" and it ; is wired as. ; ; Bit 7 = ; 6 = ; 5 = ; 4 = Load to chip 2 ; 3 = Clock to chip 2 ; 2 = Load to chip 1 ; 1 = Clock to chip 1 ; 0 = Common data to both chips ; ;---------------------------------------------------------------- ; led$data equ 099h led$ck1 equ 0000$0010b ; Clock to chip 1 led$ld1 equ 0000$0100b ; Load line to chip 1 led$ck2 equ 0000$1000b ; Clock to chip 2 led$ld2 equ 0001$0000b ; Load to chip 2 ; ;---------------------------------------------------------------- ; This is the led interrupt driver. It checks for changes in the ; led image array and also does any flashing required. ; ;---------------------------------------------------------------- ; int$led: lda led$ps ; Prescaler dcr a sta led$ps jnz int$led$chg ; If not= 0, check for a change. ; ; At the end of the prescale time, re-load it and then look to see if any ; leds to be flashed. ; mvi a,led$psV ; Initial down count value. sta led$ps ; lda fsh$cnt ; Check if any leds flashing. ora a jrnz led$int$do ; ; Check if a written change. If so, write the outputs immediately. ; int$led$chg: lda led$chg ora a rz ; Exit now if no led changes possible. ; led$int$do: lda fsh$flg xri 1000$0000b ; This variable causes all flashing sta fsh$flg ; leds to flash together. ; lxi h,led$buf ; -> base of led image table. mvi c,led$ck1 ; Clock line image for chip 1 call int$wrt ; mvi c,led$ck2 call int$wrt ; mvi a,led$ld1 or led$ld2 out led$data xra a sta led$chg out led$data ret ; ;---------------------------------------------------------------- ; Interrupt subroutine. ; ; Send 33 bytes from mHL to the chip. Clock in with the ; data byte in register C. ;---------------------------------------------------------------- ; int$wrt: mvi b,33 ; bits to clock out. int$out: mov a,m bit 6,a ; Flashing ? jrz int$out1 ; lda fsh$flg ; Get the flash state int$out1: rlc ; Put bit 7 into bit 0 for data output ani 0000$0001b ; leave in data only also. out led$data nop ora c ; Or in the clock bits out led$data inx h djnz int$out ; ; Take data and clock low a last time now. xra a out led$data ret ; ;---------------------------------------------------------------- ; Clear all leds and their buffer(s) ; ;---------------------------------------------------------------- ; zro$led: push h push b push d ; di xra a sta led$chg ; No changes sta fsh$cnt ; No flashing sta fsh$flg ; Flash state = off ; ; Setup led interrupt call prescaler to be psV increments of the ; interrupt call. mvi a,led$psV ; Downcounter sta led$ps ; for the prescaler. ; lxi h,led$buf ; -> buffer for first chip lxi d,led$buf + 33 ; -> buffer for second chip mvi b,33 ; Bits to shift out and clear ; zll: xra a mov m,a ; Clear buffer 1 byte stax d ; Clear buffer 2 byte ; out led$data ; Send data out first ori led$ck1 or led$ck2 ; OR In the clock lines nop out led$data ; inx h ;-> next 1st buffer inx d ;-> next 2nd buffer location ; djnz zll ; Clear all bits etc. ; xra a out led$data ; Take last clock low now. ; ; Load the load pulse high now mvi a,led$ld1 or led$ld2 ; Set both Load lines high out led$data xra a nop out led$data ; Load low to latch outputs ; ei ; pop d pop b pop h ret ; ;---------------------------------------------------------------- ; Force a led to be flashing. This is done by setting the ; second top bit (bit6) if not already set. The flash count ; and the state change bits must also be updated. ;---------------------------------------------------------------- ; tog$led: ora a rz ; LED 0 not used ; push b push h ; lxi h,led$buf ; -> array of leds dcr a mov c,a mvi b,0 dad b ; HL -> led image in ram ; ; Is the led already flashing ? di bit 6,m jrnz led$fsh$end ; NZ = already set mvi a,1 sta led$chg ; Flag a led has changed mov a,m ani 0011$1111b ; Clear two top bits. ori 0100$0000b ; Only or in led flash bit. mov m,a ; Led is on now. ; ; We are flashing now. lda fsh$cnt inr a sta fsh$cnt ; One more flashing led. ; led$fsh$end: ei pop h pop b ret ; ;---------------------------------------------------------------- ; Turn off a single led. This is done by clearing the top ; bit if set in the table. Also, we must check the flashing ; bit and if this is set, clear and decrement the flash counter. ;---------------------------------------------------------------- ; clr$led: ora a rz ; LED 0 not used ; push b push h ; lxi h,led$buf ; -> array of leds dcr a mov c,a mvi b,0 dad b ; HL -> led image in ram ; ; Is the led set already ? di bit 7,m jz led$off$now ; Z = already off mvi a,1 sta led$chg ; Flag a led has changed mov a,m ani 0111$1111b ; Clear the led bit. mov m,a ; Led is on now. ; ; The led is on. Determine if the flash bit will be cleared and ; clear it if necessary ; led$off$now bit 6,m jrz led$off$end ; Exit if not fashing ; We were flashing. Decrement the flash counter and clear the flash bit mov a,m ani 0011$1111b ; Clear flash bit also mov m,a ; Save back. ; lda fsh$cnt dcr a sta fsh$cnt ; One less flashing led. ; Flag a led image change mvi a,1 sta led$chg ; ; led$off$end: ei pop h pop b ret ; ;---------------------------------------------------------------- ; Turn on a single led. This is done by setting bit 7, clearing bit ; 0 if set and exiting. ;---------------------------------------------------------------- ; set$led: ora a rz ; LED 0 not used ; push b push h ; lxi h,led$buf ; -> array of leds dcr a mov c,a mvi b,0 dad b ; HL -> led image in ram ; ; Is the led set already ? di bit 7,m jnz led$on$now ; NZ = already set mvi a,1 sta led$chg ; Flag a led has changed mov a,m ori 1000$0000b ; Only or in the led set bit. mov m,a ; Led is on now. ; ; The led is on. Determine if the flash bit will be cleared and ; clear it if necessary ; led$on$now bit 6,m jrz led$on$end ; Exit if not fashing ; We were flashing. Decrement the flash counter and clear the flash bit mov a,m ani 1000$0000b mov m,a ; Preserve all but flag and on bits ; lda fsh$cnt dcr a sta fsh$cnt ; One less flashing led. ; Flag a led image change mvi a,1 sta led$chg ; led$on$end: ei pop h pop b ret ; ret ; ;---------------------------------------------------------------- ; Read the real time clock to memory at DE. This copies the ; bytes from the counters in local ram to the ram in the ; calling program. ; The format is mDE -> hrs, min, sec in that order. ;---------------------------------------------------------------- ; clk$rd: push d lda hrs stax d ; inx d lda min stax d ; inx d lda sec stax d pop d ret ; ;---------------------------------------------------------------- ; Write memory at DE into the real time clock. Ram is arranged ; in the same manner as the read routine. ; The format is mDE -> hrs, min, sec in that order. ;---------------------------------------------------------------- ; clk$wr: push d ldax d sta hrs ; inx d ldax d sta min ; inx d ldax d sta sec pop d ret ; ; if debug prhex: push psw rrc rrc rrc rrc call phexl pop psw phexl: ani 0fh adi 90h daa aci 40h daa jmp con$out endif ; ;---------------------------------------------------------------- ; Hardware system initializations. ; ; This is called by the C=0 function in the EXEC processor. ;---------------------------------------------------------------- ; sys$ini: call clr$wdt ; Reset the watchdog timer. Stop him. xra a out led$dat mvi a,1 out led$dat nop nop xra a out led$dat out led$adr ; Setup address and data ports of LED bus ; ; Disable all wait states and refreshing. Speed essential. mvi a,0000$0000b ; No wait state on ram outo dcntl,a ; to wait control port ; xra a outo rcr,a ; Stop refreshing. All static. ; Setup the General purpose output image mvi a,0000$0000b ; Disables cash drawer and beeper sta gp$dat ; Save as a image for later LCD use mainly out gp$out ; Write to port also. ret ; ;---------------------------------------------------------------- ; Simple Subroutines ;---------------------------------------------------------------- ; 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,50 ; 7 delay4: ; This loop does (4 + 13) * 230 - 5 = 3905 t call clr$wdt djnz delay4 ; 13 ; Fudge 14 machine cycles lxi b,0 ; 10 nop ; 4 pop b ; 10 ret ; page ;---------------------------------------------------------------- ; 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 ; ; 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 ;---------------------------------------------------------------- ; ; ---- Special LCD Equates. Here to make reading easy ---- ; idle equ 0 ; Idle display controllers wr$cmd2 equ 1 ; Write command. Controller 2 wr$cmd1 equ 2 ; Write command. Controller 1 ; 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 ; ;---------------------------------------------------------------- ; Initialize the LCD. ; ; 1. Data path 8 bits, 2 line display ; 2. Enable cursor. Blink. Enable display ; 4. Clear and home cursor. ; 3. Display stationary, auto increment, right ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; lcd$ini: push psw ; mvi a,1 sta err$num ; ; Turn lines to write mode di lda gp$dat ani 1101$1111b sta gp$dat out gp$out ei ; ; Select the first controller, command latching. mvi a,wr$cmd1 ; Command for controller 1 sta cmd$lcd ; Save in command port. call ini$cntrlr ; Initialize the controller jrc ini$err ; Error exit ; mvi a,wr$cmd2 ; Controller 2 sta cmd$lcd 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 ; 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,lcd$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 ; lcd$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 ; lcd$null: ret ; ;---------------------------------------------------------------- ; Return LCD Output Status. ; ; It will return a 00 if idling or an FF if busy. ; ;---------------------------------------------------------------- ; lcd$ost: push h ; ; Select read mode of the port di lda gp$dat ani 1111$0000b ; Remove any previous LCD control 4 bits mov l,a ; Save ; lda cmd$lcd ani 0000$0011b ; Get the controller bits ori 0000$0100b ; R/Wr- ora l ori 0010$0000b ; Read mode now (74HC574 disable) mov h,a ; H = complete image ; Write out, less the ENABLES, which come on after R/S setups ani 1111$1100b ; Enable low still. out gp$out ; ; Turn on enables now also mov a,h out gp$out ; Send it out ; ; This requires a 40uS delay as per the data sheet ; mvi a,18 ; Plenty of time even at 8mhZ Z80 st$dly: dcr a jrnz st$dly ; in lcd$rd ; Read the LCD data push psw ; ; Restore LCD to write mode again. ENABLE the output port. ; mov a,h ani 1111$1100b ; Enables low first out gp$out ; mov a,l ; Restore old GP data ani 1101$1111b ; Low bit 5 = WRITE mode on data port out gp$out sta gp$dat ei ; ; pop psw ; Restore data pop h ; ;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 ; ;------------------------------------------------ ; Latch the command in cmd$lcd and the ; data in lcd$dat into the LCD. ; ; On Exit ; CARRY Set = error. LCD did not respond. ; Clr = no error. ;------------------------------------------------ ; lch$lcd: push h push psw ; Save registers ; ; Now setup timeout counter in HL and look for LCD idle lxi h,100 ; Big delay, number of times to test status lch$cmd$loop0: call clr$wdt call lcd$ost ; Get LCD output status ora a jrz lch$cmd$ok0 ; ; Do a small delay to allow LCD to 'work' after each check of status mvi a,10 lcl10: call clr$wdt dcr a jrnz lcl10 ; ; Now see if timed out. dcx h mov a,l ora h ; Done ? jrnz lch$cmd$loop0 ; Loop on till IDLE (busy flag = 0) ; ; ERROR here, did not "un busy" in time. ; pop psw pop h ; mvi a,'L' call con$out mvi a,'e' call conout stc ret ; ; Restore registers and do job ; lch$cmd$ok0: pop psw push psw ; out lcd$wr ; Load to the data to the port ; ; Now toggle it in, slowly as per specs. di lda gp$dat mov l,a ; Save lda cmd$lcd ora l mov h,a ; H = complete image ; ; Send command, LESS enables first ani 1111$1100b out gp$out ; ; Turn on Enables mov a,h ; Turn on enable now out gp$out ; Turn on the command ; ; Delay..... Clear enable first mov a,h ani 1111$1100b out gp$out ; mov a,l ; Restore port out gp$out ; Send to O/P port to latch in ; ei ; Re-enable interrupts again please. ; pop psw pop h ora a ; Clear carry. No error here yet. ret ; ;---------------------------------------------------------------- ; Clear LCD and home cursor. ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; lcd$clr: push psw ; mvi a,2 sta err$num ; Clear controller 1 mvi a,wr$cmd1 sta cmd$lcd ; 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 cmd$lcd ; 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 ; ;---------------------------------------------------------------- ; Home the cursor ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; hom$lcd: push d push psw ; mvi a,3 sta err$num call cof$lcd ; All cursors off now jrc hom$err ; 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 ; ;---------------------------------------------------------------- ; Turn all cursors off. ; ; On Exit ; CARRY Set = ERROR - probably LCD missing / faulty. ; Clr = no problem - all ok. ;---------------------------------------------------------------- ; cof$lcd: push psw mvi a,wr$cmd1 sta cmd$lcd 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 cmd$lcd 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 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: 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 cmd$lcd ; 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 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: 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 ; ; Turn off all the current cursors call cof$lcd ; mvi b,3 ; Error code ; lda lcd$y ; Get current/old Y address ; mvi c,wr$cmd1 ; Default to controller 1 ; cpi 2 ; jrc cur$cc ; mvi c,wr$cmd2 ; ;cur$cc: ; mov a,c ; sta cmd$lcd ; mvi a,0000$1100b ; Display on, cursor off ; call lch$lcd ; Latch the command and data in. 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$lcd$cmd mvi c,wr$cmd2 ; cur$lcd$cmd: mov a,c sta cmd$lcd ; ; 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 ; pop psw 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 ; ;---------------------------------------------------------------- ; 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 41 ; 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 4 ; Only allowed rows 0..3 jc 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: mvi c,wr$dat1 ; Default to controller 1 lda lcd$y cpi 2 jc put$lcd$do mvi c,wr$dat2 ; put$lcd$do: mov a,c sta cmd$lcd ; pop b pop psw jmp lch$lcd ; ; 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 4 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 ; ; Initialize the printer strobe bit. ;---------------------------------------------------------------- ; prn$ini: mvi a,0100$0000b ; Printer Strobe High sta gp$dat out gp$out ret ; ;---------------------------------------------------------------- ; Return the centronic printer status. ; ; ZERO = idle. Ready for data. ; 01 = Busy ; 02 = Error ; 03 = Error and busy. ;---------------------------------------------------------------- ; prn$ost: push b in ip$rd rlc ani 0000$0001b ; Mask off dregs mov c,a ; Save busy flag in ip$rd ; Re-read status flags rlc ; Error to bit 7 rlc ; Error to bit 0 rlc ; Error to bit 1 cma ; Invert to make error bit active high ani 0000$0010b ; Leave error bit only ora c ; Add in the busy bit pop b ora a ; Load zero flag accordingly ret ; ;---------------------------------------------------------------- ; This routine sends a byte out to the centronic printer ; and does basic checking and handshaking. It is a POLLED ; printer output routine. ; ; On Entry ; A = character ; ; On Exit ; CARRY FLAG CLEAR ; printer has data ; ; CARRY FLAG SET ; A = 01 for printer ERROR ; = 02 for printer timeout ;---------------------------------------------------------------- ; prn$out: push psw ; Save character call clr$wdt call prn$ost ; Get status jrz prn$ready ; ; Busy or in error. bit 0,a ; Busy ? jrnz prn$busy ; ; -- Here the printer is in error. -- ; Return a status of in error with carry bit set. ; pop psw mvi a,1 ; ERROR Flag prn$err: stc ora a ret ; ; -- Printer is busy. Wait for it. ---- ; prn$busy: push h lxi h,750 ; Delay factor ; prn$wait: call clr$wdt call prn$ost jz prn$not$busy bit 1,a jnz prn$busy$error ; push d lxi d,500 prn$wait$loop: dcx d mov e,a ora d jrnz prn$wait$loop pop d ; dcx h mov a,l ora h jrnz prn$wait ; ; Timed out. pop h pop psw mvi a,2 jmp prn$err ; ; Here the printer went into error after being busy. ; prn$busy$error: pop h pop psw mvi a,1 jmp prn$err ; prn$not$busy: pop h ; Restore saved register ; prn$ready: pop psw out cp$dat ; Write to cent. printer data port di ; Stop any possible clash lda gp$dat ani 1011$1111b ; Strobe low out gp$out ; Put it out. ori cp$stb sta gp$dat ; Save new image out gp$out ; Strobe high. Data now in printer. ei ; ; Printed. Fall to "OK" exit point. ; prn$ok: xra a ora a ; ZERO and NC = no error 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 ; 9600 baud ; 8 bit ; 1 stop ; NO parity ;---------------------------------------------------------------- ; aux$ini: mvi a,0110$0100b outo cntla0,a ; B ; ; Setup the second ASCI control register ; mvi a,0000$0010b ; Prescale by total 640 = 9600 baud outo cntlb0,a ; call aux$ird ; Select read mode on aux port ret ; ; ;---------------------------------------------------------------- ; Send the byte in (A) to the comm link channel (actually ; the RS485 interface) ;---------------------------------------------------------------- ; aux$out: push psw aux$out$wait: call clr$wdt call aux$ost jrz aux$out$wait ; pop psw outo tdr0,a ret ; ; aux$inp: call clr$wdt call aux$ist jrz aux$inp ino a,rdr0 ret ; aux$ist: ino a,stat0 ; Status ani 1000$0000b ret ; aux$ost: ino a,stat0 ani 0000$0010b ; Transmit data register empty ret ; ;---------------------------------------------------------------- ; Initialize the AUX port to WRITE mode. ;---------------------------------------------------------------- ; aux$iwr: push psw ino a,cntla0 ; Read port ori 0001$0000b ; RTS = 1 for Write outo cntla0,a pop psw ret ; ;---------------------------------------------------------------- ; Initialize the aux port to READ mode. ;---------------------------------------------------------------- ; aux$ird: ; Ensure Transmitter is EMPTY and time exists to shift out ; any buffered byte push b push d push psw ; lxi d,3 call ms$delay ; Delay for 3 milliseconds ; ino a,cntla0 ; Read port ani 1110$1111b ; RTS = 0 for READ outo cntla0,a ; pop psw pop d pop b ret ; ;---------------------------------------------------------------- ; Turn on the BELL ;---------------------------------------------------------------- ; set$bel: push psw di lda gp$dat ori 0001$0000b ; Mask bell bit high out gp$out sta gp$dat ; Save ei pop psw ret ; ;---------------------------------------------------------------- ; Turn off the bell ;---------------------------------------------------------------- ; clr$bel: push psw di lda gp$dat ani 1110$1111b ; Bell bit low out gp$out sta gp$dat ; Save ei pop psw ret ; ; Turn on the cash drawer bit. ; set$csh: push psw di lda gp$dat ori 1000$0000b ; Mask cash bit high (turn on) out gp$out sta gp$dat ; Save ei pop psw ret ; ; Turn the cash drawer bit off. ; clr$csh: push psw di lda gp$dat ani 0111$1111b ; Cash bit low out gp$out sta gp$dat ; Save ei pop psw ret ; ;---------------------------------------------------------------- ; Read the input switch port ; ; On Entry ; Nothing required ; ; On Exit ; A = switch image array. ;---------------------------------------------------------------- ; sch$rd: in ip$rd ret ; ;---------------------------------------------------------------- ; Clear the watchdog. ;---------------------------------------------------------------- ; clr$wdt: out wdt out wdt out wdt out wdt out wdt ret ; 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: mvi a,0110$0100b outo cntla1,a ; Write to A control port ; ; Setup the second ASCI control register mvi a,0000$0010b ; Prescale by total 640 = 9600 baud outo cntlb1,a ret ; ;---------------------------------------------------------------- ; 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: ino a,stat1 ; Status port read, channel 0 ani 0111$0000b ; Only look at error bits 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: ino a,stat1 ani 1000$0000b ; Look at received data ready flag ora a ; ZERO = no char, 1 = char 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: ino a,stat1 ; Read status ani 0000$0010b ; Look at tx-busy bit ora a 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 call con$ist jrz con$inp ; Wait for the key. ; ; Character ready. ino a,rdr1 ; Received data 1 ret ; ;---------------------------------------------------------------- ; 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 con$out$wait: call clr$wdt call con$ost jrz con$out$wait pop psw ; outo tdr1,a ; Send the data out to tx port 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 ; gp$dat ds 1 ; Last data at General purpose o/p port curatt db 00 curvid db 00 ; ; -- LCD Data bytes -- cmd$lcd: ds 1 ; Command to write to LCD ; ; err$num ds 1 lcd$x: ds 1 lcd$y: ds 1 ; ; -- LED Driver data -- ; led$ps ds 1 ; Interrupt call prescaler. led$chg ds 1 ; Flags ANY led changed fsh$cnt ds 1 ; Counts Flashing leds fsh$flg ds 1 ; Led flash state ; led$buf ds 66 ; One byte per led ; ticks ds 1 ; Ticks counted to get to a second sec ds 1 ; Seconds min ds 1 ; Hours hrs ds 1 ; Minutes ; end ; ; ;