1
0
Fork 0
qterm/source/QTERM.Z

903 lines
18 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; qterm.z - patch area and main control loop for qterm
.incl "c:vars"
.incl "c:version"
.var escchr '\\' & 0x1f
.var biosjp 1
.var memtop 6
.macro table byte,addr
dw addr
db byte
.endm
jp start ; skip over the patch area
; now for the screen access routines - these are put here because moveto
; will have to access scrout, and this provides a ready access point
#kbdsts:
jp 6 ; keyboard status routine
#kbdin:
jp 9 ; keyboard input routine
#scrout:
jp 12 ; screen output
jp decoj ; access to decimal output routine
; These bits are system dependant, and are put at the beginning of the
; program so that them as what needs to change them, can.
; modist - return with z flag clear iff there is a char waiting at modem port
.org 0x0010
#modist:
ld hl,(_base_)
ld a,(hl)
inc hl
xor (hl)
ret
; modin - read char from modem port: modist has been used to check it's there
.org 0x0020
#modin:
ld hl,(_base_)
inc (hl)
res 5,(hl)
ld e,(hl)
inc hl
inc hl
ld d,0
add hl,de
ld a,(hl)
ret
; modost - return with z flag clear iff the modem can accept another char
.org 0x0030
#modost:
in a,(0x2e)
and 0x80
ret
; modout - send char to modem port
.org 0x0040
#modout:
out (0x2f),a
ret
; sbreak - start a break condition on line
.org 0x0050
#sbreak:
ld a,0x0d
out (0x2e),a
ret
; ebreak - terminate break condition
.org 0x0060
#ebreak:
ld a,0x05
out (0x2e),a
ret
; dtroff - disable dtr to cause modem to hang up
.org 0x0070
.extern #dtroff ; external so hangup can work
#dtroff:
ld a,0x24
out (0x21),a
ret
; dtron - re-enable dtr
.org 0x0080
#dtron:
xor a
out (0x21),a
ret
; setbd - take byte in a from baud table, use it to set baud rate
.org 0x0090
#setbd:
out (0x2b),a
ret
; these next eight are byte pairs - the first byte is used by setbd above.
; the second is a -1 for an active baud rate entry, and a 0 for inactive
.org 0x00a0
.extern b38400
b38400: db 0,no
.extern b19200
b19200: db 0,no
.extern b9600
b9600: db 1,yes
.extern b4800
b4800: db 2,yes
.extern b2400
b2400: db 4,yes
.extern b1200
b1200: db 8,yes
.extern b600
b600: db 16,yes
.extern b300
b300: db 32,yes
; setmod - take a byte from the mode table, use it to set the uart mode
.org 0x00b0
#setmod:
out (0x2c),a
ret
; now the twelve mode bytes for setting comm format
.org 0x00c0
.extern modtab
modtab:
n17: db 0b10101010
n18: db 0b10001010
n27: db 0b10111010
n28: db 0b10011010
e17: db 0b10101110
e18: db 0b10001110
e27: db 0b10111110
e28: db 0b10011110
o17: db 0b10101100
o18: db 0b10001100
o27: db 0b10111100
o28: db 0b10011100
.org 0x00cc
.extern resrvd
resrvd:
db 0
; xfersz - number of K to read / write to disk during protocol transfers:
; must be 1 / 2 / 4 / 8. Generally this is best left at 8 unless you have
; a REALLY slow disk (C128 maybe) when writing / reading 8K at a time
; causes timeouts. Drop this to 4 or 2 to do disk access in smaller chunks
; to help avoid the timeout problem
.extern xfersz
xfersz: db 8
; speed - simply the cpu speed for a z80 in mhz.
.extern speed
speed: db 4
; escape - this is the character used as the escape char: since the addresses
; in the table tend to move, we just put the byte here, and then transfer
; to the table later
escape: db escchr
; the signon message - change this to be appropriate for your system
.org 0x00d0
signon: db 'Televideo TS803\0'
; now the string for clear screen
.org 0x00f0
.extern clrs
clrs: db 'z' & 0x1f, 0
; moveto - this routine is called with a word in hl - h = row &
; l = column to move to, at 109 is a routine to print a char in c,
; at 10c is a routine to print a decimal number in hl (for ansi tubes)
.org 0x0100
#moveto:
push hl ; save coords
ld c,'\e'
call #scrout ; lead in escape
ld c,'='
call #scrout ; leadin '='
pop hl
push hl
ld a,h ; row to a
call poff ; out it goes w/ offset
pop hl
ld a,l ; col to a
poff: add a,' ' ; add offset
ld c,a
jp #scrout ; & print it
; these next strings are used to do various screen functions. There are
; eight of them, and immediately preceding them is a flag byte. Each string
; has a bit in the byte, and if a capability is present, its bit should
; be set. This byte is an absolute necessity, as various programs use it
; to tell if various things are present.
.org 0x012f
.extern tcbits
tcbits:
db 0b11111111
; ||||||||
; |||||||+------ 0: bright (end highlight)
; ||||||+------- 1: dim (start highlight)
; |||||+-------- 2: delete line
; ||||+--------- 3: insert line
; |||+---------- 4: delete character
; ||+----------- 5: insert character
; |+------------ 6: clear to end of line
; +------------- 7: clear to end of screen
.org 0x0130
.extern brites
brites: db '\e(\0'
.org 0x0138
.extern dims
dims: db '\e)\0'
.org 0x0140
.extern dlstr
dlstr: db '\eR\0'
.org 0x0148
.extern ilstr
ilstr: db '\eE\0'
.org 0x0150
.extern dcstr
dcstr: db '\eW\0'
.org 0x0158
.extern icstr
icstr: db '\eQ\0'
.org 0x0160
.extern ceol
ceol: db '\eT\0'
.org 0x0168
.extern ceos
ceos: db '\eY\0'
; Entry and exit hooks. These are provided to perform custom initialisation
; on startup, and also to perform custom code on exit.
.org 0x0170
.extern entry
entry: jp do_ent
.org 0x0173
.extern exit
exit: jp do_ext
.org 0x0176
.extern #user
#user:
.if dg
jp _user_ ; go do ours
.else
ret
.endif
.org 0x0179
.extern #kbmap
#kbmap:
.if dg
jp _kbmp_ ; hook into keyboard map routine
.else
ret
.endif
.org 0x017c
_ilpr_: jp prmpjp
; finally a patch area that is provided for patching odd systems that need
; a lot of space.
.org 0x0180
do_ent:
.if dg
ld c,14
ld e,3
call 5
ld c,32
ld e,30
call 5
ld a,0b10001010 ; 8n1
out (0x2c),a
ld a,4 ; 2400
out (0x2b),a
.endif
di
ld hl,(0xff00)
xor a
ld (hl),a
ld (_base_),hl ; OK, use GSX interface area for interrupts
inc hl
ld (hl),a
ld (_here_ + 1),hl
ld de,33
add hl,de
ex de,hl
push de
ld hl,icode
ld bc,{endi - icode}
ldir
ld c,0x10
pop de
jr endit
do_ext: di
ld de,(ivec)
ld c,0
endit: ld a,i
ld h,a
ld l,0x78
ld a,(hl)
ld (hl),e
ld e,a
inc hl
ld a,(hl)
ld (hl),d
ld d,a
ld (ivec),de
ld a,0x65
out (0x28),a
ld a,c
out (0x20),a
out (0x27),a
ld a,0x64
out (0x28),a
ei
clrlp: in a,(0x2f)
in a,(0x2d)
add a,a
jr c,clrlp
ret
icode: di
push hl
push af
iclp: in a,(0x2d)
add a,a
jr nc,icdone
_here_: ld hl,0 ; filled in later
inc (hl)
res 5,(hl)
ld a,(hl)
inc hl
add a,l
ld l,a
jr nc,hok
inc h
hok: in a,(0x2f)
ld (hl),a
jr iclp
icdone: pop af
pop hl
ei
reti
endi:
ivec: dw 0
_base_: dw 0
.if dg
.var linesz 40 ; default number of chars before we start
_user_:
call _ilpr_ ; prompt for and get an answer
db 'Split? \0'
ld hl,0x7f ; point just before it
_byp: inc hl
ld a,(hl) ; get the next character
cp ' ' ; space?
jr z,_byp ; yes, skip over it
sub '0'
cp 10
jr c,_numbr ; numeric input - go handle it
add a,'0' ; convert back
or 0x20 ; force lower case
xor 0x20 ; this really tests for a null .....
jr z,_gtans ; yes, save zero byte
xor 0x20 ^ 'y' ; and check for a 'y' (including 'Y')
_gtans: ld (split),a ; save the flag - zero enables, non-zero
; disables
ld a,linesz ; set line length to default of 40
_setll: ld (linlen),a ; set the line length from a
xor a
ld (count),a ; and set the count to zero
ret
_numbr: ld e,a ; save current value in e
inc hl ; point to next character
ld a,(hl)
sub '0' ; convert .....
cp 10 ; and test if it's a digit
jr nc,_gtnum ; nope - we're all done
ld d,a
ld a,e
add a,a ; a = e * 2
add a,a ; * 4
add a,e ; * 5
add a,a ; * 10
add a,d ; + d
jr _numbr
_gtnum: xor a
ld (split),a ; else set split flag to enable
ld a,e ; and get the line length back from e
jr _setll ; go do the set with whatever we have
_kbmp_: and 0x7f ; ditch bit 7, just because I'm paranoid
_nodel: ld hl,split ; point at the flag
inc (hl)
dec (hl)
ld c,a ; save char in c
jp nz,_jstcn ; not splitting - get outa here
ld hl,(state)
ld a,h
or l ; are we in the middle of a line break?
jr nz,_linbr ; yes, so handle it elsewhere
ld a,c ; get the character back from c
ld hl,0x01cf ; point at the escape character
cp (hl)
ret z ; we have to let these through!
ld hl,count ; point hl at the count for everyone
cp '\b'
jr z,_bcksp ; handle backspaces
cp 'y' & 0x1f
jr z,_ctly ; and ^Y's
cp '\r'
jr z,_retrn ; and returns
cp '\n'
jr z,_newln ; and linefeeds
inc b ; set b to 1 for a potential swallow
cp 0x7f ; throw away deletes
ret z
cp ' ' ; is it a space?
jr z,_space ; handle specially if so
ret c ; and throw away other control characters
dec b ; reset b to pass the single character
inc (hl) ; bump count
ret ; and return - char is still in a
_bcksp: dec (hl) ; anything in the current count
inc b ; set b to 1 for a swallow
inc (hl) ; restore count
ret z ; and return if nothing there
dec (hl) ; decrement count since we're allowing it
dec b ; reset b
ret
_retrn: ld hl,retstr ; point at the return string
jr _linbr
_newln: ld hl,nlstr ; point at the new line string
jr _linbr
_ctly: ld hl,(count)
ld h,0 ; set state from count
or a
jr _setbs
_space: inc (hl) ; we passed another character, count it
ld a,(linlen) ; get the line length
cp (hl) ; too long yet?
ld a,c
dec b
ret nc ; no, return character in a
ld hl,string ; else point at the string to send
_linbr: ld a,h
or a
jr z,_sndbs ; if h is zero, send l backspaces
ld a,(hl) ; get the next character from the string
inc hl ; move the pointer
inc (hl)
dec (hl) ; end of string?
ld b,2 ; set b to 2 to say there's more coming
jr nz,_savhl ; not end of string, save hl and return
ld b,(hl) ; set b to zero again
ld hl,count
ld (hl),b ; zero out the count from b
ld h,b
ld l,b ; set hl to zero to reset the state machine
_savhl: ld (state),hl ; save the pointer / NULL
ret
_sndbs: dec l ; one less to go
_setbs: ld (state),hl ; save the state back
ld b,2 ; flag more to come
jr nz,_bok ; unless this is the last
ld b,h ; in which case b gets zero
ld (count),a ; set count to zero (a is zero from jp above)
_bok: ld a,'\b' ; stuff a backspace in a, and we're done
ret
_jstcn: ld hl,(state)
ld a,h
or l ; are we in the middle of a line break?
jr nz,_linbr ; yes, so handle it elsewhere
ld a,c
ld b,0
ld hl,count ; point hl at the count for everyone
cp '\b'
jr z,_cbksp ; count backwards
cp '\n'
jr z,_cntrt ; count return's - set to zero
cp '\r'
jr z,_cntrt
cp 'y' & 0x1f
jr z,_ctly ; handle ^Y's
cp ' '
ret c ; let control chars pass, but don't count them
inc (hl)
ret
_cntrt: ld (hl),b ; set count to zero
ret
_cbksp: dec (hl) ; anything in the current count
inc (hl)
ret z ; no, leave count alone
dec (hl) ; drop it back by one
ret ; all done
count: db 0 ; count of chars since the last new line
linlen: db linesz ; line size we're currently using
split: db 1 ; flag if we're splitting: zero => we are
state: dw 0 ; what state are we in?
string: db ' .....\r\0' ; space string to break lines
retstr: db ' [end]' ; return string to end a paragraph
nlstr: db '\r\0' ; newline to end
.endif
.org 0x0400
; put this jump here, because decout may move, so we can't have it's address
; in the patch area
decoj: jp decout
; same again: prompt may move
prmpjp: jp prompt
; now the start of the main code
start: ld sp,(memtop) ; set the stack
ld a,(biosjp + 1) ; get the bios base address
ld (#kbdsts + 2),a
ld (#kbdin + 2),a
ld (#scrout + 2),a ; fix the three jumps
call #subchk ; but now check for XSUB installed
ld a,(escape)
ld (escval),a ; set the escape character in the jump table
call setshr ; do shrink adjustment
ld a,(xfersz)
ld hl,512
sizlp: add hl,hl ; set size in K in hl
rra
jr nc,sizlp ; loop till size is set in hl
ld de,work2
add hl,de
ld (ew2p),hl ; and set pointer for transfers
ld hl,fktbl
ld de,fktbl + 1
ld bc,16 * 10 - 1
ld (hl),b
ldir ; clear fk table initially
call entry ; do the users custom code
ld sp,(memtop) ; reload the stack pointer in case things got
; bashed in the entry hook
call ilprt ; print fixed part of signon
db 'QTERM V'
version
db '\r\n'
db '(C) Copyright DPG '
year
db ' - All rights reserved\r\n'
db 'Version for \0'
call dim
ld hl,signon
call prtslp ; print the system signon message
call ilprt
db '\r\nEscape character is \0'
call dim
call presc ; remind what the escape character is
call crlf
call crlf ; throw a newline or two
ld a,(#dtroff)
cp 0xc9
call nz,dtron ; enable dtr
ld c,getdrv ; now, in case we changed drive / user in the
call bdos ; entry hook, we get the current drive / user
ld (curdrv),a
ld (chtdrv),a ; chat drive
ld c,gsuser
ld e,0xff
call bdos ; find out which user
ld (curusr),a ; save it away as well
ld (chtusr),a ; chat user
ld hl,0x80 ; get command tail
ld e,(hl) ; get it's length
ld (hl),' ' ; add a space so that byp will grok it
inc hl
ld d,h ; extend length to de
ex de,hl
add hl,de
ld (hl),d ; stick a zero byte on the end of it
ex de,hl ; base address back to hl
call byp ; was there anything there?
or a
call nz,ichat ; yes - go process it
; this loop provides terminal mode: it simply loops round polling the kdb &
; modem port - when it finds something, then it starts to get clever
.extern main
main: ld sp,(memtop) ; since we long jump here occasionally
ld hl,main
push hl ; push "here" so a return gets us back
ld a,lf_bit
ld (mode),a ; reset mode
call lstmod ; keep the printer going and check modem port
ld hl,(fkptr)
ld a,(hl)
or a ; sending a function key?
jr z,nofkey ; nope, go try the keyboard
call conin ; get the key
jr gotcon
nofkey: ld (prmpfl),a
ld a,(more)
rra ; should we get a keyboard character
jr c,skipin ; skip if not
call kbdsts ; keyboard ready?
or a
ret z ; no, loop back
call kbdin ; get the char
skipin: ld b,0 ; set b so a return is a no-op
call #kbmap ; run through the keyboard window
srl b
ret c ; swallowed, start again
gotcon: ld c,a ; save in c
ld a,b ; copy more bit to a
ld (more),a ; and save it
ld de,escstt
ld a,(de) ; what state are we in?
dec a
ld a,c ; get char back
jr z,hadesc ; we've had an esc char, handle next
ld hl,cschr
cp (hl)
call z,gotxof ; note passing xoff's
jr z,noxon
ld hl,lxoff
inc (hl)
dec (hl)
ld hl,escval
jr nz,noxon
cp (hl)
call nz,gotxon ; and xon's
noxon: cp (hl) ; test the char for an escape
jr nz,modopj ; no it's not, send it to the modem
ld hl,(fkptr)
ld a,(hl) ; in the midst of a pfk send?
or a
ld a,(escval) ; get an escape char back into a
modopj: jr z,gotesc ; yes - skip the usual escape
ld a,(wflg)
or a
ld a,c
call nz,limitb ; legal character?
ret c ; nope - exit now
modjp: jp modop
; gotesc - we just got the escape char in state 0: set state to 1
gotesc: ex de,hl
inc (hl) ; set state to 1
ret
; esctbl - table of special values for the escape handler
.dseg
esctbl: dw modop
.extern escval
escval: db escchr
table '?',help
table '.',break
table 0x2c,hangup
table 'B',baud
table 'C',catch
table 'D',dir
table 'E',echo
table 'H',hdxtog
table 'I',info
table 'J',jctog
table 'K',ldfnk
table 'L',lftog
table 'M',msbtog
table 'N',newdsk
table 'O',optog
table 'P',print
table 'Q',quit
table 'R',recv
table 'S',send
table 'T',type
table 'U',#user
table 'V',vttog
table 'W',witog
table 'X',chat
table 'Y',hold
table 'Z',cclose
tblend:
.cseg
; hadesc - we are in state 1 and a char arrived: process it as appropriate
hadesc: ex de,hl
dec (hl) ; reset state
nowjp: ld a,c
ld hl,esctbl ; get address of table
ld b,{tblend - esctbl} / 3
call ucsa ; force it upper case
escscn: ld e,(hl)
inc hl
ld d,(hl) ; get jump address to de
inc hl
cp (hl) ; test for a match
inc hl
push de
ret z ; got it: go and process
pop de
djnz escscn ; loop back & try again
nowpop: ld a,c ; get char from c (again)
sub '0'
cp 10 ; check for '0' thru '9'
ret nc ; didn't find it - give up
add a,a
add a,a
add a,a
add a,a ; * 16 gives table offset
ld l,a
ld h,0
ld de,fktbl
add hl,de ; index into table
ld a,(hl) ; pick up
ld (fkdely),a ; and save delay flag
inc hl ; point at main string
ld (fkptr),hl ; save string address
ret
.dseg
escstt: db 0 ; state of escape detection
more: db 0 ; keyboard window code
; V4.1e rev
;
; we now save ix and iy through all patch and bios calls: these entries
; are used, and do all the work needed
.cseg
.macro access entry
.extern entry
entry:
push ix
push iy
call #`entry
pop iy
pop ix
ret
.endm
access kbdsts
access scrout
access modist
access modin
access modost
access modout
access sbreak
access ebreak
access dtroff
access dtron
access setbd
access setmod
access moveto
.extern kbdcc ; get a keyboard character if it's waiting
kbdcc:
call kbdsts
or a
ret z ; return 0 if nothing
access kbdin
.var #bdos 5
.extern usrbds ; call bdos, but with a user switch
usrbds:
push bc ; save the function code
ld a,(de) ; get the user number to switch to
inc de ; point de at bdos's idea of the fcb
push de ; save the address
ld e,a
ld c,gsuser
call bdos ; set the user number
pop de ; restore the fcb address
pop bc ; and function number
.extern bdos
bdos:
push ix
push iy
call #bdos
pop iy
pop ix
ret
.extern #subchk
#subchk:
ld hl,(1)
ld a,l
ld de,3
xor e
jr nz,chngit
ld l,a
ld b,6
jplp: ld a,(hl)
cp 0xc3
jr nz,chngit
add hl,de
djnz jplp
ret
chngit: ld hl,jptab
ld de,0x0103
ld bc,9
ldir
ret
jptab: jp locst
jp locin
jp locout
locst: ld hl,peekc
ld a,(hl)
.dseg
peekc: db 0
.cseg
or a
ret nz
ld c,6
ld e,-1
push hl
call 5
pop hl
ld (hl),a
or a
ret
locin: call locst
jr z,locin
ld (hl),0
ret
locout: res 7,c
ld e,c
ld c,6
jp 5