5 ; KERMIT - (Celtic for "FREE")
7 ; This is the CP/M-80 implementation of the Columbia University
8 ; KERMIT file transfer protocol.
12 ; Copyright June 1981,1982,1983,1984,1985
15 ; Originally written by Bill Catchings of the Columbia University Center for
16 ; Computing Activities, 612 W. 115th St., New York, NY 10025.
18 ; This file contains the system-dependent code and data for KERMIT
19 ; on the Amstrad range of personal computers.
24 ;edit 9, 22-Jan-1991 by MF. Added "sysinit" code at "init04" from Kermit
25 ; version 4.08 which had been left out of version 4.09 to reserve
26 ; space for the Amstrad's I/O buffers. Amstrad Kermit now stores
27 ; files correctly (version 4.09 had garbled files). Again,
28 ; this fix comes from Mr. D. P. Arnot of the Scottish Agricultural
30 ;edit 8, 16-Jan-1991 by MF. Added a "bs" left out of "delstr" per
31 ; instruction from David P. Arnot of Scottish Agricultural
32 ; College, Auchincruive, Ayr, UK.
33 ; E-mail: <D.P.Arnot@edinburgh.ac.uk>
34 ; edit 7, 22 July 1987 by OBSchou to massage code to fit with CPXCOM.ASM
35 ; Had to rename bcnout to bcnot as the former label clashed with
36 ; one of the same name in CPXCOM.ASM. Sorry, folks.
38 ; edit 6, 14th July, 1987 by OBSchou for Phillip Wade, Hull University
39 ; Computer Centre. Change to delchr routine for PCW machines,
40 ; as character 127 decimal is a printing character on
41 ; the Amstrad PCW. The routine has been replaced
42 ; by a bs,space,bs,bs string. (OBS Comment - why a total of THREE bs?)
44 ; edit 5 9 May 1987 by C.J.MILES@UK.AC.UMRCC
45 ; Hangup phone and clear screen options added to
48 ; edit 4 23 March 1987 by C.J.MILES@UK.AC.UMRCC
49 ; Use direct input drom SIO for CPC machines instead
50 ; of using BDOS. Improvement reduces time for file
51 ; transfer to 65% of time used by BDOS method.
52 ; Add reverse Prestel baud rate.
54 ; edit 3 20 March 1987 by Chris Miles (C.J.MILES@UK.AC.UMRCC)
55 ; (Greater Manchester Archaeological Unit, Manchester Univ.).
56 ; Added support for Amstrad CPC CP/M Plus machines,
57 ; Added 31250 baud rate option,
58 ; Bug fixed in sending BREAK,
59 ; Bug fixed in 1200/75 baud setup
60 ; Machine and CP/M version checks added.
62 ; edit 2 11 Febuary, 1987 by OBSchou for Ian Young.
63 ; Changes to the send break routine to change two lines of code from
64 ; ori 018h ; send break, Tx enable
65 ; and ori 0008h ; Tx enable
67 ; to ori 01ah ; send break, Tx enable, RTS
68 ; and ori 00ah ; Tx enable, RTS
70 ; This is a bug fix to hopefully keep RTS actve during breaks.
72 ; [Ed. (OBSchou) 21/1/87.
73 ; This file linked FROM cpxsys.asm, so do NOT rename this
74 ; file. The diffculty of needing a HUGE CPXSYS.ASM file on your floppy
75 ; only to act as a switcher remains, but hopefully will be better in
76 ; the future. If you ARE stuck, then rename this CPXSYS.ASM but add the
77 ; label SYSEDT: to the FAMILY label. A bit messy.]
80 ; The "author" of this system-dependent file is:
87 ; Some other addresses:
90 ; ...seismo!mcvax!ukc!latlog!ian
91 ; or c/o OBSchou@uk.ac.lut.multics
93 ; ... although much of the code concerned was written by others.
97 ; Keep module name, edit number, and last revision date in memory.
99 ;sysedt: db 'CPXSYS.ASM (36) 20-Mar-87$'
100 family: db 'CPXPCW.ASM (9) 22-Jan-1991$'
104 ; Assembly-time message announcing which version we are building
108 .printx * Assembling Amstrad PCW Kermit-80 *
112 .printx * Assembling Amstrad CPC Kermit-80 *
116 ; Miscellany of parameter settings
118 z80 EQU TRUE ; all Amstrads have a Z80.
119 defesc EQU '\'-100O ; The default escape character.
120 vtval EQU 0 ; we don't need VT52 emulation
123 ; Amstrad CPC machines use 16 bit I/O address decoding and therefore
124 ; the Z80 instructions OUT (C),A and IN A,(C) must be defined.
126 outc EQU 79edh ; IN A,(C)
127 inpc EQU 78edh ; OUT (C),A
130 ; hardware information
132 ; There is a Z80-DART (Mostek/SGS 8470) at I/O addresses E0..E3 (PCW)
133 ; and FADC..FADD (CPC), and a 8253 programmable divider running it at
134 ; E4..E7 (PCW) and FBDC..FBDF (CPC)
137 input EQU 01h ; input data available
138 output EQU 04h ; output buffer ready
141 mnport EQU 0E0h ; data register for SIO
142 mnprts EQU 0E1h ; control register for SIO
143 ctc0 EQU 0E4h ; 8253 load counter 0
144 ctc1 EQU 0E5h ; 8253 load counter 1
145 ctcmod EQU 0E7h ; 8253 write mode word
149 mnport EQU 0FADCh ; data register for SIO
150 mnprts EQU 0FADDh ; control register for SIO
151 ctc0 EQU 0FBDCh ; 8253 load counter 0
152 ctc1 EQU 0FBDDh ; 8253 load counter 1
153 ctcmod EQU 0FBDFh ; 8253 write mode word
157 ; SIO input buffering
159 siosz EQU 4096 ; size of SIO input buffer
160 siomsk EQU 4095 ; mask for wrapping buffer round
163 ; Extended BIOS jump-block addresses; reached through USERF
165 sainit EQU 00B6h ; initialise SIO
166 sabaud EQU 00B9h ; set baud rate
167 saparm EQU 00BCh ; fetch SIO parameters
168 teask EQU 00BFh ; find out cursor position
169 cdvers EQU 00E3h ; get version numbers
170 cdinfo EQU 00E6h ; get BIOS system information
173 ; System-dependent initialization
174 ; Called once at program start.
175 sysxin: ; continuation of system initialzation
177 ; check for correct CP/M version
179 mvi c,12 ; get CP/M version BDOS call
181 mov a,l ; check if CP/M Plus
184 lxi d,wrong2 ; point to error message
189 init08: ;[OBS] Moved the Cinfigured for message out as
190 ;[OBS] it is in CPXCOM.ASM
192 ; get addresses of BIOS routines
194 ; BIOS USERF is used to get to extended BIOS routines
196 lhld 1 ; warm boot vector
197 lxi d,87 ; offset to USERF vector
198 dad d ; DE now has USERF vector address
199 shld userf+1 ; ready for jumping to...
201 ; BIOS routines for fast character I/O
203 lhld 1 ; warm boot vector (#1)
205 dad d ; next is #2, CONST
207 dad d ; next is #3, CONIN
209 dad d ; next is #4, CONOUT
210 shld bcnot+1 ;[obs] Was bcnout, but this conflicts
211 ;[obs] with a label in CPXCOM.ASM
212 dad d ; next is #5, LIST
214 dad d ; next is #6, AUXOUT
215 dad d ; next is #7, AUXIN
217 lhld 1 ; warm boot vector again
218 lxi d,002Ah ; offset to LISTST (#15)
221 lhld 1 ; warm boot vector again
222 lxi d,0033h ; offset to AUXIST (#18)
226 ; check if running on correct Amstrad
237 lxi d,wrong1 ; point to error message
242 ; verify presence of SIO board by asking the BIOS.
244 init06: call userf ; C gets 00 if not fitted
247 ora c ; zero => no serial port
248 jnz init03 ; non-zero => OK
249 lxi d,nosio ; snooty message...
251 mvi c,0 ; warm boot out of here
255 ; find initial baud rate and other information
257 call userf ; gives B=rx baud, C=tx baud, D=stop bits,
258 ; E=parity, H=rx bits, L=tx bits.
259 dw saparm ; get SIO parameters
260 push h ; save bit settings
261 mov a,b ; if TX and RX speeds same, they are OK
265 jnz init02 ; no, can't be Prestel
268 jnz init02 ; no, can't be Prestel
269 lxi b,0 ; otherwise 1200/75 comes out as 0s.
270 init01: push b ; assign value to SPEED
273 init02: ; here if we leave it as is
274 pop h ; get bit settings
275 mov a,l ; no of TX data bits set
276 sui 5 ; make into 00, 01, 10, 11.
280 sta txbits ; we may need it later
282 ; set handshake mode: there are two parts to this, interrupts and
283 ; hardware handshake. The MODE byte used by the firmware expresses
284 ; this combination as -(int*2 + hand*1). Thus, both options on would
287 ; Here, we set the interrupt part of the mode on; it helps the BIOS cope.
288 ; Unfortunately, >sigh<, according to Soft971, this will only work
289 ; if you have BIOS V1.4 or higher. I have no idea what would happen
290 ; if we tried random hanshake mode flags with lower versions, so we
291 ; just skip over if it would be dangerous...
293 call userf ; fetch all parameters
295 sta orgmode ; remember original mode for later
297 call userf ; get BIOS version to B,C
299 mov a,b ; BIOS major version number (eg 1)
300 ora a ; if zero, too low...
302 cpi 1 ; if not 1, definitely OK
304 mov a,c ; otherwise, its 1.X; want >= 4
306 jm init04 ; <4 => too low
308 init05: call userf ; get the original flags back
310 xri 0FFh ; make mode into bit mask
312 ori 2 ; set interrupt mode
313 xri 0FFh ; turn back into mode value
315 call userf ; feed change back to BIOS
317 init04: ; come here if not setting mode
319 ; Locate large buffers for multi-sector I/O and SIO input buffering.
320 ; Space above ovlend is available for buffers; we have pretty well the machine
321 ; to ourselves in an Amstrad PCW because they all gave 61K TPAs. We don't even
322 ; bother to perform any checking.
323 ; We don't want to use more than maxsec for disk buffers because
324 ; if we use too many, the remote end could time out while we're
325 ; writing to disk. maxsec is system-dependent, but for now we'll just
326 ; use 8Kbytes. If you get retransmissions and other protocol errors after
327 ; transferring the first maxsec sectors, lower maxsec.
329 maxsec EQU (8*1024)/bufsiz ; 8K / number of bytes per sector
331 lxi h,ovlend+siosz ; get start of buffer
332 shld bufadr ; store in linkage section
333 mvi a,maxsec ; get size of buffer, in sectors
334 sta bufsec ; store that, too.
337 ret ; return from system-dependent routine
340 ; message complaining about wrong Amstrad machine
342 wrong1: db 'Error - This Kermit will only run on the Amstrad '
347 db 'CPC 464/664/6128'
351 ; message complaining about version of CP/M being used
353 wrong2: db 'Error - Incorrect CP/M version, needs CP/M 3.x'
356 ; message complaining of no SIO board
358 nosio: db 'Error - No SIO option fitted to this machine'
362 ; jumps to BIOS character I/O routines.
363 ; Addresses filled in by initialisation code above.
365 bcnst: jmp $-$ ; console status
366 bcnin: jmp $-$ ; console input
367 bcnot: jmp $-$ ; console output [obs - was bcnout]
368 blist: jmp $-$ ; printer output
369 baxin: jmp $-$ ; aux port input
370 baxist: jmp $-$ ; aux port status
371 lptstat:jmp $-$ ; printer status
374 ; Other BIOS routines
376 userf: jmp $-$ ; call extended BIOS function
379 ; saved value of some original parameters
385 ; system-dependent termination processing
386 ; If we've changed anything, this is our last chance to put it back.
388 call userf ; fetch firmware parameters
390 lda orgmode ; replace with original mode
391 call userf ; inform BIOS
396 ; system-dependent processing for start of CONNECT command
399 lxi d,conmsg ; how to get escape char message
403 conmsg: ; Messages printed when entering transparent (CONNECT) mode:
405 db '(Use boxed minus key next to space bar to generate a Control-\)'
410 ; syscls - system-dependent close routine
411 ; called when exiting transparent session.
417 ; sysinh - help for system-dependent special functions.
418 ; called in response to <escape>?, after listing all the
419 ; system-independent escape sequences.
422 lxi d,inhlps ; we got options...
423 call prtstr ; print them.
427 ; additional, system-dependent help for transparent mode
428 ; (two-character escape sequences)
431 db cr,lf,'B Transmit a BREAK'
432 db cr,lf,'H Hangup using DTR'
433 db cr,lf,'W Wipe screen clear'
434 db '$' ;[hh] table terminator
437 ; sysint - system dependent special functions
438 ; called when transparent escape character has been typed;
439 ; the second character of the sequence is in A (and in B).
441 ; non-skip: sequence has been processed
442 ; skip : sequence was not recognized
444 sysint: ani 137O ; convert lower case to upper, for testing...
445 cpi 'B' ; send break ?
446 jz sendbr ; yes, go do it. return nonskip when through.
449 cpi 'W' ; clear screen ?
451 jmp rskp ; take skip return - command not recognized.
454 ; Hangup (drop DTR) and Break send routine
458 mvi d,0ah ; set up hangup bit mask
459 mvi e,255 ; time for hangup is 2 1/2 secs
460 jmp setbit ; skip Tx empty test
463 mvi d,9ah ; set up break bit mask
464 mvi e,30 ; time for break is 300 ms
466 sndbr1: mvi a,1 ; select Read Register 1
468 IF pcw ; allow 8 bit I/O instructions
470 in mnprts ; read the contents
473 IF cpc ; use 16 bit I/O instructions
479 ani 1 ; test "all done" flag
480 jz sndbr1 ; loop until it's nonzero.
482 ; Next, set the break or hangup bit on the SIO.
485 mvi a,5 ; select Write Register 5
492 lda txbits ; get txbits (already in correct bit positions)
493 ora d ; send break, Tx Enable, RTS
502 ; Now, delay for duration of hangup or break
504 mov a,e ; delay count
507 ; Time's up. Put transmitter back in normal state and return.
509 mvi a,5 ; select Write Register 5
517 lda txbits ; get txbits again
518 ori 8ah ; Reset break, Tx Enable, RTS
528 ; sysflt - system-dependent filter
529 ; called with character in E.
530 ; if this character should not be printed, return with A = zero.
531 ; preserves bc, de, hl.
532 ; note: <xon>,<xoff>,<del>, and <nul> are always discarded.
535 mov a,e ; get character for testing
538 ; system-dependent processing for BYE command.
544 ; This is the system-dependent command to change the baud rate.
545 ; DE contains the two-byte value from the baud rate table; this
546 ; value is also stored in 'speed'.
549 push d ; move to HL for firmware
551 mov a,h ; if h=0 then Prestel rates
553 jnz spd01 ; if not 1200/75 then skip
554 lxi h,0802H ; else set 1200/75 into HL
555 jmp spd03 ; jump to normal setup
557 spd01: cpi 11h ; if h=11h then reverse Prestel
558 jnz spd02 ; if not 75/1200 then skip
559 lxi h,0208h ; else set 75/1200 into HL
560 jmp spd03 ; jump to normal setup
562 spd02: cpi 10h ; if h=10h then 31250 baud
563 jnz spd03 ; if not 31250 then skip to normal setup
564 mvi a,36h ; set 8253 for mode 2 binary count
565 lxi b,ctcmod ; output to CTC mode register
567 lxi b,ctc0 ; select transmit clock
568 mov a,4 ; timer value for 31250 (04h)
570 mov a,0 ; timer value for 31250 (04h)
572 lxi b,ctc0 ; select receive clock
573 mov a,4 ; timer value for 31250 (04h)
575 mov a,0 ; timer value for 31250 (04h)
579 spd03: call userf ; set whatever we have now...
580 dw sabaud ; using BIOS routine
585 ; (Note that speed tables MUST be in alphabetical order for later
586 ; lookup procedures, and must begin with a value showing the total
587 ; number of entries. The speed help tables are just for us poor
590 ; db string length,string,divisor (2 identical bytes or 1 word)
592 spdtbl: db 12h ;18 entries
593 db 03h,'110$', 03h,03h
594 db 04h,'1200$', 08h,08h
595 db 07h,'1200/75$', 00h,00h ; real values faked up when required
596 db 05h,'134.5$', 04h,04h
597 db 03h,'150$', 05h,05h
598 db 04h,'1800$', 09h,09h
599 db 05h,'19200$', 0fh,0fh
600 db 04h,'2400$', 0ah,0ah
601 db 03h,'300$', 06h,06h
602 db 05h,'31250$',10h,10h ; flag to direct setup routine
603 db 04h,'3600$', 0bh,0bh
604 db 04h,'4800$', 0ch,0ch
605 db 02h,'50$', 01h,01h
606 db 03h,'600$', 07h,07h
607 db 04h,'7200$', 0dh,0dh
608 db 02h,'75$', 02h,02h
609 db 07h,'75/1200$',11h,11h ; real values faked up when required
610 db 04h,'9600$', 0eh,0eh
613 db 'Normal rates: 50 75 110 134.5 150 300 600'
615 db ' 1200 1800 2400 3600 4800 7200 9600 19200'
617 db 'High speed : 31250 (only between Amstrads)'
619 db 'Split rates : 1200/75 (Rx=1200, Tx= 75)'
621 db ' 75/1200 (Rx= 75, Tx=1200)'
625 ; This is the system-dependent SET PORT command.
626 ; HL contains the argument from the command table.
631 prttbl equ 0 ; SET PORT is not supported
635 ; selmdm - select modem port
636 ; selcon - select console port
637 ; selmdm is called before using inpmdm or outmdm;
638 ; selcon is called before using inpcon or outcon.
639 ; preserves bc, de, hl.
646 ; Get character from console, or return zero.
647 ; result is returned in A. destroys bc, de, hl.
650 call bcnst ; get console status
651 ora a ; anything there?
653 jmp bcnin ; yes, get the character
656 ; Output character in E to the console.
657 ; destroys bc, de, hl
663 mov c,e ; set correct arg register
664 jmp bcnot ; output to console via BIOS [obs was bcnout]
667 ; perform tab expansion ourselves
669 out001: call userf ; get column in L
671 mov a,l ; a <- column 0..n
673 xri 0FFh ; not(col 0..7)
674 adi 9 ; a is 8-(colf7)
675 out002: ora a ; any left?
677 dcr a ; one less now, anyhow
678 push psw ; save over BIOS call (just in case)
679 mvi c,' ' ; print one space
680 call bcnot ;[obs was bcnout]
681 call suck ; in case any stuff coming in
682 pop psw ; fetch count back
683 jmp out002 ; and go round again
686 ; outmdm - output a char from E to the modem.
687 ; the parity bit has been set as necessary.
688 ; returns nonskip; bc, de, hl preserved.
691 push b ; save BC for CPC 16 bit I/O
694 call xsuck ; keep checking for incoming characters
696 in mnprts ; get the output done flag.
702 ani output ; is it set?
703 jz outmd1 ; if not, loop until it is.
706 out mnport ; output it.
716 ; get character from modem; return zero if none available.
717 ; destroys bc, de, hl.
720 call suck ; get any characters pending
721 lhld sioct ; count of chars in buffer
722 mov a,h ; or together to get result
724 rz ; not got any, return now
729 lhld siord ; read pointer
730 mov c,m ; fetch character ** NB TO C FOR NOW **
732 lxi d,1-ovlend ; bump pointer, subtract base
734 mov a,h ; mask high byte of offset
737 lxi d,ovlend ; add in base again
741 mov a,c ; get to proper register
745 ; flsmdm - flush comm line.
747 ; Currently, just gets characters until none are available.
749 flsmdm: call inpmdm ; Try to get a character
751 jnz flsmdm ; If so, try for another
752 ret ; Receiver is drained. Return.
755 ; SIO input buffer handling. The buffer pointers are held as pointers into
756 ; the buffer. The read pointer
757 ; is to the next unused character, the write pointer to the next unused space.
759 siord: dw ovlend ; next char to read
760 siowr: dw ovlend ; next char to write
761 sioct: dw 0 ; number in buffer
763 xsuck: push d ; save regs version of suck
775 ; this routine is called whenever it would be possible that some
776 ; characters might be available in the SIO device; they are all
777 ; transferred (there may be up to 4 pending) to the buffer.
779 ; all registers are destroyed
783 call baxist ; check input status via BDOS
784 ora a ; check if zero
787 lxi b,mnprts ; get input status directly
789 ani input ; mask for Rx ready
794 call baxin ; fetch character via BDOS
797 lxi b,mnport ; fetch character directly from SIO
801 lhld siowr ; write pointer
802 mov m,a ; put character
804 lxi d,1-ovlend ; take off base, bump pointer
806 mov a,h ; top byte of offset
807 ani siomsk/256 ; masked off
809 lxi d,ovlend ; add on base again
811 shld siowr ; replace pointer
813 lhld sioct ; bump count in buffer
817 jmp suck ; go round in case any more
820 ; outlpt - output character in E to printer
821 ; console is selected.
824 push d ; save DE in either case
825 mov c,e ; correct arg register
827 pop d ; restore saved register pair
831 ; Screen manipulation routines
832 ; csrpos - move to row B, column C
834 ; csrpos for terminals that use a leadin sequence followed
835 ; by (row + 31.) and (column + 31.)
836 ; or (row) and (column)
838 csrpos: push b ; save coordinates
839 lxi d,curldn ; get cursor leadin sequence
840 call prtstr ; print it
841 pop h ; restore coordinates
843 adi (' '-1) ; space is row one
846 call outcon ; output row
849 adi (' '-1) ; space is column one
851 jmp outcon ; output it and return
854 ; delchr - make delete look like a backspace. Unless delete is a printing
855 ; character, we just need to print a backspace. (we'll output clrspc
857 ; For Kaypro and Vector General, delete puts a blotch on the screen.
858 ; For Apple and Osborne 1, delete moves but doesn't print.
860 IF pcw ;[6] OBS for Phillip Wade
861 lxi d,delstr ;[5] send a string rather than a single character
864 delstr: db bs,' ',bs,'$' ;[OBS] Was bs,space,bs,bs
867 mvi e,bs ;get a backspace
870 ; erase the character at the current cursor position
873 mvi e,bs ;get a backspace
876 ; erase the current line
880 ; erase the whole screen, and go home. preserves b (but not c)
886 sysver: db 'Amstrad PCW with SIO option$'
889 sysver: db 'Amstrad CPC with CP/M Plus$'
892 outlin: db esc,'H',esc,'J',cr,lf,' $'
894 erascr: db esc,'H',esc,'J$' ;Clear screen and go home.
895 eralin: db cr,esc,'K$' ;Clear line.
896 curldn: db esc,'Y$' ;cursor leadin
897 ttab: ;Table start location.
898 ta: db esc,'A$',0 ;Cursor up.
899 tb: db esc,'B$',0 ;Cursor down.
900 tc: db esc,'C$',0 ;Cursor right.
901 td: db esc,'D$',0 ;Cursor left
902 te: db esc,'E$',0 ;Clear display
903 tf: db '$',0,0,0 ;Enter Graphics Mode
904 tg: db '$',0,0,0 ;Exit Graphics mode
905 th: db esc,'H$',0 ;Cursor home.
906 ti: db esc,'I$',0 ;Reverse linefeed.
907 tj: db esc,'J$',0 ;Clear to end of screen.
908 tk: db esc,'K$',0 ;Clear to end of line.
910 ovlend equ $ ; End of overlay