home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
TERM
/
Z19.ASM
< prev
Wrap
Assembly Source File
|
2000-06-30
|
23KB
|
970 lines
; Z-19.ASM Z-19 emulator for the SSM VB3 video card
;
; Version 1.0 Released 82.4.18
; Copyleft (L) Scott W. Layson. All rights reversed.
; This code is in the public domain.
;
; This code performs approximate (but usually adequate) emulation
; of the Heath/Zenith Z-19 terminal on the SSM VB3 video card. It
; supports most of the Z-19 features required for normal text editing.
; About the only useful thing missing is the special 25th line (49th?).
;
; Things to note about this code:
; -- The indentation in this file looks so strange because I edit with
; five-column instead of eight-column tabs.
; -- Sections of this code, especially the relatively low-level routines,
; are not well commented. Sorry 'bout that.
; -- The code uses Intel mnemonics, but there are a couple of Z-80
; block moves inserted with `db's. If you don't have a Z-80, you'll
; have to replace these with 8080 loops that do the same thing.
; -- This file requires MAC for assembly as it stands, though it might
; not be very hard to make it ASM-compatible.
; -- It assumes that the video controller has already been initialized
; (to 48 lines, but you can change this in the block of code below).
; -- There's no keyboard I/O code in this file.
; -- I have to turn the lower 48K of my main memory off, because my
; ExpandoRam II doesn't respond to PHANTOM* on write. You will
; presumably have to change or remove this code; it's all right
; near the beginning.
; -- This code uses a fair amount of stack space; I don't know exactly
; how much. Because of this and because I'm turning most of main
; memory off, I use a private stack at the top of memory. I recommend
; you do likewise if possible.
; -- I normally use this code in "roll mode" rather than "scroll mode":
; after the last line on the screen is printed, the cursor is placed
; on the top line, which is then cleared. The screen is never scrolled.
; I find this more comfortable on a P39 monitor, where scrolling leaves
; "trails". The Z-19, of course, supports only "scroll mode". The
; defaults for this and other modes are set in the `db's at the end
; of the file.
; -- The behavior of this code when it gets an insert- or delete- line
; command is a little weird. Instead of doing the insertion or
; deletion immediately, it counts the number of successive insertions
; or deletions and only executes them when it gets some other kind
; of command. Sometimes this means that when you give an insert-line
; or delete-line command to your editor, nothing happens until you
; type another character. The insert-n-lines and delete-n-lines
; commands, on the other hand, are executed immediately.
; -- Consider: when you put a terminal into inverse video mode and give
; it a clear command of some sort (clear-to-end-of-line, home-and-clear-
; screen), should it clear to inverse or normal spaces? The Z-19 clears
; to normal spaces; this code, like most other "smart" terminals these
; days, clears to inverse spaces. Likewise for the other video
; attributes it supports.
; -- The "graphics" mode accesses the user-definable font ROM. Obviously,
; for emulation of Z-19 graphics mode, you must burn a ROM with that
; character set.
;
; Here is a list of control characters and escape sequences the code
; currently supports. A `*' marks sequences the Z-19 doesn't have.
;
; BS
; TAB
; CR
; LF
; Esc @ Enter insert-char mode
; Esc A Cursor up
; Esc B Cursor down
; Esc C Cursor forward
; Esc D Cursor backward
; Esc E Home and clear
; Esc F Enter graphics mode
; Esc G Exit graphics mode
; Esc H Home
; Esc J Clear to end of screen
; Esc K Clear to end of line
; Esc L Insert line
; Esc M Delete line
; Esc N Delete char
; Esc O Exit insert-char mode
; Esc Y <r> <c> Cursor pos
; *Esc l <n> Insert n lines
; *Esc m <n> Delete n lines
; Esc p Enter inverse video
; Esc q Exit inverse video
; Esc x/y 5 Turn cursor off/on
; *Esc x/y * Enter/exit scroll mode
; *Esc x/y A Enter/exit inverse video
; *Esc x/y B Enter/exit hide-char mode
; *Esc x/y C Enter/exit underline mode
; *Esc x/y D Enter/exit blink mode
; *Esc x/y E Enter/exit strike-thru mode
; *Esc x/y F Enter/exit dim mode
; *Esc x/y @ Set/clear all video modes
; Esc v Wrap mode on
; Esc w Wrap mode off
;
; And now the code itself.
vnormal equ 3 ; standard character attribute code
vinverse equ 4 ; inverse video bit
bs equ 08h
tab equ 09h
lf equ 0ah
cr equ 0dh
esc equ 1Bh
vkbstat equ 0e0h ; video keyboard status port
vkbdata equ 0e1h ; video keyboard data port
vc equ 0d0h ; video controller registers:
vhcount equ vc + 0 ; char times per scan
vhsync equ vc + 1 ; interlace(1), hsp(4), hbp(3)
vchars equ vc + 2 ; 0(1), scans/char(4), chars/line(3)
vlines equ vc + 3 ; lines/frame
vscans equ vc + 4 ; scans/frame
vvsync equ vc + 5 ; vertical scan delay
vscrol equ vc + 6 ; scroll register
vcolin equ vc + 9 ; cursor column in
vcolout equ vc + 12 ; cursor column out
vrowin equ vc + 8 ; cursor row in
vrowout equ vc + 13 ; cursor row out
vncols equ 80 ; number of cols
vnrows equ 48 ; number of rows
vvideo equ 2000h ; address of video memory
voffset equ 1000h ; offset to attributes
ramcard equ 0FFh ; ExpandoRam bank-switch port
ramoff equ 1 ; to turn main memory off
ramon equ 0 ; to turn memory back on
org 0F810h
; Main entry point. The character is in C.
h19 push h ; save registers
push d
push b
lxi h, 0
dad sp ; get stack pointer
lxi sp, 0 ; move stack to high memory
push h ; save old SP
mvi a, ramoff ; turn main memory off
out ramcard
out vkbstat ; enable VB3
call curoff ; turn cursor off
call process ; process char
call curon ; cursor back on
out vkbdata ; disable VB3
mvi a, ramon ; turn main memory back on
out ramcard
pop h ; recover old SP
sphl ; and put the stack back where it was
pop b ; restore registers
pop d
pop h
ret ; and done!
process lda escmode ; are we in an escape sequence?
ora a
jnz escseq ; yes: go interpret this char
mov a, c
cpi esc ; test for esc before checking insdelcnt
jz doesc ; in case we're getting another ins or del
lda insdelcnt ; any saved line insertions or deletions to do?
ora a
cnz doinsdel ; yes: do them first
mov a, c ; check for the known control chars
cpi cr ; (others are displayed)
jz docr
cpi lf
jz dolf
cpi bs
jz dobs
cpi tab
jz dotab
; we have a displayable character.
display lda insmode ; are we in insert-char mode?
ora a
cnz inschar ; yes: move rest of line over first
lda attrib ; get current attribute byte
mov b, a ; set up for putchar
call putchar
call right ; move cursor right
rnz ; done if no wrap
lda wrapp ; are we in wrap mode?
ora a
rz ; no: return
jmp nextline ; wrap occurred
; write the char in c, attribute in b, to the video memory.
putchar push h
call addr ; get memory address of char
mov m, c ; store char
lxi d, voffset
dad d ; address of attribute
mov m, b ; store attribute
pop h
ret
nextline mvi l, 0 ; move to col. 0
; move the cursor down one line. Clear the new line
; if the cursor was on the last logical line. Scroll the screen
; if in scroll mode.
dolf lda scrollp ; are we in scroll mode?
ora a
jnz dolfscr ; yes: go do the right thing
dolfnscr lda lastlrow ; do lf in non-scroll mode:
cmp h ; are we moving off the last logical row?
push psw ; save the answer to that question
call down ; move down
jnz dolf3
mvi h, 0 ; wrap to top of screen
dolf3 pop psw ; are we moving off the last row?
rnz ; no: done
call nextlrow ; increment last-logical-row
jmp dolf2
dolfscr call down ; do lf in scroll mode
rnz ; not moving off bottom: done
call nextlrow ; increment last-logical-row
out vscrol ; scroll screen
sta lastprow ; set last-physical-row
dolf2 push h
mvi l, 0
call cleol ; clear the new line
pop h
ret
nextlrow lda lastlrow ; increment last-logical-row
inr a
cpi vnrows ; modulo vnrows
jnz nextl1
xra a
nextl1 sta lastlrow ; store result
ret
docr mvi l, 0 ; CR: set col to 0
ret
dobs jmp left ; BS: move cursor left
dotab mov a, l ; TAB: move to current col...
ani 0F8h ; ... modulo 8 ...
adi 8 ; ... plus 8
cpi vncols
jz right ; except near edge of screen
mov l, a
ret
; turn the cursor off. Returns logical cursor address in HL.
curoff lhld curaddr ; get logical cursor address
mvi a, 0FFh
out vcolout ; move cursor off screen
ret
; turn the cursor on. Called with logical cursor address in HL.
curon shld curaddr ; save logical cursor address
lda curoffp
ora a
rnz ; if cursor turned off, leave it off screen
mov a, l ; set column
out vcolout
lda lastprow ; set row relative to last physical row
inr a
add h
cpi vnrows
jc curon1
sui vnrows
curon1 out vrowout
ret
; move the cursor left, if possible. Returns Z iff at left edge.
left mov a, l ; get col
dcr l
ora a
rnz ; R(not at left edge)
mov l, a ; force col. 0
ret
; move the cursor right, if possible. Returns Z iff at right edge.
right inr l
mvi a, vncols
cmp l
rnz ; R(not at right edge)
mvi l, vncols - 1 ; can't just dcr, cuz it clears Z!
ret
; move the cursor down, if possible. Returns Z iff at bottom.
down inr h
mvi a, vnrows
cmp h
rnz ; R(not at bottom)
mvi h, vnrows - 1 ; can't just dcr, cuz it clears Z!
ret
; move the cursor up, if possible. Returns Z iff at top.
up mov a, h
dcr h
ora a
rnz
mov h, a
ret
; clear to end of line.
cleol mov d, h ; set de to end of line
mvi e, vncols - 1
mvi c, ' ' ; space character in c
lda attrib ; current attribute in b
mov b, a
jmp fills ; and do it!
; home and clear.
hcl call home
jmp cleow
; cursor home.
home lxi h, 0
ret
; clear to end of window (screen).
cleow lda lastprow
sta lastlrow ; set lastlrow to bottom of screen
mvi d, vnrows - 1 ; set de to end of screen
mvi e, vncols - 1
mvi c, ' ' ; space char in c
lda attrib ; current attribute in b
mov b, a
jmp fills ; and do it!
; insert a character at the cursor.
inschar push b
push h
mvi a, vncols - 1 ; how many chars to move?
sub l
jz insch1 ; none: skip
mov c, a
mvi b, 0 ; # chars in bc
push b ; and save it
mvi l, vncols - 1 ; set hl to end of line
call addr ; get starting address of move
push h
mov d, h ; dest in de
mov e, l
dcx h ; source in hl
; lddr ; and move!
db 0EDh, 0B8h ; Z80 instruction
pop h ; address
pop b ; byte count
lxi d, voffset ; now do attributes
dad d
mov d, h ; just like before
mov e, l
dcx h
; lddr
db 0EDh, 0B8h ; another Z80 instruction
insch1 pop h
pop b
ret
; delete a character at the cursor.
delchar push b
push h
mvi a, vncols - 1 ; get # of chars to move
sub l
jz delch1 ; none: skip
mov c, a
mvi b, 0 ; # chars in bc
push b
call addr ; get starting address
push h
mov d, h ; de = dest
mov e, l
inx h ; hl = source
; ldir ; and move!
db 0EDh, 0B0h ; Z80 instruction
pop h ; address
pop b ; byte count
lxi d, voffset ; now do attributes
dad d
mov d, h ; just like before
mov e, l
inx h
; ldir ; and move!
db 0EDh, 0B0h ; Z80 instruction
delch1 pop h ; get current row, col back
push h
mvi l, vncols - 1 ; set up to clear last char in line
mvi c, ' '
lda attrib
mov b, a
call putchar ; do it
pop h
pop b
ret
; delete the line containing the cursor.
delline mvi c, 1 ; and fall through
; delete <n> lines, starting with the one containing the cursor.
; <n> is in C.
delnlines push h
mov a, h
add c ; other end of region to be deleted
cpi vnrows
jm deln1
mvi a, vnrows
deln1 mov l, a ; h = first dest, l = first source
mvi a, vnrows
sub l
mov b, a ; b = no. of lines to move
call moveblk
mvi a, vnrows
sub c ; c = no. of lines to clear
mov h, a ; h = first row to clear
call clrblk
pop h
mvi l, 0 ; move to beginning of line
ret
; insert a line where the cursor is.
insline mvi c, 1 ; and fall through
; insert <n> lines before the line containing the cursor.
; <n> is in C.
insnlines push h
mov a, h
mov l, h
add c ; other end of region to be inserted
cpi vnrows
jm insn1
mvi a, vnrows
insn1 mov h, a ; h = first dest, l = first source
mvi a, vnrows
sub h
mov b, a ; b = no. of lines to move
call moveblk
mov h, l ; h = first row to clear
call clrblk ; c = no. of lines to clear
pop h
mvi l, 0 ; move to beginning of line
ret
; move a block of B lines from row L to row H.
moveblk push h
push d
push b
mov a, l
cmp h
jm movbrev
movbfwd mov d, h
mov h, l
movbfwd1 mov a, b
ora a
jz movbret
dcr b
call movelin
inr h
inr d
jmp movbfwd1
movbrev mov a, h
add b
mov d, a ; d = h + b
mov a, l
add b
mov h, a ; h = l + b
movbrev1 mov a, b
ora a
jz movbret
dcr b
dcr h
dcr d
call movelin
jmp movbrev1
movbret pop b
pop d
pop h
ret
; clear C lines starting at H.
clrblk push h
push d
push b
mvi l, 0
clrblk1 mov a, c
ora a
jz clrblk2
push b
call cleol
pop b
inr h
dcr c
jmp clrblk1
clrblk2 pop b
pop d
pop h
ret
; move a line from row H to row D.
movelin push h
push d
push b
mvi l, 0
mov e, l
lxi b, voffset
call addr
push h
dad b
xchg
call addr
push h
dad b
xchg
lxi b, vncols
; ldir
db 0EDh, 0B0h ; Z80 instruction
pop d
pop h
lxi b, vncols
; ldir
db 0EDh, 0B0h ; Z80 instruction
pop b
pop d
pop h
ret
; get physical address from logical address.
addr push b
lda lastprow
inr a
add h
cpi vnrows
jc addr1
sui vnrows
addr1 mov c, a
mvi b, 0
mov a, l ; save col
lxi h, rowtab
dad b
dad b
mov c, a ; get col back
mov a, m ; look row up in table
inx h
mov h, m
mov l, a
dad b ; add col
pop b
ret
rowtab
j set vncols
i set 0
rept vnrows
dw vvideo + j * i
i set i + 1
endm
; fill the screen with the data in C, the attribute in B
; from x, y location HL through DE.
fills push h
call addr
xchg
call addr
mov a, h
cmp d
jnz fills1a
mov a, l
cmp e
fills1a xchg
jnc fills1 ; J(area to fill doesn't wrap)
push d
push b
lxi d, vvideo + vnrows * vncols - 1
call fills2
pop b
pop d
lxi h, vvideo
fills1 call fills2
pop h
ret
fills2 push b
push h
push d
call fill ; fill in the data
pop h
pop d
lxi b, voffset
dad b
xchg
dad b
pop b
mov c, b
call fill ; fill in the attributes
ret
fill mov m, c ; put down first copy
mov a, e
sub l
mov c, a
mov a, d
sbb h
mov b, a ; bc = de - hl = no. bytes to move
ora c
rz ; R(nothing to do -- only one byte to fill)
mov d, h ; hl = source
mov e, l
inx d ; de = dest
; ldir
db 0EDh, 0B0h ; Z80 instruction
ret
; turn on escape mode.
doesc mvi a, stesc
sta escmode
ret
;
; These are the various escape-states we can be in. They indicate
; what part of an escape sequence we've seen already.
stesc equ 1 ; Esc
stcprow equ 2 ; Esc Y
stcpcol equ 3 ; Esc Y <row>
stsetmode equ 4 ; Esc x
stclrmode equ 5 ; Esc y
stinsn equ 6 ; Esc l
stdeln equ 7 ; Esc m
; We get here if we're in the middle of an escape sequence.
; escmode is in A.
escseq push psw
xra a
sta escmode ; clear escape mode here, for convenience
pop psw
cpi stcprow ; row byte?
jz docprow
cpi stcpcol ; col byte?
jz docpcol
cpi stsetmode ; set-mode byte?
jz dosetmode
cpi stclrmode ; clear-mode byte?
jz doclrmode
cpi stinsn ; insert-n-lines byte?
jz doinsn
cpi stdeln ; delete-n-lines byte?
jz dodeln
mov a, c
cpi 'L' ; insert (1) line?
jz doinslin
cpi 'M' ; delete (1) line?
jz dodellin
lda insdelcnt ; for anything else: do any saved
ora a ; insertions/deletions first
cnz doinsdel
mov a, c
cpi 'Y' ; cursor pos?
jz docp
cpi 'K' ; clear to end of line?
jz docleol
cpi 'E' ; home and clear screen?
jz dohcl
cpi 'H' ; home?
jz dohome
cpi 'J' ; clear to end of screen?
jz docleow
cpi 'A' ; cursor up?
jz doup
cpi 'B' ; cursor down?
jz dodown
cpi 'C' ; cursor right?
jz doright
cpi 'D' ; cursor left?
jz doleft
cpi '@' ; set char-insert mode?
jz inschon
cpi 'O' ; clear char-insert mode?
jz inschoff
cpi 'N' ; delete char?
jz dodelchar
cpi 'l' ; insert n lines?
jz doinsnlins
cpi 'm' ; delete n lines?
jz dodelnlins
cpi 'p' ; set inverse video?
jz doinvon
cpi 'q' ; clear inverse video?
jz doinvoff
cpi 'x' ; set mode?
jz setmode
cpi 'y' ; clear mode?
jz clrmode
cpi 'v' ; set wrap mode?
jz dowrapon
cpi 'w' ; clear wrap mode?
jz dowrapoff
cpi 'F' ; set graphics mode?
jz dografon
cpi 'G' ; clear graphics mode?
jz dografoff
escfail push b ; not any recognized command. display
mvi c, esc ; sequence literally so user can see what
call display ; happened
pop b
call display
ret
docp mvi a, stcprow ; cursor pos command: set esc mode
sta escmode
ret
docprow mov a, c ; get row byte
sui 32 ; subtract bias
mov h, a
mvi a, stcpcol ; set new esc mode
sta escmode
lda curoffp
inr a ; cursor off during CP
sta curoffp
ret
docpcol mov a, c ; get col byte
sui 32 ; subtract bias
mov l, a
lda curoffp
dcr a ; cursor back on (unless it was already off)
sta curoffp
ret
docleol equ cleol
dohcl equ hcl
dohome equ home
docleow equ cleow
doup equ up
dodown equ down
doright equ right
doleft equ left
inschon mvi a, 1 ; set insert-char mode
sta insmode
ret
inschoff xra a ; clear insert-char mode
sta insmode
ret
dodelchar equ delchar
doinslin lda insdelcnt ; accumulate insertions
ora a
push psw
cm doinsdel ; do any saved deletions first
pop psw
inr a ; then increment insdelcnt
sta insdelcnt
ret
dodellin lda insdelcnt ; accumulate deletions
dcr a
push psw
cp doinsdel ; do any saved insertions first
pop psw
sta insdelcnt
ret
doinsnlins mvi a, stinsn ; set escmode to expect no. of insertions
sta escmode
ret
dodelnlins mvi a, stdeln ; set escmode to expect no. of deletions
sta escmode
ret
doinsn equ insnlines
dodeln equ delnlines
doinsdel lda insdelcnt ; do saved insertions/deletions
ora a ; any to do?
rz ; no: done
push b
jm doinsdel1 ; pos: insertions; neg: deletions
mov c, a ; insertion was saved
call insnlines ; do it
jmp doinsdel2
doinsdel1 cma
inr a
mov c, a ; deletion was saved
call delnlines ; do it
doinsdel2 pop b
xra a
sta insdelcnt ; clear saved count
ret
doinvon lda attrib ; inverse video on
ori vinverse
sta attrib
ret
doinvoff lda attrib ; inverse video off
cma
ori vinverse
cma
sta attrib
ret
setmode mvi a, stsetmode ; set escmode to expect mode to set
sta escmode
ret
clrmode mvi a, stclrmode ; set escmode to expect mode to clear
sta escmode
ret
; come here with a mode to set in C
dosetmode mov a, c
cpi '5'
jz setcuroff ; turn cursor off
cpi '@'
jnc setattr ; set display attributes
cpi '*'
jz setscrol ; set scroll mode
push b
mvi c, esc ; display unimplemented sequence, so
call display ; user can see what happened
mvi c, 'x'
call display
pop b
call display
ret
setcuroff mvi a, 1 ; turn cursor off
sta curoffp
ret
setattr call attrbit ; get bit for this attribute
lda attrib ; or it into current attribute byte
ora e
sta attrib
ret
setscrol lda scrollp ; set scroll (Z-19 normal) mode
ora a
rnz ; already on
mvi a, 1
sta scrollp
mvi h, vnrows - 1
lda lastlrow ; get last logical row
out vscrol ; make it last physical row
sta lastprow
ret
; come here with a mode to clear in C
doclrmode mov a, c
cpi '5'
jz clrcuroff ; turn cursor back on
cpi '@'
jnc clrattr ; clear an attribute
cpi '*'
jz clrscrol ; turn roll mode back on
push b
mvi c, esc ; display unimplemented sequence
call display ; so the user can see what happened
mvi c, 'y'
call display
pop b
call display
ret
clrcuroff xra a ; turn cursor back on
sta curoffp
ret
clrattr call attrbit ; get bit for attribute
lda attrib
cma
ora e ; clear it in current attr. byte
cma
sta attrib
ret
clrscrol lda scrollp ; set "roll" mode: no scrolling
ora a
rz ; scroll mode already off
xra a
sta scrollp
lda lastlrow
mov h, a
mvi a, vnrows - 1 ; return screen to 0-origin
out vscrol
sta lastprow
ret
; given command char in A, leaves attribute bit set in E
attrbit sbi '@'
jz attrbit1
mov e, a
mvi a, 2 ; start with 2
attrbit2 rlc ; rotate left
dcr e ; the right number of times
jnz attrbit2
mov e, a
ret
attrbit1 mvi e, 0FCh ; '@': all attributes
ret
dowrapon mvi a, 1 ; turn wrap mode (end-of-line wrapping) on
sta wrapp
ret
dowrapoff xra a ; turn wrap mode (end-of-line wrapping) off
sta wrapp
ret
dografon lda attrib ; turn graphics mode on (enable ROM)
ani 0FDh
sta attrib
ret
dografoff lda attrib ; turn graphics mode off (disable ROM)
ori 2
sta attrib
ret
;
; Data section. If you want to change the default modes (e.g., to
; wrapping and scrolling), this is the place to do it. (Just change
; wrapp and scrollp.)
;
curaddr dw 0 ; logical address of cursor
lastprow db vnrows - 1 ; physical last row
lastlrow db vnrows - 1 ; logical last row
attrib db 3 ; character attribute byte
escmode db 0 ; escape-sequence mode
wrapp db 0 ; wrap at end of line?
insmode db 0 ; insert-character mode
scrollp db 0 ; scroll mode
curoffp db 0 ; cursor-off mode
insdelcnt db 0 ; insert/delete line count
; End of Z-19.ASM -- Z-19 Emulator for SSM VB3