From ebb2ff2af569d5822b70ee555a32d27cf7494816 Mon Sep 17 00:00:00 2001 From: Jeff Armstrong Date: Sun, 25 Oct 2026 17:42:06 -0400 Subject: Initial import of 4.11 --- src/cpsutl.asm | 1227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1227 insertions(+) create mode 100644 src/cpsutl.asm (limited to 'src/cpsutl.asm') diff --git a/src/cpsutl.asm b/src/cpsutl.asm new file mode 100644 index 0000000..3081232 --- /dev/null +++ b/src/cpsutl.asm @@ -0,0 +1,1227 @@ +; 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: +; ). +; (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 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 Q to suspend logging and R to +; resume. 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 '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 -- cgit v1.2.3