IF NOT lasm .printx * CPXPCW.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.08 ; ; 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 ; on the Amstrad range of personal computers. ; ; ; Revision history: ; ;edit 9, 22-Jan-1991 by MF. Added "sysinit" code at "init04" from Kermit ; version 4.08 which had been left out of version 4.09 to reserve ; space for the Amstrad's I/O buffers. Amstrad Kermit now stores ; files correctly (version 4.09 had garbled files). Again, ; this fix comes from Mr. D. P. Arnot of the Scottish Agricultural ; College in the UK. ;edit 8, 16-Jan-1991 by MF. Added a "bs" left out of "delstr" per ; instruction from David P. Arnot of Scottish Agricultural ; College, Auchincruive, Ayr, UK. ; E-mail: ; edit 7, 22 July 1987 by OBSchou to massage code to fit with CPXCOM.ASM ; Had to rename bcnout to bcnot as the former label clashed with ; one of the same name in CPXCOM.ASM. Sorry, folks. ; ; edit 6, 14th July, 1987 by OBSchou for Phillip Wade, Hull University ; Computer Centre. Change to delchr routine for PCW machines, ; as character 127 decimal is a printing character on ; the Amstrad PCW. The routine has been replaced ; by a bs,space,bs,bs string. (OBS Comment - why a total of THREE bs?) ; ; edit 5 9 May 1987 by C.J.MILES@UK.AC.UMRCC ; Hangup phone and clear screen options added to ; terminal mode. ; ; edit 4 23 March 1987 by C.J.MILES@UK.AC.UMRCC ; Use direct input drom SIO for CPC machines instead ; of using BDOS. Improvement reduces time for file ; transfer to 65% of time used by BDOS method. ; Add reverse Prestel baud rate. ; ; edit 3 20 March 1987 by Chris Miles (C.J.MILES@UK.AC.UMRCC) ; (Greater Manchester Archaeological Unit, Manchester Univ.). ; Added support for Amstrad CPC CP/M Plus machines, ; Added 31250 baud rate option, ; Bug fixed in sending BREAK, ; Bug fixed in 1200/75 baud setup ; Machine and CP/M version checks added. ; ; edit 2 11 Febuary, 1987 by OBSchou for Ian Young. ; Changes to the send break routine to change two lines of code from ; ori 018h ; send break, Tx enable ; and ori 0008h ; Tx enable ; ; to ori 01ah ; send break, Tx enable, RTS ; and ori 00ah ; Tx enable, RTS ; ; This is a bug fix to hopefully keep RTS actve during breaks. ; ; [Ed. (OBSchou) 21/1/87. ; This file linked FROM cpxsys.asm, so do NOT rename this ; file. The diffculty of needing a HUGE CPXSYS.ASM file on your floppy ; only to act as a switcher remains, but hopefully will be better in ; the future. If you ARE stuck, then rename this CPXSYS.ASM but add the ; label SYSEDT: to the FAMILY label. A bit messy.] ; ; ; The "author" of this system-dependent file is: ; ; Ian A. Young ; Lattice Logic Ltd ; 9 Wemyss Place ; Edinburgh ; ; Some other addresses: ; ; ian@latlog.uucp ; ...seismo!mcvax!ukc!latlog!ian ; or c/o OBSchou@uk.ac.lut.multics ; ; ... although much of the code concerned was written by others. ; ; Keep module name, edit number, and last revision date in memory. ;sysedt: db 'CPXSYS.ASM (36) 20-Mar-87$' family: db 'CPXPCW.ASM (9) 22-Jan-1991$' ; ; Assembly-time message announcing which version we are building ; IF pcw .printx * Assembling Amstrad PCW Kermit-80 * ENDIF IF cpc .printx * Assembling Amstrad CPC Kermit-80 * ENDIF ; ; Miscellany of parameter settings ; z80 EQU TRUE ; all Amstrads have a Z80. defesc EQU '\'-100O ; The default escape character. vtval EQU 0 ; we don't need VT52 emulation ; ; Amstrad CPC machines use 16 bit I/O address decoding and therefore ; the Z80 instructions OUT (C),A and IN A,(C) must be defined. ; outc EQU 79edh ; IN A,(C) inpc EQU 78edh ; OUT (C),A ; ; hardware information ; ; There is a Z80-DART (Mostek/SGS 8470) at I/O addresses E0..E3 (PCW) ; and FADC..FADD (CPC), and a 8253 programmable divider running it at ; E4..E7 (PCW) and FBDC..FBDF (CPC) ; input EQU 01h ; input data available output EQU 04h ; output buffer ready IF pcw mnport EQU 0E0h ; data register for SIO mnprts EQU 0E1h ; control register for SIO ctc0 EQU 0E4h ; 8253 load counter 0 ctc1 EQU 0E5h ; 8253 load counter 1 ctcmod EQU 0E7h ; 8253 write mode word ENDIF IF cpc mnport EQU 0FADCh ; data register for SIO mnprts EQU 0FADDh ; control register for SIO ctc0 EQU 0FBDCh ; 8253 load counter 0 ctc1 EQU 0FBDDh ; 8253 load counter 1 ctcmod EQU 0FBDFh ; 8253 write mode word ENDIF ; ; SIO input buffering ; siosz EQU 4096 ; size of SIO input buffer siomsk EQU 4095 ; mask for wrapping buffer round ; ; Extended BIOS jump-block addresses; reached through USERF ; sainit EQU 00B6h ; initialise SIO sabaud EQU 00B9h ; set baud rate saparm EQU 00BCh ; fetch SIO parameters teask EQU 00BFh ; find out cursor position cdvers EQU 00E3h ; get version numbers cdinfo EQU 00E6h ; get BIOS system information ; ; System-dependent initialization ; Called once at program start. sysxin: ; continuation of system initialzation ; ; check for correct CP/M version ; mvi c,12 ; get CP/M version BDOS call call bdos mov a,l ; check if CP/M Plus cpi 31h jz init08 lxi d,wrong2 ; point to error message call prtstr mvi c,0 ; warm boot call bdos ; init08: ;[OBS] Moved the Cinfigured for message out as ;[OBS] it is in CPXCOM.ASM ; ; get addresses of BIOS routines ; ; BIOS USERF is used to get to extended BIOS routines ; lhld 1 ; warm boot vector lxi d,87 ; offset to USERF vector dad d ; DE now has USERF vector address shld userf+1 ; ready for jumping to... ; ; BIOS routines for fast character I/O ; lhld 1 ; warm boot vector (#1) lxi d,3 dad d ; next is #2, CONST shld bcnst+1 dad d ; next is #3, CONIN shld bcnin+1 dad d ; next is #4, CONOUT shld bcnot+1 ;[obs] Was bcnout, but this conflicts ;[obs] with a label in CPXCOM.ASM dad d ; next is #5, LIST shld blist+1 dad d ; next is #6, AUXOUT dad d ; next is #7, AUXIN shld baxin+1 lhld 1 ; warm boot vector again lxi d,002Ah ; offset to LISTST (#15) dad d shld lptstat+1 lhld 1 ; warm boot vector again lxi d,0033h ; offset to AUXIST (#18) dad d shld baxist+1 ; ; check if running on correct Amstrad ; call userf dw cdvers IF pcw cpi 0 ENDIF IF cpc cpi 1 ENDIF jnz init06 lxi d,wrong1 ; point to error message call prtstr mvi c,0 call bdos ; ; verify presence of SIO board by asking the BIOS. ; init06: call userf ; C gets 00 if not fitted dw cdinfo xra a ; a <- 0 ora c ; zero => no serial port jnz init03 ; non-zero => OK lxi d,nosio ; snooty message... call prtstr mvi c,0 ; warm boot out of here call bdos init03: ; ; find initial baud rate and other information ; call userf ; gives B=rx baud, C=tx baud, D=stop bits, ; E=parity, H=rx bits, L=tx bits. dw saparm ; get SIO parameters push h ; save bit settings mov a,b ; if TX and RX speeds same, they are OK cmp c jz init01 cpi 8 ; rx=1200? jnz init02 ; no, can't be Prestel mov a,c cpi 2 ; tx=75? jnz init02 ; no, can't be Prestel lxi b,0 ; otherwise 1200/75 comes out as 0s. init01: push b ; assign value to SPEED pop h shld speed init02: ; here if we leave it as is pop h ; get bit settings mov a,l ; no of TX data bits set sui 5 ; make into 00, 01, 10, 11. rrc rrc rrc sta txbits ; we may need it later ; ; set handshake mode: there are two parts to this, interrupts and ; hardware handshake. The MODE byte used by the firmware expresses ; this combination as -(int*2 + hand*1). Thus, both options on would ; be (-3) or 0FDh. ; ; Here, we set the interrupt part of the mode on; it helps the BIOS cope. ; Unfortunately, >sigh<, according to Soft971, this will only work ; if you have BIOS V1.4 or higher. I have no idea what would happen ; if we tried random hanshake mode flags with lower versions, so we ; just skip over if it would be dangerous... ; call userf ; fetch all parameters dw saparm sta orgmode ; remember original mode for later call userf ; get BIOS version to B,C dw cdvers mov a,b ; BIOS major version number (eg 1) ora a ; if zero, too low... jz init04 cpi 1 ; if not 1, definitely OK jnz init05 mov a,c ; otherwise, its 1.X; want >= 4 cpi 4 jm init04 ; <4 => too low init05: call userf ; get the original flags back dw saparm xri 0FFh ; make mode into bit mask inr a ori 2 ; set interrupt mode xri 0FFh ; turn back into mode value inr a call userf ; feed change back to BIOS dw sainit init04: ; come here if not setting mode ; Locate large buffers for multi-sector I/O and SIO input buffering. ; Space above ovlend is available for buffers; we have pretty well the machine ; to ourselves in an Amstrad PCW because they all gave 61K TPAs. We don't even ; bother to perform any checking. ; We don't want to use more than maxsec for disk buffers because ; 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. ; maxsec EQU (8*1024)/bufsiz ; 8K / number of bytes per sector lxi h,ovlend+siosz ; get start of buffer shld bufadr ; store in linkage section mvi a,maxsec ; get size of buffer, in sectors sta bufsec ; store that, too. ret ; return from system-dependent routine ; ; message complaining about wrong Amstrad machine ; wrong1: db 'Error - This Kermit will only run on the Amstrad ' IF pcw db 'PCW 8256/8512' ENDIF IF cpc db 'CPC 464/664/6128' ENDIF db cr,lf,'$' ; ; message complaining about version of CP/M being used ; wrong2: db 'Error - Incorrect CP/M version, needs CP/M 3.x' db cr,lf,'$' ; ; message complaining of no SIO board ; nosio: db 'Error - No SIO option fitted to this machine' db cr, lf, '$' ; ; jumps to BIOS character I/O routines. ; Addresses filled in by initialisation code above. ; bcnst: jmp $-$ ; console status bcnin: jmp $-$ ; console input bcnot: jmp $-$ ; console output [obs - was bcnout] blist: jmp $-$ ; printer output baxin: jmp $-$ ; aux port input baxist: jmp $-$ ; aux port status lptstat:jmp $-$ ; printer status ; ; Other BIOS routines ; userf: jmp $-$ ; call extended BIOS function ; ; saved value of some original parameters ; orgmode:ds 1 txbits: ds 1 ; ; system-dependent termination processing ; If we've changed anything, this is our last chance to put it back. sysexit: call userf ; fetch firmware parameters dw saparm lda orgmode ; replace with original mode call userf ; inform BIOS dw sainit ret ; ; system-dependent processing for start of CONNECT command ; syscon: lxi d,conmsg ; how to get escape char message call prtstr ret conmsg: ; Messages printed when entering transparent (CONNECT) mode: IF pcw db '(Use boxed minus key next to space bar to generate a Control-\)' ENDIF db cr,lf,'$' ; ; 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: lxi d,inhlps ; we got options... call prtstr ; print them. ret ; ; additional, system-dependent help for transparent mode ; (two-character escape sequences) ; inhlps: db cr,lf,'B Transmit a BREAK' db cr,lf,'H Hangup using DTR' db cr,lf,'W Wipe screen clear' 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... cpi 'B' ; send break ? jz sendbr ; yes, go do it. return nonskip when through. cpi 'H' ; hang up ? jz hangup cpi 'W' ; clear screen ? jz clrtop jmp rskp ; take skip return - command not recognized. ; ; Hangup (drop DTR) and Break send routine ; hangup: mvi d,0ah ; set up hangup bit mask mvi e,255 ; time for hangup is 2 1/2 secs jmp setbit ; skip Tx empty test sendbr: mvi d,9ah ; set up break bit mask mvi e,30 ; time for break is 300 ms sndbr1: mvi a,1 ; select Read Register 1 IF pcw ; allow 8 bit I/O instructions out mnprts in mnprts ; read the contents ENDIF IF cpc ; use 16 bit I/O instructions lxi b,mnprts dw outc ; OUT (C),A dw inpc ; IN A,(C) ENDIF ani 1 ; test "all done" flag jz sndbr1 ; loop until it's nonzero. ; ; Next, set the break or hangup bit on the SIO. ; setbit: mvi a,5 ; select Write Register 5 IF pcw out mnprts ENDIF IF cpc dw outc ; OUT (C),A ENDIF lda txbits ; get txbits (already in correct bit positions) ora d ; send break, Tx Enable, RTS IF pcw out mnprts ENDIF IF cpc dw outc ; OUT (C),A ENDIF ; ; Now, delay for duration of hangup or break ; mov a,e ; delay count call delay ; ; Time's up. Put transmitter back in normal state and return. ; mvi a,5 ; select Write Register 5 IF pcw out mnprts ENDIF IF cpc lxi b,mnprts dw outc ; OUT (C),A ENDIF lda txbits ; get txbits again ori 8ah ; Reset break, Tx Enable, RTS IF pcw out mnprts ENDIF IF cpc dw outc ; OUT (C),A ENDIF ret ; done. ; ; 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 ; ; 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; this ; value is also stored in 'speed'. ; sysspd: push d ; move to HL for firmware pop h mov a,h ; if h=0 then Prestel rates ora a jnz spd01 ; if not 1200/75 then skip lxi h,0802H ; else set 1200/75 into HL jmp spd03 ; jump to normal setup spd01: cpi 11h ; if h=11h then reverse Prestel jnz spd02 ; if not 75/1200 then skip lxi h,0208h ; else set 75/1200 into HL jmp spd03 ; jump to normal setup spd02: cpi 10h ; if h=10h then 31250 baud jnz spd03 ; if not 31250 then skip to normal setup mvi a,36h ; set 8253 for mode 2 binary count lxi b,ctcmod ; output to CTC mode register dw outc lxi b,ctc0 ; select transmit clock mov a,4 ; timer value for 31250 (04h) dw outc mov a,0 ; timer value for 31250 (04h) dw outc lxi b,ctc0 ; select receive clock mov a,4 ; timer value for 31250 (04h) dw outc mov a,0 ; timer value for 31250 (04h) dw outc ret spd03: call userf ; set whatever we have now... dw sabaud ; using BIOS routine 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 identical bytes or 1 word) ; spdtbl: db 12h ;18 entries db 03h,'110$', 03h,03h db 04h,'1200$', 08h,08h db 07h,'1200/75$', 00h,00h ; real values faked up when required db 05h,'134.5$', 04h,04h db 03h,'150$', 05h,05h db 04h,'1800$', 09h,09h db 05h,'19200$', 0fh,0fh db 04h,'2400$', 0ah,0ah db 03h,'300$', 06h,06h db 05h,'31250$',10h,10h ; flag to direct setup routine db 04h,'3600$', 0bh,0bh db 04h,'4800$', 0ch,0ch db 02h,'50$', 01h,01h db 03h,'600$', 07h,07h db 04h,'7200$', 0dh,0dh db 02h,'75$', 02h,02h db 07h,'75/1200$',11h,11h ; real values faked up when required db 04h,'9600$', 0eh,0eh sphtbl: db cr,lf,lf db 'Normal rates: 50 75 110 134.5 150 300 600' db cr,lf db ' 1200 1800 2400 3600 4800 7200 9600 19200' db cr,lf,lf db 'High speed : 31250 (only between Amstrads)' db cr,lf,lf db 'Split rates : 1200/75 (Rx=1200, Tx= 75)' db cr,lf db ' 75/1200 (Rx= 75, Tx=1200)' db cr,lf,'$' ; ; This is the system-dependent SET PORT command. ; HL contains the argument from the command table. ; sysprt: ret prttbl equ 0 ; SET PORT is not supported prhtbl equ 0 ; ; selmdm - select modem port ; selcon - select console port ; selmdm is called before using inpmdm or outmdm; ; selcon is called before using inpcon or outcon. ; preserves bc, de, hl. ; selmdm: selcon: ret ; ; Get character from console, or return zero. ; result is returned in A. destroys bc, de, hl. ; inpcon: call bcnst ; get console status ora a ; anything there? rz ; no, forget it jmp bcnin ; yes, get the character ; ; Output character in E to the console. ; destroys bc, de, hl ; outcon: mov a,e ; TAB? cpi tab jz out001 mov c,e ; set correct arg register jmp bcnot ; output to console via BIOS [obs was bcnout] ; ; perform tab expansion ourselves ; out001: call userf ; get column in L dw teask mov a,l ; a <- column 0..n ani 7 ; column 0..7 xri 0FFh ; not(col 0..7) adi 9 ; a is 8-(colf7) out002: ora a ; any left? rz ; return if not dcr a ; one less now, anyhow push psw ; save over BIOS call (just in case) mvi c,' ' ; print one space call bcnot ;[obs was bcnout] call suck ; in case any stuff coming in pop psw ; fetch count back jmp out002 ; and go round again ; ; 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 cpc push b ; save BC for CPC 16 bit I/O ENDIF outmd1: call xsuck ; keep checking for incoming characters IF pcw in mnprts ; get the output done flag. ENDIF IF cpc lxi b,mnprts dw inpc ; IN A,(C) ENDIF ani output ; is it set? jz outmd1 ; if not, loop until it is. mov a,e IF pcw out mnport ; output it. ENDIF IF cpc lxi b,mnport dw outc ; OUT (C),A pop b ; restore BC ENDIF ret ; ; get character from modem; return zero if none available. ; destroys bc, de, hl. ; inpmdm: call suck ; get any characters pending lhld sioct ; count of chars in buffer mov a,h ; or together to get result ora l rz ; not got any, return now dcx h ; down count shld sioct lhld siord ; read pointer mov c,m ; fetch character ** NB TO C FOR NOW ** lxi d,1-ovlend ; bump pointer, subtract base dad d mov a,h ; mask high byte of offset ani siomsk/256 mov h,a lxi d,ovlend ; add in base again dad d shld siord mov a,c ; get to proper register 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. ; ; SIO input buffer handling. The buffer pointers are held as pointers into ; the buffer. The read pointer ; is to the next unused character, the write pointer to the next unused space. ; siord: dw ovlend ; next char to read siowr: dw ovlend ; next char to write sioct: dw 0 ; number in buffer xsuck: push d ; save regs version of suck push b push h call suck pop h pop b pop d ret ; ; suck ; ; this routine is called whenever it would be possible that some ; characters might be available in the SIO device; they are all ; transferred (there may be up to 4 pending) to the buffer. ; ; all registers are destroyed ; suck: IF pcw call baxist ; check input status via BDOS ora a ; check if zero ENDIF IF cpc lxi b,mnprts ; get input status directly dw inpc ; IN A,(C) ani input ; mask for Rx ready ENDIF rz ; return if no IF pcw call baxin ; fetch character via BDOS ENDIF IF cpc lxi b,mnport ; fetch character directly from SIO dw inpc ; IN A,(C) ENDIF lhld siowr ; write pointer mov m,a ; put character lxi d,1-ovlend ; take off base, bump pointer dad d mov a,h ; top byte of offset ani siomsk/256 ; masked off mov h,a lxi d,ovlend ; add on base again dad d shld siowr ; replace pointer lhld sioct ; bump count in buffer inx h shld sioct jmp suck ; go round in case any more ; ; outlpt - output character in E to printer ; console is selected. ; preserves de. outlpt: push d ; save DE in either case mov c,e ; correct arg register call blist 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 mov a,h ; get row adi (' '-1) ; space is row one mov e,a push h call outcon ; output row pop h mov a,l ; get column adi (' '-1) ; space is column one mov e,a jmp outcon ; output it and return ; ; 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 pcw ;[6] OBS for Phillip Wade lxi d,delstr ;[5] send a string rather than a single character call prtstr delstr: db bs,' ',bs,'$' ;[OBS] Was bs,space,bs,bs ENDIF ;pcw 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 pcw sysver: db 'Amstrad PCW with SIO option$' ENDIF IF cpc sysver: db 'Amstrad CPC with CP/M Plus$' ENDIF outlin: db esc,'H',esc,'J',cr,lf,' $' 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 '$',0,0,0 ;Enter Graphics Mode tg: db '$',0,0,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. ovlend equ $ ; End of overlay END