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/cpxsb.asm | 696 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 696 insertions(+) create mode 100644 src/cpxsb.asm (limited to 'src/cpxsb.asm') diff --git a/src/cpxsb.asm b/src/cpxsb.asm new file mode 100644 index 0000000..3beefe7 --- /dev/null +++ b/src/cpxsb.asm @@ -0,0 +1,696 @@ +IF NOT lasm +.printx * CPXSB.ASM * +ENDIF ;NOT lasm +; 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. +; +; This file created 16 July, 1987 by OBSchou from code submitted +; by William Rose for the Micromint SB180 systems (as featured in +; BYTE magazine, 1986). This file has been modified to fit in with +; Kermit-80 V4.08 etc. His file KERSYS.ASM is a stripped down version +; of CPXSYS.ASM (formerly CP4SYS.ASM). +; +; revision history: +; KERSYS.ASM - version 0.8 dated 13 Jul 87. +; +; Cutdown CP4SYS.ASM for SB-180/Ampro 230. +; +; +; While this is a single CPU version (to ease editing) the assembler +; conditionals have been kept to identify machine specific code. +; +; Note that the baud setting routine also sets parity, but this does not +; change the parity given by Kermit's 'stat' command. I assume that the +; main body of the program does its own parity check. +; +; Revision history (last entry first) +; +; edit 2, 22 July by OBSchou to massage file to fit with CPXCOM.ASM. +; +; edit 1, 15 July, 1987 by OBSchou for William Rose who submitted +; the code for Kermit-80 V 4.05. Modified code as appropriate +; for 4.08 compatability. +; + +delfac EQU 150 ; Delay factor in SB-180 input loop - a fudge + +; +; Keep module name, edit number, and last revision date in memory. +; +;sysedt: db 'KERSYS.ASM (03) 12-FEB-87 $' ; last SB-180 revision +;sysedt: db 'KERSYS.ASM (04) 12-APR-87 $' ; Telecom Merlin added +;sysedt: db 'KERSYS.ASM (5) 9-May-87 $' ; Minor tidying +;sysedt: db 'KERSYS.ASM (6A) 17-Jun-87 $' ; BT Merlin M2215 only +;sysedt: db 'KERSYS.ASM (7) 19-Jun-87 $' ; SB-180 only +;sysedt: db 'KERSYS.ASM (8) 13-Jul-87 $' ; 6/9 MHz version +family: db 'CPXSB.ASM (2) 22-Jul-87$' ; First entry for V4.08/9 + +; +; Assembly time message to let me know I'm building the right version. +; + +IF sb180 +.printx * Assembling Kermit-80 for Micromint SB-180 * +ENDIF + + +IF sb180 +mnctrla EQU 000H ;Modem control port - CNTLA0 +mnctrlb EQU 002H ;Modem control port - CNTLB0 +mnstat EQU 004H ;Modem status port - STAT0 +mntxdat EQU 006H ;Modem output port - TDR0 +mnrddat EQU 008H ;Modem input port - RDR0 +output EQU 002H ;Transmit data register empty mask - TDRE +input EQU 080H ;Receive data register full mask - RDRF +z80 EQU TRUE ;This one's an HD64180, but Z80 will do +ENDIF + + +sysxin: ; continuation of system initialisation from sysinit + +IF sb180 + lxi h, porbuf ; park the original settings + db 0EDh, 038h, mnctrla ; HD64180 code IN0 g,(m) + mov m, a + inx h + db 0EDh, 038h, mnctrlb + mov m, a + inx h + db 0EDh, 038h, mnstat + mov m, a +ENDIF + ; re-initialise for KERMIT +IF sb6 + mvi h, 08h ; 0000$1001 - 9600 baud, (even) parity + mvi l, 08h ; 'speed' is two bytes +ENDIF + +IF sb9 + mvi h, 21h ; 0010$0001 - 9600 baud, (even) parity + mvi l, 21h ; 'speed' is two bytes +ENDIF + +IF sb180 + shld speed + lxi h, parind + mvi m, 8 ; index for 8 bits, no parity, 2 stop + call setpor +ENDIF + + ret + +porbuf: ds 3 ; original port settings + +; +; system-dependent KERMIT termination processing +; If we've changed anything, this is our last chance to put it back. +; +sysexit: + +IF sb180 + lxi h, porbuf + mov a, m ; output parity + db 0EDh, 039h, mnctrla ; HD64180 code OUT0 (m),g + inx h + mov a, m ; output baud rate + db 0EDh, 039h, mnctrlb + inx h + mov a, m ; output to clear error flags + db 0EDh, 039h, mnstat + ; read twice to reset DCD0 ? + db 0EDh, 038h, mnstat + db 0EDh, 038h, mnstat +ENDIF + + ret + +; +; system-dependent processing for start of CONNECT command +; +syscon: + ret + +conmsg: ; Messages printed when entering transparent (CONNECT) mode: + + db '$' + +; +; 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 sb180 + lxi d, inhlps + call prtstr +ENDIF + + ret + +; Additional, system-dependent help for transparent mode +; (two-character escape sequences) +inhlps: + +IF sb180 + db cr, lf, 'V Cycle port parameters' +ENDIF + + db '$' ; string 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: seqence was not recognized +; +sysint: +; ani 137O ; convert lower case to upper, for testing... + ; does this work? +IF sb180 + cpi 'V' ; cycle port ? + jz pcycl + cpi 'v' + jz pcycl +ENDIF + + jmp rskp ; take skip return - command not recognised + +; Actual commands + +IF sb180 +pcycl: + lxi h, parind ; increment parval, modulo 12 + mov a, m + adi 1 + cpi 13 + jnz pcy1 + mvi a, 1 +pcy1: mov m, a ; update the storage + ; get index of name in parstr + ora a ; clear flags + dcr a + rlc + rlc + mov c, a + mvi b, 0 + lxi h, parstr + inx h + dad b + push h + lxi d, cgmsg1 + call prtstr + pop d + call prtstr + lxi d, cgmsg2 + call prtstr + call setpor ; reset the port + + ret + +cgmsg1: db '<$' +cgmsg2: db '>$' +ENDIF + + ret + +; +; 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 + +; +; 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 + ret + +; +; mdmflt - modem filter +; called with character to be sent to printer in E +; with parity set as appropriate. +; return with accumulator = 0 do do nothing, +; <> 0 to send char in E. +mdmflt: + mov a,e ; get character to test + ret + +; +; prtflt - printer filter +; called with character to be sent to printer in E +; returns with a = 0 to do nothing +; a <> 0 to print it. +; +; this routine for those printer that automatically insert +; a lf on cr, or cr for lf. Should this be shifted to +; the system indep. stuff, in say 4.06? +; +prtflt: + mov a,e ; get character to test + +IF FALSE ; strip out lf from printer stream + ani 7fh ; make sure it is parity less + cpi lf ; is it a line feed? + rnz ; no, print it +; xra a ; yes, don't. + +ENDIF + + ret + +; +; system-dependent processing for BYE command. +; +sysbye: + ret + +; +; This is the system-dependent command to change the baud rate. +; DE contains the two-byte value from the baud rate table; both +; bytes of this value are also stored in 'speed'. +; +sysspd: + +IF sb180 + lxi d, prtmsg ; ask for variables + call prtstr + + lxi d, tbuf ; get suitable string + mvi c, 10 + call bdos + + lxi h, tbuf1 + mov a, m + ora a + jz setpor ; leave unchanged if string zero length + + cpi 3 ; check given length + jnz spd1 ; error - wrong length + inx h + inx h + mov a, m + ani 137O ; convert parity code to upper case + mov m, a + + lxi d, tbuf1 ; get index of given parameter + lxi h, parstr + call sposn + ora a + jnz spd2 ; or fall through if error +spd1: lxi d, invmsg ; invalid input - try again + call prtstr + jmp sysspd + +spd2: adi 3 ; get index to parval table + rrc ; by dividing by 4 + rrc + ani 15 ; mask out high bits + lxi h, parind + mov m, a ; and store it + call setpor ; set up port iaw index and speed bytes + + ret + +prtmsg: db cr,lf,'Enter bit/char, parity, and stop bits required.' + db cr,lf,'(Bit 7/8 Parity N/O/E Stop 1/2 - CR same) : $' +invmsg: db cr,lf,'Invalid parameters$' + +parind: db 8 ; default <8N2> index +parstr: db 48,'7N1$7N2$7O1$7O2$7E1$7E2$' + db '8N1$8N2$8O1$8O2$8E1$8E2$' +parval: db 0,1,16+2,16+3,2,3 + db 4,5,16+6,16+7,6,7 +tbuf db 6 +tbuf1 db 3,'8N2','$$$$' + +; +; Set up the port using the table index in parind and the speed byte +; +setpor: + lxi h, parind + mov a, m + dcr a + lxi h, parval ; table base + mvi b, 0 + mov c, a + dad b ; HL points at parameter value + mov a, m + mov b, a ; park parval + + ani 16 ; the parity switch bit + lxi h, speed + add m ; this is now the baud rate byte + mov c, a ; park it + + mov a, b ; sort out the parameter byte + ani 7 ; b/p/s only wanted + adi 96 ; RE, TE enable + db 0EDh,039h,mnctrla ; output parity etc. + mov a, c + db 0EDh,039h,mnctrlb ; output baud rate + mvi a, 0 + db 0EDh,039h,mnstat ; clear status + db 0EDh,038h,mnstat ; read twice to reset DCD0 + db 0EDh,038h,mnstat + ret + +; +; Find substring position - Leventhal page 293, modified +; enter with subtring in DE and string in HL +; returns index in A or 0 for failure +; +sposn: + mov a, m ; exit if either string length zero + ora a + jz notfnd + sta slen + mov b, a + inx h + shld string + xchg + mov a, m + ora a + jz notfnd + sta sublen + mov c, a + inx h + shld substg + mov a, b + +; no of searches = stringlen - substrlen + 1 +; if substr longer than string quit immediately + + sub c + jc notfnd + inr a + mov c, a + xra a + sta index + +; search until remaining string shorter than substring + +slp1: lxi h, index + inr m + lda sublen + mov b, a + lhld substg + xchg + lhld string + +; try to match substring starting at index + +cmplp: ldax d + cmp m + jnz slp2 + dcr b + jz found + inx h + inx d + jmp cmplp + +; arrive here if match fails + +slp2: dcr c + jz notfnd + lhld string + inx h + shld string + jmp slp1 + +; found, return index + +found: lda index + ret + +; not found, return zero + +notfnd: sub a + ret + +string: ds 2 +substg: ds 2 +slen: ds 1 +sublen: ds 1 +index: ds 1 +ENDIF + + ret + +; +; 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 bytes or 1 word, ab) +; the data byte a is return in A and E, and b in D +; only byte 'a' is the key for the table + +IF sb6 +spdtbl: db 9 ; 9 entries + db 04h,'1200$', 0Bh,0Bh + db 03h,'150$', 0Eh,0Eh + db 05h,'19200$', 01h,01h + db 04h,'2400$', 0Ah,0Ah + db 03h,'300$', 0Dh,0Dh + db 05h,'38400$', 00h,00h + db 04h,'4800$', 09h,09h + db 03h,'600$', 0Ch,0Ch + db 04h,'9600$', 08h,08h + +sphtbl: db cr,lf + db ' 150 300 600 1200 2400 4800 9600 19200 38400$' +ENDIF + +IF sb9 +spdtbl: db 7 ; 7 entries + db 04h,'1200$', 24h,24h + db 05h,'19200$', 20h,20h + db 04h,'2400$', 23h,23h + db 03h,'300$', 26h,26h + db 04h,'4800$', 22h,22h + db 03h,'600$', 25h,25h + db 04h,'9600$', 21h,21h + +sphtbl: db cr,lf + db ' 300 600 1200 2400 4800 9600 19200$' +ENDIF + + +; +; This is the system-dependent SET PORT command. +; HL contains the argument from the command table. +; +sysprt: + ret + +IF sb180 +prttbl EQU 0 ; SET PORT is not supported +prhtbl EQU 0 +ENDIF + +; +; 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 the rest, does nothing. +; preserves bc, de, hl. +; +selmdm: + ret + +selcon: + ret + +; +; Get character from console, or return zero. +; result is returned in A. destroys bc, de, hl. +; +inpcon: + mvi c,dconio ;Direct console I/O BDOS call. + mvi e,0FFH ;Input. + call BDOS + + ret + +; +; Output character in E to the console. +; destroys bc, de, hl +; +outcon: + mvi c,dconio ;Console output bdos call. + call bdos ;Output the char to the console. + + 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 sb180 + db 0EDh,038h,mnstat + ani output ; check status + jz outmdm ; wait until port is available + mov a, e + db 0EDh,039h,mntxdat ; transmit +ENDIF + + ret + +; +; 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 sb180 + lxi h, delfac ; loops to give delay +inpm1: db 0EDh,038h,mnstat + ani input ; check status + jz inpm2 + db 0EDh,038h,mnrddat ; get a byte + ret + +inpm2: dcx h ; no data + mov h, a + ora l + jnz inpm1 ; still tries left + ret ; with zero in A +ENDIF + + ret + +; +; 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. + +; + +; +; lptstat - get the printer status. Return a=0 if ok, or 0ffh if not. +lptstat: +IF iobyte ;[33] + call bprtst ; get status +ENDIF ;iobyte[33] +IF NOT iobyte ;[33] + xra a ; assume it is ok.. this may not be necessary +ENDIF ;iobyte [33] + ret +; +; outlpt - output character in E to printer +; console is selected. +; preserves de. +; +outlpt: + push d ; save DE in either case + call prtflt ; go through printer filter [30] + ana a ; if A = 0 do nothing, + jz outlp1 ; if a=0 do nothing + +outlp1: pop d ; restore saved register pair + ret + +; 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) +delchr: + + mvi e,bs ;get a backspace + jmp outcon + +; 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 + +IF sb180 +sysver: db 'MicroMint SB 180 ' +ENDIF + +IF sb6 + db ' (6 MHz)' +ENDIF + +IF sb9 + db ' (9 MHz)' +ENDIF + db '$' + +IF lasm +LINK CPXVDU.ASM ; get terminal defs etc +ENDIF ;lasm -- cgit v1.2.3