home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
utils
/
asmutl
/
hd64180a.lbr
/
EXCORE.AZM
/
EXCORE.ASM
Wrap
Assembly Source File
|
1991-08-04
|
43KB
|
1,984 lines
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
;
;