home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
kermit
/
cp411src.ark
/
cpsutl.asm
< prev
next >
Wrap
Assembly Source File
|
1991-08-27
|
42KB
|
1,228 lines
; CPSUTL.ASM
; KERMIT - (Celtic for "FREE")
;
; This is the CP/M-80 implementation of the Columbia University
; KERMIT file transfer protocol.
;
; Version 4.0
;
; Copyright June 1981,1982,1983,1984,1985
; Columbia University
;
; Originally written by Bill Catchings of the Columbia University Center for
; Computing Activities, 612 W. 115th St., New York, NY 10025.
;
; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
; others.
;
; Utility routines, pure and impure data.
;
; revision history:
;
;edit 31, 21-Mar-1991 by MF. Implement edit 30 without checking takflg after
; "r1tch1" as we are **always** TAKEing from a file if we get to that
; point in the code. Makes for simplicity.
;edit 30, 27-Feb-1991 by MF. When TAKEing characters from a TAKE-file,
; view semicolons as normal characters (not command separators).
; This will allow such commands as REMOTE DELETE *.*;* to work
; properly from TAKE-files without having to revert to old code in
; cpsrem.asm at "remcl0" to decode Remote command arguments. TAKE-files
; ought not (in my opinion) to have multiple commands per line anyway.
;edit 29, 30-Jan-1991 by MF. Fix bug in IN1CHR which decremented "chrcnt"
; once too often after a call to INBUF (which predecrements it
; already). This, along with a fix in CPSTT.ASM, fixes a bug in the
; TRANSMIT command wherein certain characters in the file were not
; being transmitted. This bug was reported to me by Lance Tagliapietra
; of the University of Wisconsin at Platteville, WI (Email:
; <TAGLANCE@ucs.UWPLATT.EDU>).
; (he suggested not predecrementing "chrcnt" in INBUF" but this
; breaks code in routine "GTCHR" from CPSPK1.ASM so it's better
; to modify IN1CHR in this module and XMIT in CPSTT.ASM).
;edit 28, 30-Nov-1990 by MF. Modify routine "p20ln" to use "pausit" routine
; rather than explicitly checking for Console input to eliminate
; redundant code. Also fix spelling in "p20ln"'s comments.
;edit 27, 9-Sep-90 by MF. Put RET in routine PAUSIT per CPKERM.BWR.
; edit 26, September, 1987. Added pause-it routine to wait for a user keysroke.
;
; edit 25, August 19, 1987 by OBSchou. Fixed a few bugs here and there.
;
; edit 24, April 8, 1987 by OBSchou. Added routine to return one character
; from a section of several sectors worth of file. This routine needed
; for TRANSMIT.
;
; edit 23, March 11, 1986 by OBSchou for Richard Russell
; Bug in the TAKE code, such that a new sector was read in after 256
; bytes, and not the CPM value. A jnz is now jp in the test to see if
; the file buffer has bben exhausted. Many thanks for finding this
; bug. I have never used TAKE files more than 128 bytes long.
;
; edit 22, January 28, 1986 by OBSchou.
; split off the data areas from CPSUTL to CPSDAT.ASM (All in line
; with keeping individual files relatively small)
;
; edit 21 August, 1986 by OBSchou. Sorted a few more bugs in printer buffer
; etc. Have yet to try this with a real printer The code, apart from
; actually printing works OK.
;
; edit 26 20 August, 1986 by OBSchou for Godfrey Nix:
; edit 8-Aug-86 by Godfrey N. Nix [gnn] Nottingham University
; Added two extra bytes for storage of the send and receive
; start-of-packet characters. Used by CP4PKT, and altered
; by SET option in CP4MIT. Also added message strings for
; use by show routines. Added remote filename buffer and length byte.
;
; edit 11: June 20, 1986 by OBSchou. Added multi-fcbs for the DIR command
; together with some bug clearing and new routines. Had to move
; the overlay to 5000h as we ran out of space...
;
; edit 10: June 16, 1986 OBSchou. Added a pseudo clock and check for printer
; ready whenever one enters BDOS... This may slow things down a little
; but adds in (hopefully) pseudo background printing...
;
; edit 9 30-May-86 OBSchou. Added XON/XOFF routines here for the world
; at large to use. Also added two new entries in the overlay tables.
; One to give the address of the family of machines using the overlay,
; the other to the routine for giving printer status.
;
; edit 8: 27-May-86 OBSchou. Added code to check BDOS calls for info from
; the console. If so, and the take flag (takflg) is set then we
; substitute our own characters. Simple, a little tatty...
; Also added bits for SET CASE-SENSITIVE and SET FLOW=CONTROL, and
; removed the XMIT rubbish. This is a prelude to better TRANSMIT
;
; edit 7: 22 April, 1986 by OBSchou Lohghborough University
; Prlude to more changee, this time make overlay address to 4000h
; May revert back to ($+0ffh) AND 0ff00h as address for overlay.
; This gives us space to make quite a few modifications to the system
; dependent part without much fear of having to change this overlay
; address. Should also fix the Osborne problem of having to have io
; routines ii memory above 16k. I know I should not be introducing
; such system dependent rot here, but it wont be too difficult to fill
; memory to 4000h.
;
; edit 6: February 6, 1985
; Added a storage location for the port value (PORT, just below
; SPEED) which is used by the port status routine, and moved the
; printer copy flag (PRNFLG:) into the communications area so
; that the machine dependant overlay can toggle it. [Hal Hostetler]
; Added ffussy flag for filename checking. Generate the version
; string from 'verno', which is set in CP4KER, because CP4KER has the
; list of modules and their edit numbers. [Charles Carvalho]
;
; edit 5: 13-Jan-85 by Vanya J.Cooper Pima Commun. College Tel: 602-884-6809
;
;pcc002 28-Dec-84 modules:cp4tt,cp4utl
; Add connect mode <esc>P command to toggle printer on
; and off. Conflicts with "official" recommended commands
; in protocol manual, but I don't think CP/M will ever get
; a PUSH command.
;
;pcc003-pcc005 2-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
; These edits must all be installed together and change the way
; logging is handled. The log file spec is moved to a separate
; fcb, and not opened until an actual CONNECT command is given.
; This takes care of a NASTY bug that if you used any other file
; command between the LOG and CONNECT, the log file would get
; written over the last file used. This also allows logging to
; be "permanently" enabled until an CLOSE (new command) for all
; CONNECT sessions, like most other kermits do. If a log file
; already exists, it will be appended to. Also add two new
; CONNECT mode commands <esc>Q to suspend logging and <esc>R to
; resume. <esc>R means something else during TRANSMIT, but
; logging is never on then, so there shouldn't be any conflict.
; I also changed the write code, so that it can handle one more
; character after the XOFF is send to stop the host. This allows
; a little "slop" for systems that don't stop immediately (such
; as TOPS10), but it didn't help much.
;
;pcc006 2-jan-85 VJC modules:cp4cmd,cp4utl
; Problems with "?" in filespecs. On reparse, may cause action
; flag to be reset at wrong point, requiring multiple <CR>'s
; to terminate the line or other weird stuff. Also need to
; check flag and complain if wild-cards illegal.
;
;pcc008 2-Jan-85 vjc modules:cp4def,cp4tt,cp4utl
; Keyboard input during CONNECT mode can get locked out if
; there is enough input from the modem port to keep prtchr
; busy. This can happen for example, if the printer is running
; at the same speed as the modem line, leaving you helpless to
; turn it off or abort the host. Add a fairness count, so that
; at least every prfair characters we look at console input.
;
;pcc012 4-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
; Use the big buffer for the log file. Move the log file back
; into the common fcb and only save the drive, name, and
; extension between connects. Add new routines to cp4utl to
; create or append to an existing file, and to conditionally
; advance buffers only if in memory. Remove edit pcc003 that
; allows one more character after the xoff, since it didn't
; really work very well and does not fit in well with the way
; the buffer advancing routines are set up. If someone still
; thinks this would be useful, it could be put back in with a
; little more work.
;
; While testing this edit, I also noticed another bug that
; the command parsing routines do not limit or check the
; length of command lines or file specs, trashing what ever
; comes after them. Currently because of where the fcb and
; command buffer are located, this does not usually cause a
; problem, but could if an extremely long line was typed in,
; or in the future multiple fcbs defined elsewhere in memory
; were used. Maybe this should be put on the bug list
; somewhere.
;
;pcc013 8-Jan-85 vjc modules:cp4mit,cp4utl,cp4typ
; Replace CLOSE command to cancel session logging to SET
; LOGGING ON/OFF. This seems to fit in with the command
; structure better. Default the log file to KERMIT.LOG
; incase no previous LOG command. Logging is also enabled
; by LOG command, as before.
;
; edit 4: September 9, 1984
; Move command tables and associated help text to CP4MIT. Add
; makfil/clofil routines and modify outbuf to write files in big
; chunks. Update Kermit's version to 4.03.
;
; edit 3: August 21, 1984
; Make inbuf read files in big chunks to minimize disk start/stop
; delays. Buffer size and address is specified by system-dependent
; overlay.
;
; edit 2: August 3, 1984
; move "mover" to CP4SYS to allow use of Z80 block move instruction.
;
; edit 1: July 27, 1984
; extracted from CP4MIT.M80 edit 2, as part of LASM support. This is
; the last file linked for the system-independent code.
;
utlver: db 'CPSUTL.ASM (31) 21-Mar-1991$'
; Set the parity for a character in A.
; called by: spack, rexmit, logit, vt52, conchr, intchr
setpar: push h ;Save HL.
push b
lxi h,parity
mov c,m ;Get the parity routine.
mvi b,0
lxi h,parjmp ;Get the first address.
dad b
pchl
parjmp: jmp even
jmp mark
jmp none
jmp odd
jmp spacep
none: jmp parret ;Don't touch the parity bit.
even: ani 7FH ;Strip parity.
jpe parret ;Already even, leave it.
ori 80H ;Make it even parity.
jmp parret
mark: ori 80H ;Turn on the parity bit.
jmp parret
odd: ani 7FH ;Strip parity.
jpo parret ;Already odd, leave it.
ori 80H ;Make it odd parity.
jmp parret
spacep: ani 7FH ;Turn off the parity bit.
jmp parret
parret: pop b
pop h ;Restore HL.
ret
;
; Print the escape char.
; called by: stat01, telnet, intchr
escpr: lda escchr ;Get the escape char.
escpr1: cpi ' ' ;Is it a control char?
jp escpr2
push psw ; save the character
lxi d,inms10 ;Output "Control-".
call prtstr
pop psw ; restore the character
ori 100O ;De-controlify.
escpr2: mvi c,conout ;Output the char
mov e,a
call bdos
ret
; fetch keyword; if unsuccessful, return to command level.
; called by: kermit, setcom
keycmd: mvi a,cmkey
call comnd
jmp keycm2 ;no match
ret
keycm2: lxi d,ermes1 ;"Unrecognized Command"
call prtstr
jmp kermit ;Do it again.
; request confirmation; if unsuccessful, return to command level
; called by: bye, exit, help, log, setcom, show, status, send,
; finish, logout, telnet
cfmcmd: mvi a,cmcfm
call comnd
jmp kermt3 ;"Not confirmed"
ret
;
; This routine prints the number in HL on the screen in decimal.
; Uses all ACs.
; called by: cp4sys, read, send, updrtr, dir, user
nout: mvi a,'0' ; fill tempx with zeros
call filltmp
lxi b,-10 ;Get some useful constants.
nout1: lxi d,-1
nout2: dad b ;Subtract as many 10s as possible.
inx d ;Count them.
jc nout2 ;If some left keep going.
push h ;save remainder - 10
xchg ;Swap the remainder and the quotient.
mov a,h ;Get the number of 10s found.
ora l ;check for quotient non zero
cnz nout1 ;If non zero, recurse.
pop h ;Get the remainder - 10
mov a,l ;in a
adi '0'+10 ;Make the number printable and add the 10 back
call shiftmp ; cycle temp registers
sta temp1 ; and save digit
mov e,a ;Output the digit.
lda nquiet ; are we to be quiet?
ana a
rnz ; yup, so return here rather than frm bdos
mvi c,conout
jmp bdos
; prcrlf - print a CR/LF. (Saves no registers.) [Toad Hall]
; prtstr - print string pointed to by DE (now in overlay section.. see prtstx)
; called by: lots of places.
prcrlf: lxi d,crlf ;Point to the CR/LF
jmp prtstr ; Use the one in the overlay
; prtstx is funtionally the same as prtstr in the overaly but is here as
; we may need a print a string routine in case the overlay is
; either incorrect version or simply not present.
prtstx: ; PRTSTR moved to overlay, but we do need the
; same function for writing the sign-on
; message and error message if the overlay
;is not present. Thence prtstx
mvi c,prstr ; output string
jmp bdos ;a CALL followed by a RET becomes a JMP
; Jumping to this location is like retskp. It assumes the instruction
; after the call is a jmp addr.
; here from: many places.
rskp: pop h ;Get the return address.
inx h ;Increment by three.
inx h
inx h
pchl
; Jumping here is the same as a ret. 'jmp r' is used after routines
; that have skip returns, where the non-skip instruction must be 3 bytes.
; here from: many places.
r: ret
; Pause-it routine. Informs the user to press any key to continue
; and then waits for a key input. Called by the any routine
; with more than, say, 20 lines of output.
pausit: lxi d,anymes ; ask user to press any key to continue
call prtstr
pausi1: call ckcon ; see if a key typed
ana a
jz pausi1 ; loop until a key has been pressed.
ret
;
; Open a file for reading (with inbuf). The filename is already
; in fcb; upon return, the end of file flag is cleared and chrcnt
; is set to zero, so inbuf will be called to get a buffer when we
; next attempt to get a character.
; called by: sinit, seof
getfil: xra a
sta chrcnt ;Buffer is empty.
sta seccnt ;No sectors buffered.
sta eoflag ;Not the end of file.
sta endsts ;No EOF/error pending.
sta fcb+0CH ;Zero the extent.
sta fcb+0EH ;Must be zero for MAKEF or OPENF.
sta fcb+20H ;Zero the current record.
mvi c,openf ;Open the file.
lxi d,fcb
call bdos
ret
; Get next sector. If necessary, read some more from disk.
; preserves bc, de, hl
; returns nonskip if EOF or error;
; returns skip with chrcnt and bufpnt updated if success.
; called by: gtchr, get1xc (from xmt/transmit)
inbuf: lda eoflag ;Have we reached the end?
ora a
rnz ;Return if so.
push b
push d
push h
inbuf1: lda seccnt ; Do we have any sectors left?
ora a
jz inbuf3 ; If not, go get some more.
inbuf2: lhld nxtbuf ; Yes. Get address of next sector
shld bufpnt ; Update current buffer pointer
lxi b,bufsiz ; Get number of bytes in sector
dad b ; Update HL to point to next sector
shld nxtbuf ; Save for next time
dcr a ; Decrement count of buffered sectors
sta seccnt ; Put it back
mvi a,bufsiz-1 ; Number of bytes in buffer (pre-decremented)
sta chrcnt ; Store for our caller
pop h
pop d
pop b
jmp rskp ; Return success
; We don't have any sectors buffered. If we've already hit an error or
; EOF, return that status to the user.
inbuf3: lda endsts ; Check status from previous read
ora a
jz inbuf4 ; It was OK. Get some more sectors.
sta eoflag ; End of file or error. Set the flag.
xra a
sta chrcnt ; Say no characters in buffer.
pop h
pop d
pop b
ret ; Return failure
; Read sectors until we fill the buffer or get an error or EOF, then return
; the first buffer to the user. (seccnt is zero)
inbuf4: lhld bufadr ; Get address of big buffer
shld nxtbuf ; Store as next buffer address to give user
inbuf5: shld bufpnt ; Store as next buffer address to read into
xchg ; Move buffer address to DE
mvi c,setdma ; Tell CP/M where to put the data
call bdos ; ...
mvi c,readf ; Read a record.
lxi d,fcb
call bdos
sta endsts ; Save ending status
ora a ; 00H => read O.K
jnz inbuf6 ; EOF/error: stop reading.
lxi h,seccnt ; Success. Get addr of sector count
inr m ; Bump sector count by one
lda bufsec ; Get max number of sectors in buffer
cmp m ; Are we there yet?
jz inbuf7 ; Yes, can't read any more.
lhld bufpnt ; No, try for another. Get buffer address,
lxi d,bufsiz ; and size of sector,
dad d ; giving next buffer address in HL
jmp inbuf5 ; Go read another sector.
; We hit EOF or got an error. Put the DMA address back where it belongs,
; then go see if we have any sectors (before the one that got the error)
; to return to the caller. Nxtbuf points to the first good sector, if
; any; seccnt contains the count of good sectors.
inbuf6: call rstdma
jmp inbuf1 ; Go see if we have some data to return
; We've filled the big buffer. Reset the DMA address, then go return a
; sector to the caller. nxtbuf points to the beginning of the buffer;
; seccnt contains the number of sectors successfully read (except that
; if we've read 256 sectors, seccnt contains zero, so we can't just go
; to inbuf1).
inbuf7: call rstdma ;[pcc012]
lda seccnt ; Get sector count again.
jmp inbuf2 ; Return a sector.
; IN1CHR - get a single character from the file. Taken code from old
; TRANSMIT routine.
in1chr:
lda eoflag ;EOF encountered?
ora a
rnz ; Yes, finish.
lxi d,cmdbuf ; Use comnd buffer as line buffer.
lhld bufpnt ; Get current buffer pointer.
lda chrcnt ; Get current byte count
mov b,a ; in B
in1ch1: dcr b ; Assume there's a character there
jp in1ch2 ; If there was, proceed.
call inbuf ; There wasn't. Try for another buffer.
jmp in1che ; End of file.
lhld bufpnt ; Got another buffer. Get new pointer in HL
lda chrcnt ; and new byte count
mov b,a ; in B
;[MF]The modification below was made 30-Jan-1991 per report from
;[MF]Lance Tagliapietra from the University of Wisconsin at Platteville
;[MF]The following instruction should not be executed as the character counter
;[MF]has already been decremented by INBUF.
; dcr b ; we are reading in a character, so less one
in1ch2: mov a,b ; save new count
sta chrcnt
mov a,m ; Get a character from disk buffer.
inx h
shld bufpnt ; save new pointer
ani 7FH ; Mask 7 bits.
jz in1ch1 ; Skip nulls.
ret ; character in a
in1che: mvi a,0ffh ; dubious about this one...
sta eoflag
ret ; set end of file flag...?????
;;[pcc012]
; appfil - Create or append to an existing file. File name is in FCB.
; Non-skip return if could not be done. Skip return with file
; open and bufpnt pointing to end of file.
; called by logopn
appfil: xra a ;[pcc012] zero out stuff for open
sta fcb+0CH ;[pcc012] extent
sta fcb+0EH ;[pcc012] Must be zero for MAKEF or OPENF.
sta fcb+20H ;[pcc012] Zero the current record.
mvi c,openf ;[pcc012] Try to open the file
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
cpi 0FFH ;[pcc012] Did we find it?
jz makfi1 ;[pcc012] If not, go create it
mvi c,cflsz ;[pcc012] Compute the file size
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
lhld fcb+21H ;[pcc012] random record pointer
mov a,h ;[pcc012] See if zero length file
ora l ;[pcc012]
jz makfi2 ;[pcc012] set up pointers if null file
dcx h ;[pcc012] backup to last record written
shld fcb+21H ;[pcc012] store rec ptr back
lhld bufadr ;[pcc012] get buffer address
xchg ;[pcc012] to DE
mvi c,setdma ;[pcc012] set dma address
call bdos ;[pcc012] for read
mvi c,rrand ;[pcc012] read the last block
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
ora a ;[pcc012] check results
jnz rstdma ;[pcc012] reset dma and return if error
lhld bufadr ;[pcc012] get address again
lxi d,bufsiz ;[pcc012] and and size
mvi a,'Z'-40H ;[pcc012] control-Z for comparison
appcz: cmp m ;[pcc012] Is this the EOF?
jz appxit ;[pcc012] Jump if yes
inx h ;[pcc012] no, bump
dcr e ;[pcc012] and grind
jnz appcz ;[pcc012] until find or buffer empty
appxit: shld bufpnt ;[pcc012] store buffer pointer
dad d ;[pcc012] compute next buffer adr
shld nxtbuf ;[pcc012] and store
mov a,e ;[pcc012] updated chr count
sta chrcnt ;[pcc012]
xra a ;[pcc012] reset sector count
sta seccnt ;[pcc012]
call rstdma ;[pcc012] reset normal dma
jmp rskp ;[pcc012] and give good return
; Create a file, deleting any previous version. The filename is in
; fcb.
; Returns nonskip if file could not be created.
; If successful, takes skip return with bufpnt and chrcnt initialized
; for output; buffers should be output via outbuf.
; called by: gofil
makfil: mvi c,delf ; delete the file if it exists.
lxi d,fcb
call bdos
xra a
sta fcb+0CH ; zero the extent.
sta fcb+0EH ; must be zero for MAKEF or OPENF.
sta fcb+20H ; zero the current record.
;[pcc012] here from appfil above if file does not exist
makfi1: mvi c,makef ;[pcc012] now create it.
lxi d,fcb
call bdos
cpi 0FFH ; is the disk full?
rz ; take error return if so.
; success. set up pointers and counters for multisector buffering.
;[pcc012] also here from appfil if found zero length file
makfi2: lhld bufadr ;[pcc012] find beginning of buffer space.
shld bufpnt ; make it current buffer.
lxi d,bufsiz ; get sector size.
dad d ; find beginning of next buffer.
shld nxtbuf ; store for later.
mov a,e ; store buffer size
sta chrcnt ; for caller.
xra a
sta seccnt ; no sectors stored yet.
jmp rskp ; take success return.
;[pcc012]
; outadv - conditionally advance output buffer if disk write not needed.
; preserves BC
; skip return with with next output buffer set up
; non-skip return if memory buffer full and must write to disk.
; called by:logit
outadv: push b ;[pcc012] save BC as advertised
lxi h,seccnt ;[pcc012] point to sectors buffered
inr m ;[pcc012] count this one
lda bufsec ;[pcc012] how many we can hold
cmp m ;[pcc012] check if full
jnz outbf2 ;[pcc012] continue if not
dcr m ;[pcc012] full, un-advance sector count
pop b ;[pcc012] restore bc
ret ;[pcc012] and give non-skip return
; get a fresh output buffer, flushing big buffer if necessary.
; returns nonskip if disk full.
; if successful, returns skip with bufpnt and chrcnt updated. Note
; that chrcnt holds one less than the buffer size.
; preserves BC.
; called by: ptchr,logwrt
outbuf: push b
lxi h,seccnt ; count another buffered sector
inr m ; ...
lda bufsec ; get number of sectors we can hold
cmp m ; full?
jnz outbf2 ; if not, set up pointers and return
call outmbf ; flush the big buffer
jmp outbf9 ; disk error.
;[pcc012] also here from outadv to advance buffer
outbf2: lhld nxtbuf ; get pointer to fresh buffer
shld bufpnt ; store for caller
lxi d,bufsiz ; advance our pointer to next buffer
dad d
shld nxtbuf
mvi a,bufsiz-1 ; get buffer size (pre-decremented)
sta chrcnt ; store for caller
pop b
jmp rskp ; return success.
outbf9: pop b ; clean up stack
ret ; and take error return.
; flush incore output buffers.
; returns nonskip if disk full.
; if successful, returns skip with nxtbuf reset to start of buffer and
; seccnt zero.
; destroys all ac's.
; called by: outbuf, clofil.
outmbf: lhld bufadr ; get start of buffer
shld nxtbuf ; store for next fill cycle
shld bufpnt ; store for empty loop
outmb2: lhld bufpnt ; get address of current sector
xchg ; into DE
lxi h,bufsiz ; advance HL to next sector
dad d ; ...
shld bufpnt ; and store for later
mvi c,setdma
call bdos ; point CP/M at current sector
lxi d,fcb
mvi c,writef
call bdos ; output the sector
ora a ; test for error (A non-zero)
jnz rstdma ;[pcc012] reset dma and take nonskip return if so
lxi h,seccnt
dcr m ; count down buffered sectors
jnz outmb2 ; loop if more saved
call rstdma ;[pcc012] restore normal dma
jmp rskp ; return success.
; output current buffer, flush incore buffers, and close output file.
; returns nonskip if disk full; skip if successful.
; called by: rdata
clofil:
lda chrcnt ; get the number of chars left in the buffer.
cpi bufsiz ; Virgin buffer?
jz clofl3 ; yes, don't output it.
lhld bufpnt ; get the buffer pointer.
clofl1: dcr a ; lower the count.
jm clofl2 ; if full then stop.
mvi m,'Z'-100O ; put in a ^Z for EOF.
inx h ; point to the next space.
jmp clofl1
clofl2: call outbuf ; output the last buffer.
jmp r ; give up if the disk is full.
clofl3: lda seccnt ; any sectors buffered in memory?
ora a
jz clofl4 ; if not, don't try to flush.
call outmbf ; flush buffers
jmp r ; disk full.
clofl4: mvi c,closf ; close up the file.
lxi d,fcb
call bdos
jmp rskp ; return success.
; Reset DMA address to the default buffer
; called from inbuf,appfil,outmbf
rstdma: lxi d,buff ;[pcc012]
mvi c,setdma ;[pcc012]
jmp bdos ;[pcc012]
; [8] Intercept BDOS calls to check for console input
; This leads to simple trapping for input from disk rather than from the
; keyboard, alowing commands to be stored in a TAKE file.
; Printer is tested for readiness, and the second fuzzy clock is updated.
bdos: ;call print ; print a character to the printer if needed
;call clock ; update the clock
push psw ; we will need this register
lda takflg ; are we taking from a
ana a ; file or from command line
jz notake ; no, so do usual BDOS stuff
mov a,c ; get bdos function
cpi conin ; is it console in?
jz bd1in ; get a single character
cpi dconio ; direct console in or out?
jz bd1io ; test further for inpu or output
cpi rdstr ; read the console buffer?
jz bdcbuf ; then do it
cpi consta ; get the console status?
jz bdcst ; anything left in buffer?
notake: pop psw ; else we have a kosher BDOS call
jmp 0005h ; Absolute address = BDOS entry point
;
bd1in: ; get a single character from take file
pop psw ; restore stack
call r1tchr ; read a single take character
ret ; and return. We dont expand tabs,
; check for xon/off or backspaces.
;Make sure the take file is error free?
;
bd1io: ;get or put a single character from/to console
mov a,e ; get e. If 0ffh then input else output
inr a ; if 0 then input
jnz notake ; its for output, so let notake restore stack
pop psw ; otherwise we do it
call r1tchr ; read a single take character
ret ; and return from out BDOS
;
bdcbuf: ; read a line of edited (?) input from the console
pop psw ; restore stack
inx d ; point to nc
bdosc1: call r1tchr ; get a character
cpi cr ; if a cr then return
jz nomore
cpi lf ; ignore line feeds
jz bdosc1 ; so get another character
push psw ; we will want it later
mvi h,0
ldax d ; get nc, the no of characters in buffer
mov l,a ; now use as index from de+2
mvi h,0
inx d ; de is now de + 2
dad d
pop psw ; Told you we will want this
mov m,a
dcx d ; point again to nc
xchg ; make hl point to memory
inr m ; update pointer nc
xchg ; restore it
jmp bdosc1
nomore: dcx d ; restore de to point to buffer
ret
bdcst: ; get the console status. Returns a 00 if at eof
pop psw
push h ; now save de, hl for return
push d ;
lxi d,takdma ; make a point to next byte...
lhld takptr ; pointer from dma address. There will always
dad d ; be at least one byte, as the buffer is
mov a,m ; ...
pop d ; filled only if a read empties it.
pop h ; restore hl, de
cpi cntlz ; end of file?
mvi a,0ffh ; say there is
rnz ; if it is not a cntl z
jmp closet ; otherwise, close take file etc
r1tchr: ; read a single character from the take file or command line
push h
push d ; save in case of return
lda takflg ; see if character is to come from file or line
ani 1 ; if bit zero set, from take file
jz r1lchr ; get character from the command line
lxi d,takdma
lhld takptr ; get next data byte
dad d
mov a,m
lhld takptr ; cos it's destroyed with dad
inx h
shld takptr ; update pointer
call p1tchr ; print it (so the user sees it)
push psw ; save the read data for a while
mov a,l ; if l = 0 then read another sector
ana a
jp r1tch1 ;[23] was jnz. jp => 128 byte sectors
call rnsect ;read next sector
r1tch1: pop psw ; now, is this a cntl-z.. in whic case
pop d ; also these...
pop h
cpi lf ; skip if a line feed
jz r1tchr ;
;[MF][31][30]Following lines commented out so semicolons are not considered
;[MF][31][30]command separators and thus are considered part of the command
;[MF][31][30]so commands like REMOTE DELETE *.*;* work correctly.
; cpi semico ; see if its a semicolon
; jnz r1tch2 ; no, ignore it
; mvi a,cr ; else say its a cr (in case of command lines)
r1tch2: cpi cntlz ; end of file??
rnz
c1tchr: call closet ; close file etc, then
mvi a,cr ; fake a carriage return chr
; ( => clears kermit comnd line)
c1tch1: ret ; and hope that editing etc not required.
r1lchr: ; read a single character from the command line
lxi d,cbuff ; point to buffer
lda cbptr ; get pointer for next character
mov l,a
lda cbuff ; get total number of characters there
cmp l ; ... less current character
jp r1lch1 ; if positive, we have more characters
lda takflg ; no more, so reset command line bit (bit 4)
ani 0efh
sta takflg
mvi a,cntlz ; fudge an end of file
push psw ; save for common exit (r1tch1)
jmp r1tch1
;
r1lch1: mov a,l ; get count back again
mvi h,0
dad d ; get offset to character
inr a
sta cbptr
mov a,m ; get next character
cpi semico ; if a semicolon, make it a carriage return
jnz r1lch2
mvi a,cr
r1lch2: call p1tchr ; send a copy to the console
push psw ; save it for r1tch1
jmp r1tch1 ; common exit
; rnsect - read the next take sector from disk to the take dma address
; if there is no more then close the file too
rnsect: push b
push d
push h ; save in case we need these later
mvi c,setdma
lxi d,takdma ; set a next read from disk
call bdos ; recursive...
mvi c,readf
lxi d,takfcb
call bdos
ana a
cnz closet ; if returned value not zero, assume eof
lxi h,0
shld takptr ; pointer restored
call rstdma ; reset the dma address for fussy routines (this one)
pop h
pop d
pop b
ret
; closet - close the take file and set the take flag to 0 (ie no takes)
;
closet: lda takflg ; reset the take file bit (bit 0)
ani 0feh
sta takflg ; close the take file, and restore the flag
mvi c,closf
lxi d,takfcb
call bdos
call rstdma ; in case we did not do it above, reset dma
ret
;
; clock - is a 32 bit counter incremented every BDOS call. It serves as a
; timer of sorts and allows a background clock to tick away..
clock: push psw ; we need flags and hl
push h
lhld clkbit ; get the counter
inx h ;
shld clkbit
mov a,h
ora l ; do we need to update the next lot of clock bits?
jnz clockx
lhld clkbit+2 ; if carry up the top 16 bits
inx h
shld clkbit+2
clockx: pop h
pop psw
ret
; p1tchr - print a character in accumulator directly to the console
; bypassing the bdos trap above.
p1tchr: cpi lf ; if a lf ignore it
jz p1tchx
cpi cr ; ditto carriage returns
jz p1tchx
cpi cntlz ; control z
jz p1tchx ; then dont write it out
push psw ; we do not want to loose it, do we?
push b
push d
push h ; 'cos you never know what bdos does...
mov e,a
mvi c,conout ; direct console io
call 5 ; absolute address as we skip the trap
pop h
pop d
pop b ; ... and we need some of these regs.
pop psw ;
p1tchx: ret ; and return
; outprn - This routine sends charactes to the printer if the latter is ready,
; or to a buffer if the printer is not ready. If the buffer is nearly
; full, an XOFF is sent to the host, asking it to be quiet. The buffer
; is emptied by a series of calls in the connect state only.
; If the buffer is made nearly empty, then an XON is sent to the host.
;
outprn: mov a,e ; get the character to send back to a
sta prntmp ; we need all registers.
jmp outprx ; -testing-testing-testing- avoid buffer
;
outp0: call tstfree ; see how many spaces free
cpi 2 ; (free spaces in a on return)
jp outp1 ; enough free spaces, so keep going
call print ; else see if we can print summat
jmp outp0 ; and try again
;
outp1: cpi 4 ; common test - if three or less then send xoff
cm sndxoff
outpr2: mov a,b ; inc ptr and check for wrap around
call wrapt
mov b,a ; input pointer to b
sta prnbuf+1 ; save the new pointer away
lxi h,prnbuf+2 ; point to first real data entry in buffer
call inchl ; add offset in a to hl
lda prntmp ; get th character to save away
mov m,a ; save the data away
ret
; outprx - send character in a to the printer. (We have checked to see if
; the printer is ready)
; called by outprn, print
outprx: mov e,a ; character has to be in e register
call outlpt ; send it to printer
ret ; assume we print it
; TSTFREE - see how many free spacse there are in the buffer
; - returns with free space in a, ip pointer in b, op pointer in c
szecyc equ 127 ; 128 bytes in buffer (less for debugging)
;
tstfree:
lda prnbuf ; get output pointer
mov c,a ;.. to c
lda prnbuf+1 ; and input pointer ...
mov b,a ; ... to b
;
;
; Now comes the tricky bit. We must establish whether there is less than
; three characters left in the buffer. There are two conditions to test for
; 1) the input pointer is a higher value than the output pointer
; 2) the input pointer has been wrapped round and is less than the output pointer
; ie
; |-------|-------|---------------------------------------------|
; Buffer |o/p ptr|i/p ptr| Buffer proper filling ---> |
; |-------|-------|------|-------------|-----------|------------|
; i/p2 o/p i/p1
;
; If ip = ip1 then if
; (size of buffer - ip ptr + op ptr) < 3 send xoff
; If ip = ip2 then if
; (op ptr - input ptr ) < 3 send xoff
;
; First decide whice one applies
mov a,b ; get ip ptr
sub c ; see if op ptr > ip ptr (case 2)
jm outp2 ; yup, so do case two
mvi a,szecyc ; else do buffer - ip + op
sub b
add c
jmp outpx ; do common test
outp2: mov a,c ; get op pointer
sub b ; less input pointer
outpx: ret ; with free space in a
;
;
;
; print - get a character from the buffer and print it if the printer
; is ready for it. If the buffer clears more than 3 spare characters
; and an xoff has been sent, then send an xon again.
print: push h ; save for rainy days
push d
push b
push psw ; .. as we may need flags etc....
lda initflg ; First check if the system has initialised
ana a
jz printx ; If system not set up then skip
; nop
; nop
; nop ; debugging only...
call ckprtr ; check to see if printer is ready...
ana a ; not zero => ok
jz printx ; else skipit.
; nop
; nop
; nop ; skip the jump for debugging
lxi h,prnbuf
mov a,m ; get input pointer
inx h ; test against output pointer
cmp m ; if = then buffer empty
jz printx ; so quit
dcx h ;pointer to output pointer
call wrapt ; check for wrap around
sta prnbuf ; save new pointer
inx h
inx h ;
call inchl ; add output pointer to hl
mov c,m ; get byte
lda hosths ; have we told host to be quiet?
cpi xoff ; if = xoff then we have
jnz print1 ; nope, so just print it..
push b ; save the character to print
call tstfree ; see how many free bytes in buffer
pop b
cpi 4 ; 3 characters left?
jz printx
push b
call sndxon ; send an xon to host and wake it up.
pop b
print1: mov a,c ; we are gonna print a character, so ...
call outprx ; get it to a (as required by outprx) and print it
printx:
pop psw
pop b
pop d
pop h ; restore regs.
ret
;
; Utilities for the cyclical buffer. Returns a 0ffh if printer ready,
; else 0h. Called by outprn, print
ckprtr:
call lptstat ; no registers saved
; mvi a,0 ; FOR DEBUGGING PURPOSES
; nop
ret
;
inchl: push psw ; we do maths through this register
add l
mov l,a
mvi a,0
adc h
mov h,a
pop psw ; hl = a + hl
ret
; wrapt - checks the offset in a with the limits of the buffer.
; returns next address or if wrap around then 0 (start of buffer)
wrapt: push b
inr a
mov b,a ; save new a into b for now
mvi a,szecyc ; test for size of buffer
sub b
mov a,b
pop b ; restore bc regs again
rnz
xra a ; if wrap around, then reset pointer
ret ; return with next address pointer to in a
; sndxoff - send an xoff to the host and save the xoff character in hosths
; saves all regs. is called by logwrt, outprn
sndxoff:
push psw
push b
push d
push h ; some calling routines may be sensitive...
lda floctl ; are we doing flow control?
ana a
jz sndxf ; no, so dont bother.
mvi a,xoff ;^S to stop the host while we write the buffer.
sta hosths ; save it so we know we have sent it
call setpar ; set correct parity...
mov e,a
call outmdm ; output it.
lxi d,ofsnt ; say we have sent an xoff
call prtstr
sndxf: pop h
pop d
pop b
pop psw ; some routines touchy
ret
ofsnt: db cr,lf,'[XOFF sent to host]',cr,lf,'$'
; sndxon - send an xon to the host and clear the hosths flag. saves everything
; called by logwrt, print
sndxon: push psw
push b
push d
push h
lda floctl ; are we doing flow control?
ana a
jz sndxn
xra a
sta hosths ; no xoff to hos any more
mvi a,xon ;^Q to restart the host
call setpar ; set appropriate parity
mov e,a
call outmdm ; send it.
lxi d,onsnt
call prtstr ; say xon sent to host...
sndxn: pop h
pop d
pop b
pop psw
ret ; shame we dont do a pushall/popall subroutine...
onsnt: db cr,lf,'[XON sent to host]',cr,lf,'$'
;
; Routines to clear (or rather fill) TEMPnnn space with the data in A
; and to shift it all along one (filltmp an shiftmp respectively)
filltmp:
push b
push d
push h ; save all
lxi h,temp1
mvi b,10 ; ten locations to fill
fillp: mov m,a
inx h
dcr b ; loop til all done
jnz fillp
pop h
pop d
pop b ; restore all
ret
shiftmp:
push psw ; save all again
push b
push d
push h
lxi d,temp9
lxi h,temp10
mvi b,9 ; shift nine times
shiftl: ldax d ; from tempx
mov m,a ; to tempx+1
dcx d
dcx h ; mover does not work as that increments
dcr b
jnz shiftl
pop h
pop d
pop b
pop psw
ret ; else all done
; getun - get the user number to temp1 (lsd) and temp2 (msd)
;
getun: mvi a,0ffh ; tell nout to be quiet
sta nquiet
mvi c,usrcod
mvi e,0ffh ; get current user from bdos
call bdos
mov l,a ; put into hl
mvi h,0
call nout ; decimalise it (decimalise???)
xra a
sta nquiet ; let nout print again
ret
; ckcon - Do a direct console IO (read) to see if there is any input
; returns with a=0 (no input) or character (input received)
; Assume that all regs may be destroy.
ckcon: mvi e,0ffh ; direct console input
mvi c,dconio
call bdos
ret ; and return with wahtever returned in a
;
; subbc - Subtract the unsigned number in bc from the unsigned number
; in HL with the answer in HL. Flags altered, all
; other registers left intact.
subbc: sta temp1 ; hl = hl - bc.. we need the accumulator
mov a,l
sub c
mov l,a
mov a,h
sbb b
mov h,a
lda temp1 ; restore loop counter but not flags
ret
; P20LN - Routine to print a string at (DE) and count the number of
; line feeds. Pause after 20 lines printed.
p20ln: xra a ; clear the line counter
sta lincnt
p20ln1: ldax d ; get character to print
inx d
cpi '$' ; if a dollar we have done
rz
push d
push psw ; save pointer and character to print
mov e,a
call outcon ; send character
pop psw
pop d ; restore pointers etc
cpi lf ; was that last character a line feed?
jnz p20ln1 ; no, so carry on
lda lincnt ; yup, so update counter
inr a
sta lincnt
cpi 20 ; 20 lines printed?
jnz p20ln1 ; not yet
push d ; we need DE
; lxi d,anymes ; pause a while [MF]removed
; call prtstr ; write the message [MF]
;p20ln2: call ckcon ; wait for any input [MF]
; ana a ;[MF]
; jz p20ln2 ;[MF]
call pausit ;[MF] pause a while
pop d
jmp p20ln ; and continue
; Little code to allow some expansion of code without changing
; every futher address, only up to the end of this file.
; TO BE REMOVED FOR RELEASE!
; org ($+100h) AND 0FF00H
; link to the data area (was part of CPSUTL.ASM)
IF lasm
LINK CPSDAT
ENDIF ;lasm