; CP4SYS.ASM Keep filename the same, so that we do not have to ; change CP4DEF and CP4LNK ; 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. ; ; This file contains the system-dependent code and data for KERMIT ; the Amstrad range of personal computers. ; ; Martin D. Beer ; Department of Computer Science, ; University of Liverpool. ; ; In association with Northern Computers Ltd., Frodsham. ; ; Modifications Copyright Martin D. Beer. December 1985. ; ; revision history: ; [2] add support for CP/M 2.2 screen handling. ; [1] Edit from distribution copy of CP4SYS.ASM. ; Use generic CP/M 2.2 and CP/M 3.1 for now. ; ; Keep module name, edit number, and last revision date in memory. sysedt: db 'CP4AMS.ASM (2) 20-Dec-85$' ; ; Assembly time message to let me know I'm building the right version. ; LASM generates an 'S' error along with the message, which is messy, but ; better than trying to put everything inside a IF m80 OR mac80 conditional, ; because LASM doesn't like nested IF's, either. IF gener .printx * Assembling Generic KERMIT-80 * ENDIF IF cpm3 .printx * Assembling Generic KERMIT-80 for CP/M 3.0 * ENDIF ; Also tell what kind of terminal, if any, is selected IF crt .printx * generic CRT selected * ENDIF IF vt52 .printx * VT52 selected * ENDIF ; ;========================================================================= ; I/O Byte assignments (2-bit fields for 4 devices at loc 3) ; ;bits 6+7 LIST field ; 0 LIST is Teletype device (TTY:) ; 1 LIST is CRT device (CRT:) ; 2 LIST is Lineprinter (LPT:) ; 3 LIST is user defined (UL1:) ; ;bits 4+5 PUNCH field ; 0 PUNCH is Teletype device (TTY:) ; 1 PUNCH is high speed punch (PUN:) ; 2 PUNCH is user defined #1 (UP1:) ; 3 PUNCH is user defined #2 (UP2:) ; ;bits 2+3 READER field ; 0 READER is Teletype device (TTY:) ; 1 READER is high speed reader (RDR:) ; 2 READER is user defined #1 (UR1:) ; 3 READER is user defined #2 (UR2:) ; ;bits 0+1 CONSOLE field ; 0 CONSOLE is console printer (TTY:) ; 1 CONSOLE is CRT device (CRT:) ; 2 CONSOLE is in Batch-mode (BAT:);READER = Input, ; LIST = Output ; 3 CONSOLE is user defined (UC1:) ; ;========================================================================= iobyte EQU 03H ;Location of I/O byte ; the basics... IF robin OR gener batio EQU 056H ;I/O byte CON=BAT,LIST=CRT,READER=RDR,PUNCH=PTP defio EQU 095H ;I/O byte CON=CRT,LIST=LPT,READER=RDR,PUNCH=PTP ENDIF;robin OR gener IF gener crtio equ 01010101B ; use CRT: device ptrio equ 01010110B ; use PTR: device ttyio equ 00000000B ; use TTY: device uc1io equ 01010111B ; use UC1: device ur1io equ 01101010B ; use UR1: device ur2io equ 01111110B ; use UR2: device ENDIF;gener ; ; ; Protocol parameters. Some of these can be changed with commands. ; drpsiz EQU 5EH ;Default receive packet size. (maximum is 5EH) dspsiz EQU 20H ;Default send packet size. (maximum is 5EH) dstime EQU 08H ;Default send time out interval. drtime EQU 05H ;Default receive time out interval. dspad EQU 00H ;Default send padding. drpad EQU 00H ;Default receive padding. dspadc EQU 00H ;Default send padding char. drpadc EQU 00H ;Default receive padding char. dseol EQU CR ;Default send EOL char. dreol EQU CR ;Default receive EOL char. dsquot EQU '#' ;Default send quote char. drquot EQU '#' ;Default receive quote char. dschkt EQU '1' ;Default checksum type ; IF lobo ;[hh] mnport EQU 0F7E4H ;Modem data port A mnprts EQU 0F7E5H ;Modem status/conrtol port A baudrt EQU 0F7D0H ;Baud rate port A output EQU 04H ;Transmit buffer empty input EQU 01H ;Receive data available z80 EQU TRUE ;a good z80, here ENDIF;lobo z80 EQU TRUE ;all Amstrads have a Z80. ; defesc EQU ']'-100O ;The default escape character. ; Select initial setting for VT-52 emulation flag. IF ams3 ; If console looks like (or is) VT52 vtval EQU 0 ; we don't need VT52 emulation ENDIF;ams3 ; If none of the above, default to VT52-EMULATION ON. IF ams2 vtval EQU 1 ENDIF;ams2 ; Set the fuzzy timeout value. Range is 1 (VERY short) through 0ffffH to zero ; (maximum). The actual duration is a function of the loop length and the ; processor speed. For now, we'll make it zero for everybody, but feel free ; to change it for your system. fuzval EQU 0 ; ; ; System-dependent initialization ; Called once at program start. sysinit: mvi c,getvnm ; get the BDOS version number (e.g. 22H, 31H) call bdos mov a,l sta bdosvr ; and store it away for future reference ; lxi d,cfgmsg ; "configured for " call prtstr lxi d,sysver ; get configuration we're configured for call prtstr ; print it. ; ; If we're set up to do special terminal handling, say what kind ; of terminal we expect... (unless it's the generic 'crt') call prcrlf ; print CR/LF ; ; now, to work... ; IF NOT osbrn1 ; locate large buffer for multi-sector I/O ; What we want to do here is find the ccp. Space between ovlend and the ccp ; is available for buffering, except we don't want to use more than maxsec ; buffers (if we use too many, the remote end could time out while we're ; writing to disk). maxsec is system-dependent, but for now we'll just ; use 8Kbytes. If you get retransmissions and other protocol errors after ; transferring the first maxsec sectors, lower maxsec. ; I'm excluding the Osborne 1 for now because it needs code up at 4000H, ; so we'd have to start the buffer after that. maxsec EQU (8*1024)/bufsiz ; 8K / number of bytes per sector lxi h,ovlend ; get start of buffer shld bufadr ; store in linkage section mvi a,maxsec ; get size of buffer, in sectors sta bufsec ; store that, too. ENDIF;NOT osbrn1 IF iobyt ; (actually, we ought to do this for everybody) call iniadr ;Initialize the BIOS addresses mvi c,gtiob ;Get current I/O byte call bdos ;From CP/M sta coniob ;Remember where console is ENDIF;iobyt IF bbI OR bbII lxi d,siotbl ; Load the address of the status able mvi c,siolen ; Length of status table siolup: ;Loop back here for each command byte ldax d ; Load the first byte into A inx d ; Index the pointer out mnprts ; Send it to the status port dcr c ; Decrement the byte counter jnz siolup ; Jump back for more commands ENDIF;bbI or bbII IF lobo ;[hh] lxi d,siotbl ;[hh] address of status table mvi c,siolen ;[hh] length of the table siolup: ;[hh] loop here for each command byte ldax d ;[hh] load first byte into A inx d ;[hh] index pointer to next bute sta mnprts ;[hh] send it to status port A sta mnprts+2 ;[hh] and to status port B dcr c ;[hh] decrement the counter jnz siolup ;[hh] loop back for more commands mvi a,05H ;[hh] value for 300 baud sta baudrt ;[hh] starting default for port A sta baudrt+4 ;[hh] and for port B sta speed ;[hh] tell program they're set mvi a,0E4H ;[hh] value for port A sta port ;[hh] tell program we've set this, too mvi a,0D0H ;[hh] port A baud rate value sta port+1 ;[hh] save this as well, for consistancy ENDIF ;lobo ret ; return from system-dependent routine bdosvr: ds 1 ; space to save the BDOS version number ; IF iobyt ; This one is hopefully the last "improvement" in view of GENERIC ;Kermit. It uses for Character-I/O the BIOS-routines ( instead of the ;"normal" BDOS routines. What does it give us (hopefully) : More speed, ;higher chance of success ( I/O byte implemented in BIOS [if at all]), ;but no "extra" device handling - that's done by BDOS. ; ; How do we "get" the call-adresses? Location 0 has a JMP Warm-Boot ;in CP/M which points into the second location of the BIOS JMP-Vector. The ;next three locations of the JMP-Vector point to the CONSTAT,CONIN,CONOUT ;BIOS-routines. CONOUT wants the character in C. ; ;- Bernie Eiben iniadr: lhld 1 ;get BIOS Warmstart-address lxi d,3 ;next adress is CONSTAT in BIOS dad d shld bconst+1 ;stuff it into the call-instruction lxi d,3 ;next adress is CONIN in BIOS dad d shld bconin+1 ; lxi d,3 ;next adress is CONOUT in BIOS dad d shld bcnout+1 lxi d,3 ;next address is LIST in BIOS dad d shld blsout+1 ret ;And return bconst: jmp $-$ ;Call BIOS directly (filled in by iniadr) bconin: jmp $-$ ;Call BIOS directly (filled in by iniadr) bcnout: jmp $-$ ;Call BIOS directly (filled in by iniadr) blsout: jmp $-$ ; .... ENDIF;iobyt IF mikko ;currently for MIKROMIKKO only ; copy command block into memory-mapped SIO. movmik: di ;disable interrupts movmk1: ldax d ;Get a register value mov m,a ;Output it inx d ;Next value dcr c ;Decrement counter jnz movmk1 ;Repeat until done ei ret ENDIF;mikko IF bbI OR bbII OR lobo ; List of commands to set up SIO channel A for asynchronous operation. siotbl: DB 18H ; Channel reset DB 18H ; another, in case register 0 wasn't selected DB 04H ; Select register 4 DB 44H ; 1 stop bit, clock*16 DB 01H ; Select register 1 DB 00H ; No interrupts enabled DB 03H ; Select register 3 DB 0C1H ; Rx enable, 8 bit Rx character DB 05H ; Select register 5 DB 0EAH ; Tx enable, 8 bit Tx character, ; raise DTR and RTS siolen equ $-siotbl ; length of command list ENDIF;bbI or bbII OR lobo IF mikko ; command list to set SIO chip back to normal state miotbl: db 3 ;reg. 3 db sioo3 db 5 ;reg. 5 db sioo5 db 4 ;reg. 4 db sioo4 db 0 ;reselect reg. 0 miolen equ $-miotbl ;MikroMikko SIO table length (original values) ; command list to set up SIO chip for operation with Kermit mintbl: db 3 ;reg. 3 db sion3 db 5 ;reg. 5 db sion5 db 4 ;reg. 4 db sion4 db 0 ;reselect reg. 0 minlen equ $-mintbl ;MikroMikko SIO table length (KERMIT values) ENDIF;mikko ; ; ; system-dependent termination processing ; If we've changed anything, this is our last chance to put it back. sysexit: IF mikko lxi d,miotbl ;Load the adress of original reg values mvi c,miolen ;Length of table lxi h,sioac ;Send data to ch A SIO registers call movmik mvi a,07FH ;Set ch A mask to use just 7 bits sta chmask ENDIF;mikko ret ; ; system-dependent processing for start of CONNECT command ; syscon: ret conmsg: ; Messages printed when entering transparent (CONNECT) mode: IF robin ; for Robin, control-S key is hidden db ' (Type Left Arrow to send CTRL-S)',cr,lf,'$' ENDIF;robin IF trs80 ; for TRS-80, the preferred escape key is hidden db ' (Control-_ is the Down-Arrow key on the TRS-80 keyboard)' db cr,lf,'$' ENDIF;trs80 IF cpt85xx ; for CPT-85xx, some graphics map "funny" to keyboard in CP/M db ' (Use CODE + SHIFT + 1/2 key to generate a Control-\)' db cr,lf,'$' ENDIF;cpt85xx ; ; ; syscls - system-dependent close routine ; called when exiting transparent session. ; syscls: ret ; ; ; sysinh - help for system-dependent special functions. ; called in response to ?, after listing all the ; system-independent escape sequences. ; sysinh: IF apmmdm OR robin OR dmII OR bbII OR bbI OR cpt85xx OR heath OR lobo lxi d,inhlps ; we got options... call prtstr ; print them. ENDIF;apmmdm OR robin OR dmII OR bbII OR bbI OR cpt85xx OR heath OR lobo ret ;additional, system-dependent help for transparent mode ; (two-character escape sequences) inhlps: IF robin OR dmII OR bbII OR bbI OR cpt85xx OR heath OR lobo db cr,lf,'B Transmit a BREAK' ENDIF;robin OR dmII OR bbII OR bbI OR cpt85xx OR heath OR lobo IF apmmdm OR heath OR lobo db cr,lf,'D Drop the line' ENDIF;apmmdm OR heath OR lobo db '$' ;[hh] table terminator ; ; sysint - system dependent special functions ; called when transparent escape character has been typed; ; the second character of the sequence is in A (and in B). ; returns: ; non-skip: sequence has been processed ; skip: sequence was not recognized sysint: ani 137O ; convert lower case to upper, for testing... IF apmmdm cpi 'D' ;Disconnect Modem? jnz intc00 ;No. xra a ;Yes, hangup the modem sta mnmodm ret ; command has been executed intc00: ENDIF;apmmdm IF heath cpi 'D' ; drop line? jnz intc00 ; no: try next function character mdmdrp: in mnport+acemcr ; (we also get here from sysbye) ani 0FFH-acedtr out mnport+acemcr ; yes: drop DTR mvi a,50 ; for half a second call delay in mnport+acemcr ori acedtr out mnport+acemcr ; and then restore it ret intc00: ENDIF;heath IF robin OR dmII OR bbI OR bbII OR cpt85xx OR heath OR lobo cpi 'B' ; send break? jz sendbr ; yes, go do it. return nonskip when through. ENDIF;robin OR dmII OR bbI OR bbII OR cpt85xx OR heath OR lobo IF lobo ;[hh] cpi 'D' ;[hh] disconnect? jz discon ;[hh] yes, go do it. nonskip return when done. ENDIF ;lobo jmp rskp ; take skip return - command not recognized. ; IF bbI OR bbII ;[cjc] send break on Kaypro and bbII ; Officially, a "break" is 300 milliseconds of "space" (idle line is ; "mark"). (or maybe 200 milliseconds; I forget.) The timing isn't ; usually that critical, but we'll make an attempt, at least. Sending ; too long a break can cause some modems to hang up. sendbr: ; First, make sure the transmitter is really empty. (The SIO sets ; "transmitter buffer empty" when it can accept another character; ; the previous character is still being shifted onto the line. ; Another status bit, "all done", is set to indicate that the ; transmitter is really idle. sndbr1: mvi a,1 ; select Read Register 1 out mnprts in mnprts ; read the contents ani 1 ; test "all done" flag jz sndbr1 ; loop until it's nonzero. ; ; Next, set the "send break" bit to start the transmitter spacing. mvi a,5 ; select Write Register 5 out mnprts mvi a,0FAH ; Tx enable, 8 bit Tx character, Send Break, out mnprts ; DTR and RTS on. ; ; Now, delay for 30 hundredths of a second mvi a,30 ; delay count call delay ; ; Time's up. Put transmitter back in normal state (data byte is the ; same as the one in siotbl: for Write Register 5) and return. mvi a,5 ; select Write Register 5 out mnprts mvi a,0EAH ; Tx enable, 8 bit Tx character, out mnprts ; DTR and RTS on. ret ; done. ENDIF;bbI OR bbII IF lobo ;[hh] This routine sends a break tone or disconnects a modem ; (those that respond to it) by setting the DTR line low ; for 300 ms. ; sendbr: mvi a,05H ;[hh] write register 5 call outctl ;[hh] send it to control port mvi a,0FAH ;[hh] value to send break tone jmp sndbr1 ;[hh] ; discon: mvi a,05H ;[hh] write register 5 call outctl ;[hh] send it to the control port mvi a,06AH ;[hh] DTR off and break tone on sndbr1: call outctl ;[hh] send to control port mvi a,30 ;[hh] delay count for 300 ms. call delay ;[hh] wait a while... mvi a,05H ;[hh] write register 5 call outctl ;[hh] get it's attention mvi a,0EAH ;[hh] normal 8 bits, DTR on, RTS on, etc. call outctl ;[hh] restore SIO ret ; outctl: sta mnprts ;[hh] ret ENDIF ;lobo ; IF bbI OR bbII OR cpt85xx OR heath OR lobo ; ;[cjc] Delay routine. Called with time (hundredths of seconds) in A. ; The inner loop delays 1001 T-states, assuming no wait states are ; inserted; this is repeated CPUSPD times, for a total delay of just ; over 0.01 second. (CPUSPD should be set to the system clock rate, ; in units of 100KHz: for an unmodified Kaypro II, that's 25 for ; 2.5 MHz. Some enterprising soul could determine whether or not the ; Kaypro actually inserts a wait state on instruction fetch (a common ; practice); if so, the magic number at delay2 needs to be decreased. ; (We also neglect to consider time spent at interrupt level). ; ; called by: sendbr ; destroys BC delay: mvi c,cpuspd ; Number of times to wait 1000 T-states to ; make .01 second delay delay2: mvi b,70 ; Number of times to execute inner loop to ; make 1000 T-state delay delay3: dcr b ; 4 T-states (* 70 * cpuspd) jnz delay3 ; 10 T-states (* 70 * cpuspd) dcr c ; 4 T-states (* cpuspd) jnz delay2 ; 10 T-states (* cpuspd) ; total delay: ((14 * 70) + 14) * cpuspd ; = 1001 * cpuspd dcr a ; 4 T-states jnz delay ; 10 T-states ret ; grand total: ((1001 * cpuspd) + 14) * a ENDIF;bbI OR bbII OR cpt85xx OR heath OR lobo ; ; ; sysflt - system-dependent filter ; called with character in E. ; if this character should not be printed, return with A = zero. ; preserves bc, de, hl. ; note: ,,, and are always discarded. sysflt: mov a,e ; get character for testing IF mikko cpi 'O'-100O ;Control-O's lock keyboard rnz ; if not control-O, it's ok. xra a ; don't allow control-O out. ENDIF;mikko ret ; ; ; system-dependent processing for BYE command. ; for apmmdm, heath, and lobo, hang up the phone. sysbye: IF lobo ;[hh] call discon ;[hh] force modem to hang up ENDIF;lobo ret ; ; This is the system-dependent command to change the baud rate. ; DE contains the two-byte value from the baud rate table; this ; value is also stored in 'speed'. sysspd: ;[hh] set the speed for a lobo MAX-80 IF lobo mov a,e ;[hh] get the parsed value setbd: sta baudrt ;[hh] and send it to the baud rate port ret ;[hh] ENDIF;lobo ; ; Speed tables ; (Note that speed tables MUST be in alphabetical order for later ; lookup procedures, and must begin with a value showing the total ; number of entries. The speed help tables are just for us poor ; humans. ; db string length,string,divisor (2 identical bytes or 1 word) ; [Toad Hall] IF bbI OR brain OR delphi OR lobo ;[hh] spdtbl: db 10h ;16 entries db 03h,'110$', 02h,02h db 04h,'1200$', 07h,07h db 05h,'134.5$', 03h,03h db 03h,'150$', 04h,04h db 04h,'1800$', 08h,08h db 05h,'19200$', 0fh,0fh db 04h,'2000$', 09h,09h db 04h,'2400$', 0ah,0ah db 03h,'300$', 05h,05h db 04h,'3600$', 0bh,0bh db 04h,'4800$', 0ch,0ch db 02h,'50$', 00h,00h db 03h,'600$', 06h,06h db 04h,'7200$', 0dh,0dh db 02h,'75$', 01h,01h db 04h,'9600$', 0eh,0eh sphtbl: db cr,lf,' 50 75 110 134.5 150 300 600 1200' db cr,lf,' 1800 2000 2400 3600 4800 7200 9600 19200$' ENDIF;bbI OR brain OR delphi OR lobo ;[hh] ; The following conditionals were once a huge if not statement. There ; wasn't enough room to add the lobo to the list, so it had to be broken ; into 2, which you can't do with an if not. I redid it as two ifs and ; applied them to those that wouldn't set baud. [Hal Hostetler] IF robin OR gener OR dmII OR vector OR z100 OR trs80 OR telcon spdtbl equ 0 ; SET BAUD not supported. sphtbl equ 0 ENDIF;robin OR gener OR dmII OR vector OR z100 OR trs80 OR telcon ; IF mmdI OR osi OR cpm3 OR apmmdm spdtbl EQU 0 ;[hh] SET BAUD not supported. sphtbl EQU 0 ;[hh] ran out of room above... ENDIF;mmdI OR osi OR cpm3 OR apmmdm ; ; This is the system-dependent SET PORT command. ; HL contains the argument from the command table. sysprt: IF lobo ;[hh] mov a,e ;[hh] get the data port value and store at sta outmd3+1 ;[hh] the two places we use... sta inpmd2+1 ;[hh] MNPORT in the overlay sta port ;[hh] inform program of the change in ports inr a ;[hh] status port = data port + 1 in the Lobo sta outmd1+1 ;[hh] store it at the three places... sta inpmd1+1 ;[hh] we use MNPRTS... sta outctl+1 ;[hh] in the overlay mov a,d ;[hh] now get the baud rate port value sta getbd+1 ;[hh] store it in the two places we use... sta setbd+1 ;[hh] BAUDRT in the overlay sta port+1 ;[hh] don't need to, but keeps it consistant getbd: lda baudrt ;[hh] get baud rate value from port sta speed ;[hh] tell STAT. baud rate for each port ;[hh] is independant of the other ENDIF ;lobo IF iobyt mov a,m ;Get the I/O byte sta prtiob ;Save the desired IO byte for this port inx h ;Point at next entry mov a,m ;Get the output function sta prtfun ;Save it ENDIF;iobyt IF iobyt AND robin inx h ;Point at next entry mov a,m ;Get the hardware address for the port sta prtadr ;Store it ENDIF;iobyt AND robin ret ; ; ; Port tables for Lobo MAX-80 IF lobo ;[hh] ; help text prhtbl: db cr,lf,'RS-232 port A or B$' ; ; command table prttbl: db 02H ;[hh] two entries db 01H,'A$',0E4H,0D0H db 01H,'B$',0E6H,0D4H ENDIF ;lobo ; ; ; Port tables for GENERIC CPM 2.2 IF gener ; help text prhtbl: db cr,lf,'CRT device' db cr,lf,'PTR device' db cr,lf,'TTY device' db cr,lf,'UC1 device' db cr,lf,'UR1 device' db cr,lf,'UR2 device$' ; command table prttbl: db 06H ;Six devices to choose from db 03H,'CRT$' dw crtptb db 03H,'PTR$' dw ptrptb db 03H,'TTY$' dw ttyptb db 03H,'UC1$' dw uc1ptb db 03H,'UR1$' dw ur1ptb db 03H,'UR2$' dw ur2ptb ; port entry table ; table entries are: ; db iobyte-value, BDOS output function, reserved crtptb: db crtio,conout,0 ptrptb: db ptrio,punout,0 ttyptb: db ttyio,conout,0 uc1ptb: db uc1io,conout,0 ur1ptb: db ur1io,punout,0 ur2ptb: db ur2io,punout,0 ENDIF;gener ; ; ; Port tables for DECmate II or MicroMikko ; IF dmII OR mikko ; help text prhtbl: db cr,lf,'COMMUNICATIONS port$' ; command table prttbl: db 01H ;Only one port known at this point db 0EH,'COMMUNICATIONS$' dw comptb ;address of info ; port entry table ; table entries are: ; db iobyte-value, BDOS output function, reserved comptb: db batio,punout,0 ENDIF;dmII OR mikko IF iobyt prtfun: db punout ;Function to use for output to comm port prtiob: db batio ;I/O byte to use for communicating coniob: db defio ;I/O byte to use for console ENDIF;iobyt IF NOT (iobyt OR lobo) ;[hh] prttbl equ 0 ; SET PORT is not supported prhtbl equ 0 ENDIF;NOT iobyt OR lobo ; ; ; Set up screen display for file transfer ; called with kermit version in DE ; sysscr: push d ; save version for a bit lxi d,outlin ; clear screen, position cursor call prtstr ; do it pop d ; get Kermit's version IF NOT (osi OR crt) ; got cursor control? call prtstr ; print it mvi e,'[' ; open bracket call outcon ; print it (close bracket is in outln2) lxi d,sysver ; get name and version of system module call prtstr lxi d,outln2 ; yes, print field names call prtstr lda dbgflg ; is debugging enabled? ora a rz ; finished if no debugging lxi d,outln3 ; set up debugging fields call prtstr ENDIF;NOT (osi OR crt) ret ; ; Calculate free space for current drive ; returns value in HL sysspc: lda bdosvr ;cpm3's alloc vect may be in another bank cpi 30H ;cpm3 or later? jm cp2spc ;no: use cp/m 2 algorithm lda fcb ;If no drive, get ora a ; logged in drive jz dir180 dcr a ;FCB drive A=1 normalize to be A=0 jmp dir18a dir180: mvi c,rddrv call bdos dir18a: mov e,a ;drive in e mvi c,getfs ;get free space BDOS funct call bdos ;returns free recs (3 bytes in buff..buff+2) mvi b,3 ;conv recs to K by 3 bit shift dir18b: xra a ;clear carry mvi c,3 ;for 3 bytes lxi h,buff+3 ;point to addr + 1 dir18c: dcx h ;point to less sig. byte mov a,m ;get byte rar ;carry -> A -> carry mov m,a ;put back byte dcr c ;for all bytes (carry not mod) jnz dir18c dcr b ;shift 1 bit 3 times jnz dir18b mov e,m ;get least sig byte inx h mov d,m ;get most sig byte xchg ;get K free in HL ret ; the rest are CP/M 2.2 systems, so use the alloc vector cp2spc: mvi c,getalv ;Address of CP/M Allocation Vector call bdos xchg ;Get its length lhld bmax inx h lxi b,0 ;Initialize Block count to zero dir19: push d ;Save allocation address ldax d mvi e,8 ;set to process 8 blocks dir20: ral ;Test bit jc dir20a inx b dir20a: mov d,a ;Save bits dcx h mov a,l ora h jz dir21 ;Quit if out of blocks mov a,d ;Restore bits dcr e ;count down 8 bits jnz dir20 ;do another bit pop d ;Bump to next count of Allocation Vector inx d jmp dir19 ;process it dir21: pop d ;Clear Allocation vector from stack mov l,c ;Copy block to 'HL' mov h,b lda bshiftf ;Get Block Shift Factor sui 3 ;Convert from records to thousands rz ;Skip shifts if 1K blocks dir22: dad h ;Multiply blocks by 'K per Block' dcr a jnz dir22 ret ; ; ; selmdm - select modem port ; selcon - select console port ; selmdm is called before using inpmdm or outmdm; ; selcon is called before using inpcon or outcon. ; For iobyt systems, diddle the I/O byte to select console or comm port; ; For Decision I, switches Multi I/O board to console or modem serial ; port. [Toad Hall] ; For the rest, does nothing. ; preserves bc, de, hl. selmdm: IF iobyt lda prtiob ;Set up for output to go to the comm port sta iobyte ;Switch byte directly ENDIF;iobyt ret selcon: IF iobyt lda coniob ;Set up for output to go to the console port sta iobyte ;Switch directly ENDIF;iobyt ret ; ; Get character from console, or return zero. ; result is returned in A. destroys bc, de, hl. ; inpcon: IF NOT iobyt mvi c,dconio ;Direct console I/O BDOS call. mvi e,0FFH ;Input. call BDOS ENDIF;NOT iobyt IF iobyt call bconst ;Get the status ora a ;Anything there? rz ;No, forget it call bconin ;Yes, get the character ENDIF;iobyt ret ; ; ; Output character in E to the console. ; destroys bc, de, hl ; outcon: IF NOT iobyt mvi c,dconio ;Console output bdos call. call bdos ;Output the char to the console. ENDIF;NOT iobyt IF iobyt mov c,e ;Character call bcnout ;to Console ENDIF;iobyt ret ; ; ; outmdm - output a char from E to the modem. ; the parity bit has been set as necessary. ; returns nonskip; bc, de, hl preserved. outmdm: IF osi OR apple OR lobo ;[hh] push h outmd1: lxi h,mnprts ;address of the port status register outmd2: mov a,m ; get port status in A ani output ;Loop till ready. jz outmd2 outmd3: lxi h,mnport ;address of port data register mov m,e ; write the character pop h ret ENDIF;osi OR apple OR lobo IF inout in mnprts ;Get the output done flag. ani output ;Is it set? jz outmdm ;If not, loop until it is. mov a,e out mnport ;Output it. ret ENDIF;inout IF iobyt ;**** Note that we enter from outpkt with the I/O byte already set up for ; output to go to the comm port push h push b lda prtfun ;Get the output function mov c,a ;Into C call bdos ;And output the character pop b pop h ret ENDIF;iobyt IF cpm3 push h push b mvi c,auxout ;Output to the aux output device call bdos pop b pop h ret ENDIF;cpm3 ; ; ; get character from modem; return zero if none available. ; for IOBYT systems, the modem port has already been selected. ; destroys bc, de, hl. inpmdm: IF iobyt call bconst ;Is Char at COMM-Port? ora a ;something there? rz ; return if nothing there call bconin ; data present. read data. ENDIF;iobyt IF cpm3 mvi c,auxist call bdos ;is char at auxin? ora a ;something there? rz ;no mvi c,auxin call bdos ;read char from auxin ENDIF;cpm3 IF inout ;Note: modem port should already be selected for mdI. [Toad Hall] in mnprts ;Get the port status into A. ani input ;See if the input ready bit is on. rz ;If not then return. in mnport ;If so, get the char. ENDIF;inout ret ; return with character in A ; ; flsmdm - flush comm line. ; Modem is selected. ; Currently, just gets characters until none are available. flsmdm: call inpmdm ; Try to get a character ora a ; Got one? jnz flsmdm ; If so, try for another ret ; Receiver is drained. Return. ; ; ; outlpt - output character in E to printer ; console is selected. ; preserves de. outlpt: push d ; save DE in either case IF NOT iobyt mvi c,lstout call bdos ;Char to printer ENDIF;NOT iobyt IF iobyt mov c,e call blsout ENDIF;iobyt pop d ; restore saved register pair ret ; ; ; Screen manipulation routines ; csrpos - move to row B, column C ; ; csrpos for terminals that use a leadin sequence followed ; by (row + 31.) and (column + 31.) ; or (row) and (column) ; csrpos: push b ; save coordinates lxi d,curldn ; get cursor leadin sequence call prtstr ; print it pop h ; restore coordinates IF ams2 mov a,l ; get column ENDIF ;ams2 IF ams3 mov a,h ; get row adi (' '-1) ; space is row one ENDIF;ams3 mov e,a push h call outcon ; output row pop h IF ams2 mov a,h ; get row ENDIF ;ams2 IF ams3 mov a,l ; get column adi (' '-1) ; space is column one ENDIF;ams3 mov e,a jmp outcon ; output it and return ; ; position to various fields: ; for the Kermits with cursor positioning, the display looks like this: ; 5 10 15 20 25 30 35 ; +----|----|----|----|----|----|----|... ; 1 | ; 2 | Kermit-80 v4.0 [system] ; 3 | ; 4 |Number of packets: ____ ; 5 |Number of retries: ____ ; 6 |File name: ____________ ; 7 |... ; 8 |... ; 9 |RPack: ___(if debugging)... ; 10 | ; 11 |SPack: ___(if debugging)... ; 12 | ; 13 |Kermit-80 A:> (when finished) ; IF NOT (osi OR crt) scrnp: lxi b,4*100H+20 jmp csrpos scrnrt: lxi b,5*100H+20 jmp csrpos scrfln: lxi b,6*100H+12 call csrpos clreol: lxi d,tk jmp prtstr screrr: lxi b,7*100H+1 call csrpos jmp clreol scrst: lxi b,8*100H+1 call csrpos jmp clreol rppos: lxi b,9*100H+8 call csrpos jmp clreol sppos: lxi b,11*100H+8 call csrpos jmp clreol scrend: lxi b,13*100H+1 call csrpos clreos: lxi d,tj jmp prtstr ENDIF;NOT (osi OR crt) ; ; delchr - make delete look like a backspace. Unless delete is a printing ; character, we just need to print a backspace. (we'll output clrspc ; afterwards) ; For Kaypro and Vector General, delete puts a blotch on the screen. ; For Apple and Osborne 1, delete moves but doesn't print. delchr: IF NOT (bbI OR vector OR apple OR osbrn1) mvi e,bs ;get a backspace jmp outcon ENDIF;NOT (bbI OR vector OR apple OR osbrn1) ; erase the character at the current cursor position clrspc: mvi e,' ' call outcon mvi e,bs ;get a backspace jmp outcon ; erase the current line clrlin: lxi d,eralin jmp prtstr ; erase the whole screen, and go home. preserves b (but not c) clrtop: lxi d,erascr jmp prtstr ; Some frequently-used routines (duplicates of those in CP4MIT): ; prcrlf - output a CR/LF ; prtstr - output string in DE ; rskp - return, skipping over error return prcrlf: lxi d,crlf prtstr: mvi c,prstr jmp bdos rskp: pop h ; Get the return address inx h ; Increment by three inx h inx h pchl ; Copy block of data ; source in HL, destination in DE, byte count in BC ; called by: cp4sys, mfname ; mover: db 0EDh,0B0h ; Z80 LDIR instruction ret ; ; ; Miscellaneous messages ; crlf: db cr,lf,'$' cfgmsg: db 'configured for $' ;**************************Terminal tables**************************** IF NOT (osi OR crt) ; got cursor control? outln2: db ']',cr,lf,cr,lf,'Number of packets:' db cr,lf,'Number of retries:' db cr,lf,'File name:$' outln3: db cr,lf,cr,lf ; debugging messages db cr,lf,'RPack:' db cr,lf ; blank line in case of long packet db cr,lf,'SPack:$' ENDIF;NOT (osi OR crt) IF ams3 ;a straight VT52 sysver: db 'AMSTRAD CP/M PLUS$' outlin: db esc,'H',esc,'J',cr,lf,tab,tab,'$' erascr: db esc,'H',esc,'J$' ;Clear screen and go home. eralin: db cr,esc,'K$' ;Clear line. curldn: db esc,'Y$' ;cursor leadin ttab: ;Table start location. ta: db esc,'A$',0 ;Cursor up. tb: db esc,'B$',0 ;Cursor down. tc: db esc,'C$',0 ;Cursor right. td: db esc,'D$',0 ;Cursor left te: db esc,'E$',0 ;Clear display tf: db esc,'F$',0 ;Enter Graphics Mode tg: db esc,'G$',0 ;Exit Graphics mode th: db esc,'H$',0 ;Cursor home. ti: db esc,'I$',0 ;Reverse linefeed. tj: db esc,'J$',0 ;Clear to end of screen. tk: db esc,'K$',0 ;Clear to end of line. ENDIF;ams3 IF ams2 ;This one is a special sysver: db 'AMSTRAD CP/M 2.2$' outlin: db 0CH,cr,lf,tab,tab,'$' erascr: db 0CH,'$' ;Clear screen and go home. eralin: db 0DH,12H,'$' ;Clear line. curldn: db 1FH,'$' ;cursor leadin ttab: ;Table start location. ta: db 0BH,'$',0,0 ;Cursor up. tb: db 0AH,'$',0,0 ;Cursor down. tc: db 09H,'$',0,0 ;Cursor right. td: db 08H,'$',0,0 ;Cursor left te: db 13H,14H,'$',0 ;Clear display tf: db '$',0,0,0 ;(Don't) Enter Graphics Mode tg: db '$',0,0,0 ;(Don't) Exit Graphics mode th: db 1EH,'$',0,0 ;Cursor home. ti: db 0DH,0BH,'$',0 ;Reverse linefeed. tj: db 14H,'$',0,0 ;Clear to end of screen. tk: db 12H,'$',0,0 ;Clear to end of line. ENDIF;ams2 ovlend equ $ ; End of overlay END