IF NOT lasm .printx * CPXAC.ASM * ENDIF ;NOT lasm IF lasm .printx Error: Z80 macro assembler (i.e. M80) required END ENDIF ;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. ; ; ; ; ; Keep module name, edit number, and last revision date in memory. ; family: db 'CPXCA.ASM (1) 3-DEC-2015$' ; First entry for V4.11 ; ; Assembly time message to let me know I'm building the right version. ; .printx * Assembling Kermit-80 for AVR-CP/M * SC16IS740_ADDR equ 90H ;SC16IS740 I2C address. (8bit, A0=VDD, A1=VDD) OUTSIZE equ 64 VERSION_MIN equ 0304H ;Minimum AVR-CP/M firmware version required ; Virtual I2C Interface VI2C_STAT equ 05h VI2C_CTRL equ 05h VI2C_BLEN equ 06h VI2C_ADR equ 07h VI2C_BSIZ equ 66 ;largest message size including address byte (SLA) ;----------------------------- ISC16IS740 UART ------------------------------- I2C_UART_PORT equ 50H I2C_UART_RHR equ I2C_UART_PORT+00H ;R Receive Holding I2C_UART_THR equ I2C_UART_PORT+00H ;W Transmit Holding I2C_UART_IER equ I2C_UART_PORT+01H ;R/W Interrupt Enable I2C_UART_FCR equ I2C_UART_PORT+02H ;W FIFO Control TX_FIFO_RES equ 04H ; TX FIFO reset RX_FIFO_RES equ 02H ; RX FIFO reset FIFO_ENABLE equ 01H ; FIFO enable I2C_UART_IIR equ I2C_UART_PORT+02H ;R Interrupt Identification I2C_UART_LCR equ I2C_UART_PORT+03H ;R/W Line Control DLAB equ 80H ; Devisor latch enable WLS0 equ 01H ; Word Length Select Bit 0 WLS1 equ 02H ; Word Length Select Bit 1 for 8 bit word STB equ 04H ; Stop bit count - 2 stop bits I2C_UART_MCR equ I2C_UART_PORT+04H ;R/W Modem Control RTS equ 02H ;RTS pin, 1 = active (low) DTR equ 01H ;DTR pin (not on '740) I2C_UART_LSR equ I2C_UART_PORT+05H ;R Line Status TXE equ 40H ; THR and TSR empty TXRDY equ 20H ; THR empty RX_FE equ 08H ; Framig error RX_PE equ 04H ; Parity error RX_OE equ 02H ; Overrun error RXRDY equ 01H ; Receved byte available I2C_UART_MSR equ I2C_UART_PORT+06H ;R Modem Status I2C_UART_SPR equ I2C_UART_PORT+07H ;R/W Scratchpad I2C_UART_TCR equ I2C_UART_PORT+06H ;R/W Transmission Control I2C_UART_TLR equ I2C_UART_PORT+07H ;R/W Trigger Level I2C_UART_TXLVL equ I2C_UART_PORT+08H ;R Transmit FIFO Level I2C_UART_RXLVL equ I2C_UART_PORT+09H ;R Receive FIFO Level I2C_UART_EFCR equ I2C_UART_PORT+0FH ;R/W Extra Features I2C_UART_DLL equ I2C_UART_PORT+00H ;R/W divisor latch LSB I2C_UART_DLH equ I2C_UART_PORT+01H ;R/W divisor latch MSB I2C_UART_EFR equ I2C_UART_PORT+02H ;R/W Enhanced Feature I2C_UART_XON1 equ I2C_UART_PORT+04H ;R/W Xon1 word I2C_UART_XON2 equ I2C_UART_PORT+05H ;R/W Xon2 word I2C_UART_XOFF1 equ I2C_UART_PORT+06H ;R/W Xoff1 word I2C_UART_XOFF2 equ I2C_UART_PORT+07H ;R/W Xoff2 word z80 set TRUE ;This one emulates an Z80. .z80 ;---------------------------------------------------------------------- ; Macros ;---------------------------------------------------------------------- ; make a message table ; usage: ; label: mkmsgtab mkm_tab macro x n?msg defl 0 irp y, n?msg defl n?msg+1 endm db n?msg irp y, db '&y','$' endm endm ; make a message table ; usage: ; label: mkmsgtab mkms_tab macro x n?msg defl 0 irp y, n?msg defl n?msg+1 endm db n?msg irp y, ifnb db y,'$' else db '$' endif endm endm ;---------------------------------------------------------------------- ; Messages ;---------------------------------------------------------------------- umsg_tab: ; 0 1 2 3 4 5 mkms_tab <'UART',,' not', ' detected',', crystal frequency: ','!'> fmsg_tab: mkm_tab fdim_msg: db ' MHz.',cr,lf,'$' fw_msg: db 'AVR firmware to old, at least version 3.4 neded.','$' exit_msg: db cr,lf,'Exiting!',cr,lf,'$' ;---------------------------------------------------------------------- ; Utilities ;---------------------------------------------------------------------- ; Print message from table ; ; hl: message table address ; first byte is number of table entries ; a: number of message to print (0 based, index in table) ; ; If index is out of range, print message #0 pdecoded: push bc push de push hl ld bc,0 cp (hl) ; number of messages in table jr c,pdc_1 ld a,c pdc_1: inc hl ld e,a ld a,'$' inc e jr pdc_2 pdc_nxt_str: cpir pdc_2: dec e jr nz,pdc_nxt_str ex de,hl call prtstr pop hl pop de pop bc ret ;---------------------------------------------------------------------- ; output bytes to ports ; ; hl: tables of port,value pairs: ; db n ;number of pairs ; db port1,val1, port2,val2,... portn,valn ; ... ; db 0 ; Terminate table ioinil: push bc ld b,(hl) ;count inc hl io1_lp: ld c,(hl) ;port address inc hl outi jr nz,io1_lp pop bc ret ;---------------------------------------------------------------------- vi2c_setup_chk: ld hl,chkbuf vi2c_setup: out (VI2C_BLEN),a ld a,h out (VI2C_ADR+1),a ld a,l out (VI2C_ADR+0),a ret ;---------------------------------------------------------------------- prspeedmsg: ld hl,fmsg_tab call pdecoded ld de,fdim_msg call prtstr ret ;---------------------------------------------------------------------- ; ;---------------------------------------------------------------------- fw_check: ld bc,000CH ; out (c),b ;write 0 to version port in a,(c) cp 1 ;result should be 0 ret nc ;exit (a != 0) if it wasn't inc b out (c),b in h,(c) ;get MAJOR inc b out (c),b in l,(c) ;get MINOR ld de,VERSION_MIN xor a ;clear carry sbc hl,de sbc a,a ;z if hl >= VERSION_MIN ret ;---------------------------------------------------------------------- uart_check: ld a,3 ;2 byte + SLA call vi2c_setup_chk ld a,2 ;write cmd out (VI2C_CTRL),a uc_0: in a,(VI2C_CTRL) ;do: get i2c result bit 7,a ; jr nz,uc_0 ;while busy cp 0FH jr nz,uc_err ;error in transaction in a,(VI2C_BLEN) cp 3 jr nz,uc_err inc hl inc hl in a,(I2C_UART_SPR) cp (hl) jr nz,uc_err ld (hl),42H ld a,2 out (VI2C_CTRL),a in a,(I2C_UART_SPR) cp (hl) jr nz,uc_err xor a jr uc_1 uc_err: ld a,1 uc_1: ld c,a ld hl,umsg_tab xor a call pdecoded ld a,1 add a,c call pdecoded ld a,3 call pdecoded ld a,4 add a,c call pdecoded ld a,c or a ret ;---------------------------------------------------------------------- chkbuf: db SC16IS740_ADDR db (I2C_UART_SPR - I2C_UART_PORT) shl 3 ;address of scratch pad register db 5AH ;---------------------------------------------------------------------- speedtest: ld hl,spt_tab ;init UART in loop back mode call ioinil ; and fill tx fifo with 60 chars in a,(I2C_UART_MSR) ;Clear Modem Status Register in a,(I2C_UART_LSR) ;Clear Line Status Register in a,(I2C_UART_RHR) ;Clear Receiver Buffers in a,(I2C_UART_RHR) ld a,2 ;start write transaction out (VI2C_CTRL),a ; get time stamp in a,(41H) ;lsb ms ld e,a in a,(42H) ;msb ms ld d,a in a,(43H) ;lsb seconds ld c,a spt_1: in a,(I2C_UART_RXLVL) ;wait till all 60 char in rx fifo cp 60 jr nz,spt_1 ; get 2nd time stamp in a,(41H) ;lsb ms ld l,a in a,(42H) ;msb ms ld h,a in a,(43H) ;lsb seconds sub c ;seconds diff jr z,spt_3 ld bc,1000 spt_2: ;convert s to ms add hl,bc dec a jr nz,spt_2 spt_3: sbc hl,de ;hl = elapsed time (ms) for 60 chars ld d,h ld e,l inc hl srl h rr l ld bc,500 add hl,bc xor a ;clear carry spt_4: inc a sbc hl,de jr nc,spt_4 dec a ret spt_tab: db (spt_tab_end - ($+1))/2 db I2C_UART_LCR, DLAB+03H ;Set devisor latch access bit db I2C_UART_DLL, low 96 ;1200 bit/s at 1.832 MHz db I2C_UART_DLH, high 96 ;Out to the MSB divisor port db I2C_UART_LCR, 03H ;Disable Divisor Access Latch db I2C_UART_FCR, 07H ;Clear and enable fifos db I2C_UART_MCR, 10H ;Enable loopback db I2C_UART_IER, 0 ;Set no interrupts db VI2C_ADR+0, low outbuf db VI2C_ADR+1, high outbuf db VI2C_BLEN, 60+2 spt_tab_end: ;---------------------------------------------------------------------- ; ;---------------------------------------------------------------------- sysxin: ; continuation of system initialisation from sysinit call fw_check jr nz,si_exit_f call uart_check jr nz,si_exit call speedtest ld (clk_div),a call prspeedmsg ld hl,6 ;set default baud rate ld (speed),hl ex de,hl call sysspd ld a,RTS+DTR out (I2C_UART_MCR),a ret si_exit_f: ld de,fw_msg call prtstr si_exit: ld de,exit_msg call prtstr jp 0 ; ld a,07H ;Enable and clear fifos ; out (I2C_UART_FCR),a ; ; ld a,03H ;8N1 ; out (I2C_UART_LCR),a ; ; ; system-dependent KERMIT termination processing ; If we've changed anything, this is our last chance to put it back. ; sysexit: 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 .8080 ; ; sysinh - help for system-dependent special functions. ; called in response to ?, after listing all the ; system-independent escape sequences. ; sysinh: lxi d, inhlps call prtstr ret ; Additional, system-dependent help for transparent mode ; (two-character escape sequences) inhlps: db cr,lf,'B Transmit a BREAK (0.3s)' db cr,lf,'L Transmit a LONG BREAK (1.8s)' db '$' ; string terminator .z80 ; ; 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: and 5FH ; convert lower case to upper, for testing... cp 'B' ; send break ? jr z,sendbr ; then jump to send break routine cp 'L' ; long break ? jr z,longbr ; then jump to long break routine jp rskp ; take skip return - command not recognised ; ; Break routines ; longbr: ld e,180 ; time for long break is 1800 ms jr setbit sendbr: ld e,25 ; time for break is 300 ms setbit: in a,(I2C_UART_LCR) set 6,a ; Break contol bit out (I2C_UART_LCR),a ; ; Now, delay for duration of hangup or break ld a,e ; delay count call delay ; ; Time's up. Put transmitter back in normal state and return. in a,(I2C_UART_LCR) res 6,a ; Break contol bit out (I2C_UART_LCR),a ret ; done. .8080 ; ; 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'. ; .z80 sysspd: ld hl,0 ld a,(clk_div) ld b,a sysspd_1: add hl,de djnz sysspd_1 ld a,l ld (sysspd_dll),a ld a,h ld (sysspd_dlh),a ld hl,sysspd_tab call ioinil in a,(I2C_UART_MSR) ;Clear Modem Status Register in a,(I2C_UART_LSR) ;Clear Line Status Register in a,(I2C_UART_RHR) ;Clear Receiver Buffers in a,(I2C_UART_RHR) ret sysspd_tab: db (sysspd_tab_end - ($+1))/2 db I2C_UART_LCR, DLAB+03H ;Set devisor latch access bit db I2C_UART_DLL sysspd_dll: ds 1 ;1200 bit/s at 1.832 MHz db I2C_UART_DLH sysspd_dlh: ds 1 ;Out to the MSB divisor port db I2C_UART_LCR, 03H ;Disable Divisor Access Latch db I2C_UART_FCR, 07H ;Clear and enable fifos db I2C_UART_MCR, 00H ;Enable loopback db I2C_UART_IER, 0 ;Set no interrupts sysspd_tab_end: ; ; 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 spdtbl: db 15 ; Number of entries db 3,'110$' dw 1047 db 6,'115200$' dw 1 db 4,'1200$' dw 96 db 3,'150$' dw 768 db 5,'19200$' dw 6 db 4,'2400$' dw 48 db 5,'28800$' dw 5 db 3,'300$' dw 384 db 5,'38400$' dw 3 db 3,'450$' dw 256 db 4,'4800$' dw 24 db 5,'57600$' dw 2 db 3,'600$' dw 192 db 2,'75$' dw 1536 db 4,'9600$' dw 12 IF 0 sphtbl: db cr,lf,' 75 110 150 300 450 600 1200 2400' db cr,lf,'4800 9600 19200 28800 38400 57600 115200$' ENDIF sphtbl: db cr,lf,' 110 300 600 2400 9600 28800 57600' db cr,lf,' 75 150 450 1200 4800 19200 38400 115200$' ; ; 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. ; 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: jr omflush ; ; Get character from console, or return zero. ; result is returned in A. destroys bc, de, hl. ; inpcon: ld c,dconio ;Direct console I/O BDOS call. ld e,0FFH ;Input. call BDOS ret ; ; Output character in E to the console. ; destroys bc, de, hl ; outcon: ld 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: push hl ld hl,(outptr) ld (hl),e ;return buffered char inc hl ld (outptr),hl ld a,(outcnt) inc a ld (outcnt),a cp OUTSIZE jr nc,omflush_1 ;buffer full pop hl ret omflush: ld a,(outcnt) or a ret z scf push hl omflush_1: push bc ld c,a ;outcnt ld b,a jr c,$+4 ld b,1 ld hl,outbuf ld a,h out (VI2C_ADR+1),a ld a,l out (VI2C_ADR+0),a omf_0: in a,(I2C_UART_TXLVL) cp b jr c,omf_0 cp c jr nc,omf_2 ld c,a omf_2: ld a,c add a,2 out (VI2C_BLEN),a ld a,2 ;start write transaction out (VI2C_CTRL),a ld hl,outbuf+2 ld (outptr),hl ld a,(outcnt) sub c ld (outcnt),a jr z,omfex push de ld d,h ld e,l ld b,0 add hl,bc ld c,a ldir pop de omfex: pop bc pop hl 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: ld a,(inpcnt) ;any buffered chars? dec a jp m,imdrdi2c ;no, buffer empty ld (inpcnt),a ;save updated buffer counter ld hl,(inpptr) ld a,(hl) ;return buffered char inc hl ld (inpptr),hl ;save buffer pointer ret imdrdi2c: in a,(I2C_UART_RXLVL) ;get rx fifo count or a ret z ;fifo is empty, return 0 ; prepare fifo read inc a ;+ slave address ld hl,inbuf call vi2c_setup inc hl ld (hl),0 ;select subaddr 0 (RHR) for next read ld a,3 ;write 1 byte (subaddr.), then read fifo out (VI2C_CTRL),a in a,(VI2C_CTRL) ;get i2c result xor 01h and 11h ;transfer completed? jr nz,imrdex in a,(VI2C_BLEN) ;get actual transfer count sub 2 ;- (slave address + char to return now) jr c,imrdex ld (inpcnt),a ;save new buffer count ld a,(hl) inc hl ld (inpptr),hl ;save buffer pointer ret imrdex: xor a ret .8080 ; ; 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 ;---------------------------------------------------------------------- sysver: db 'AVR-CP/M' db '$' ;---------------------------------------------------------------------- clk_div: db 1 ;default div inpptr: dw 0 inpcnt: db 0 inbuf: db SC16IS740_ADDR ds VI2C_BSIZ-1 outptr: dw outbuf+2 outcnt: db 0 outbuf: db SC16IS740_ADDR db 0 ;RHR subaddress ds OUTSIZE ;---------------------------------------------------------------------- IF lasm LINK CPXVDU.ASM ; get terminal defs etc ENDIF ;lasm