aboutsummaryrefslogtreecommitdiff
path: root/src/cpxsb.asm
diff options
context:
space:
mode:
authorJeff Armstrong <jeff@approximatrix.com>2026-10-25 17:42:06 -0400
committerJeff Armstrong <jeff@approximatrix.com>2026-10-25 17:42:06 -0400
commitebb2ff2af569d5822b70ee555a32d27cf7494816 (patch)
tree7ac5e7b3fa73f7a8d996129086932ecce66374ad /src/cpxsb.asm
downloadkermit-80-ebb2ff2af569d5822b70ee555a32d27cf7494816.tar.gz
kermit-80-ebb2ff2af569d5822b70ee555a32d27cf7494816.zip
Initial import of 4.11HEADmain
Diffstat (limited to 'src/cpxsb.asm')
-rw-r--r--src/cpxsb.asm696
1 files changed, 696 insertions, 0 deletions
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 <escape>?, 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: <xon>,<xoff>,<del>, and <nul> 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