IF NOT lasm .printx * CPXCOM.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 contains part common code required for most if not all ; systems. Specifiacally, SYSINIT, INIADR, MOVER, and DELAY to name ; the most important ones. ; ; revision history: ; ; edit 1, 21st July 1987 by OBSchou. Regretably, I have had to include ; some system dependent IFs, mainly for CRT, TORCH and OSI. Inclusion ; here means simpler family files later on. (Regrettably, the delay ; loop for APMMDM differs too.) ; ; Set the fuzzy timeout value. Range is 1 (VERY short) through 0ffffH to zero ; (maximum). The actual duration is a function of the loop length and the ; processor speed. For now, we'll make it zero for everybody, but feel free ; to change it for your system. ; [OBS] make it a little less than max, say 1000H. More useful. fuzval EQU 1000H ; ; System-dependent initialization ; Called once at program start. sysinit: ; ; [13] Had to move this call to here, as the prtstr routine needs this ; before the config message is sent out. It has only been moved. ; call iniadr ;Initialize the BIOS addresses mvi c,gtiob ;Get current I/O byte call bdos ;From CP/M sta coniob ;Remember where console is mvi c,getvnm ; get the BDOS version number (e.g. 22H, 31H) call bdos mov a,l sta bdosvr ; and store it away for future reference lxi d,cfgmsg ; "configured for " call prtstr lxi d,sysver ; get configuration we're configured for call prtstr ; print it. ; ; If we're set up to do special terminal handling, say what kind ; of terminal we expect... (unless it's the generic 'crt') IF termin lxi d,witmsg ; " with " call prtstr lxi d,ttytyp ; terminal type call prtstr ENDIF;termin call prcrlf ; print CR/LF ; ; now, to work... ; ; locate large buffer for multi-sector I/O ; What we want to do here is find the ccp. Space between ovlend and the ccp ; is available for buffering, except we don't want to use more than maxsec ; buffers (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 ; get start of buffer shld bufadr ; store in linkage section mvi a,maxsec ; get size of buffer, in sectors sta bufsec ; store that, too. call sysxin ; call system specific init code ret ; return from system-dependent routine bdosvr: ds 1 ; space to save the BDOS version number IF NOT iobyt coniob: ds 1 ; space to save copy of IO byte ENDIF ;NOT iobyt ; ; This one is hopefully the last "improvement" in view of GENERIC ;Kermit. It uses for Character-I/O the BIOS-routines ( instead of the ;"normal" BDOS routines. What does it give us (hopefully) : More speed, ;higher chance of success ( I/O byte implemented in BIOS [if at all]), ;but no "extra" device handling - that's done by BDOS. ; ; How do we "get" the call-adresses? Location 0 has a JMP Warm-Boot ;in CP/M which points into the second location of the BIOS JMP-Vector. The ;next three locations of the JMP-Vector point to the CONSTAT,CONIN,CONOUT ;BIOS-routines. CONOUT wants the character in C. ; ;- Bernie Eiben iniadr: lhld 1 ;get BIOS Warmstart-address lxi d,3 ;next adress is CONSTAT in BIOS dad d shld bconst+1 ;stuff it into the call-instruction lxi d,3 ;next adress is CONIN in BIOS dad d shld bconin+1 ; lxi d,3 ;next adress is CONOUT in BIOS dad d shld bcnout+1 lxi d,3 ;next address is LIST in BIOS dad d shld blsout+1 lxi d,10*3 ; get printer status routine dad d shld bprtst ret ;And return bconst: jmp $-$ ;Call BIOS directly (filled in by iniadr) bconin: jmp $-$ ;Call BIOS directly (filled in by iniadr) bcnout: jmp $-$ ;Call BIOS directly (filled in by iniadr) blsout: jmp $-$ ; .... bprtst: jmp $-$ ; Call BIOS directly for printer status IF NOT apmmdm ; Shame about this, but the Apple needs a different delay ; ;[cjc] 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 ENDIF ; NOT apmmdm ; ; ; Set up screen display for file transfer ; called with kermit version in DE ; sysscr: push d ; save version for a bit lxi d,outlin ; clear screen, position cursor call prtstr ; do it pop d ; get Kermit's version IF NOT (osi OR crt) ; got cursor control? call prtstr ; print it mvi e,'[' ; open bracket call outcon ; print it (close bracket is in outln2) lxi d,sysver ; get name and version of system module call prtstr lxi d,outln2 ; yes, print field names call prtstr lda dbgflg ; is debugging enabled? ora a rz ; finished if no debugging lxi d,outln3 ; set up debugging fields call prtstr ENDIF;NOT (osi OR crt) ret ; ; Calculate free space for current drive ; returns value in HL sysspc: lda bdosvr ;cpm3's alloc vect may be in another bank cpi 30H ;cpm3 or later? jm cp2spc ;no: use cp/m 2 algorithm lda fcb ;If no drive, get ora a ; logged in drive jz dir180 dcr a ;FCB drive A=1 normalize to be A=0 jmp dir18a dir180: mvi c,rddrv call bdos dir18a: mov e,a ;drive in e mvi c,getfs ;get free space BDOS funct call bdos ;returns free recs (3 bytes in buff..buff+2) mvi b,3 ;conv recs to K by 3 bit shift dir18b: xra a ;clear carry mvi c,3 ;for 3 bytes lxi h,buff+3 ;point to addr + 1 dir18c: dcx h ;point to less sig. byte mov a,m ;get byte rar ;carry -> A -> carry mov m,a ;put back byte dcr c ;for all bytes (carry not mod) jnz dir18c dcr b ;shift 1 bit 3 times jnz dir18b mov e,m ;get least sig byte inx h mov d,m ;get most sig byte xchg ;get K free in HL ret ; the rest are CP/M 2.2 systems, so use the alloc vector cp2spc: mvi c,getalv ;Address of CP/M Allocation Vector call bdos xchg ;Get its length lhld bmax inx h lxi b,0 ;Initialize Block count to zero dir19: push d ;Save allocation address ldax d mvi e,8 ;set to process 8 blocks dir20: ral ;Test bit jc dir20a inx b dir20a: mov d,a ;Save bits dcx h mov a,l ora h jz dir21 ;Quit if out of blocks mov a,d ;Restore bits dcr e ;count down 8 bits jnz dir20 ;do another bit pop d ;Bump to next count of Allocation Vector inx d jmp dir19 ;process it dir21: pop d ;Clear Allocation vector from stack mov l,c ;Copy block to 'HL' mov h,b lda bshiftf ;Get Block Shift Factor sui 3 ;Convert from records to thousands rz ;Skip shifts if 1K blocks dir22: dad h ;Multiply blocks by 'K per Block' dcr a jnz dir22 ret ; +----|----|----|----|----|----|----|... ; 1 | ; 2 | Kermit-80 v4.0 [system] ; 3 | ; 4 |Number of packets: ____ ; 5 |Number of retries: ____ ; 6 |File name: ____________ ; 7 |<error>... ; 8 |<status>... ; 9 |RPack: ___(if debugging)... ; 10 | ; 11 |SPack: ___(if debugging)... ; 12 | ; 13 |Kermit-80 A:> (when finished) ; ; For the PX-8, the display looks like: ; 5 10 15 20 25 30 35 40 45 50 55 ; +----|----|----|----|----|----|----|----|----|----|----|----|---- ; 1 |Kermit-80 v4.05 [Epson PX-8] Number of retries: ____ ; 2 |Number of packets: ____ File name: ________.___ ; 3 |<error>... ; 4 |<status>... ; 5 |RPack: ___ (if debugging)... ; 6 | ; 7 |SPack: ___ (if debugging)... ; 8 | ; 9 |Kermit-80 A:> (when finished) ; IF NOT px8 ; [29] nppos EQU 4*100h+20 rtpos EQU 5*100h+20 fnpos EQU 6*100h+12 errlin EQU 7 stlin EQU 8 rplin EQU 9 splin EQU 11 prplin EQU 13 ENDIF ; NOT px8 IF px8 nppos EQU 2*100h+20 rtpos EQU 1*100h+59 fnpos EQU 2*100h+51 errlin EQU 3 stlin EQU 4 rplin EQU 5 splin EQU 7 prplin EQU 9 ENDIF ; px8 [29] IF NOT (osi OR crt );[26] scrnp: lxi b,nppos jmp csrpos scrnrt: lxi b,rtpos jmp csrpos scrfln: lxi b,fnpos call csrpos clreol: lxi d,tk jmp prtstr screrr: lxi b,errlin*100H+1 call csrpos jmp clreol scrst: lxi b,stlin*100H+1 call csrpos jmp clreol rppos: lxi b,rplin*100H+8 call csrpos jmp clreol sppos: lxi b,splin*100H+8 call csrpos jmp clreol ; [29] Modify scrend to make the cursor line conditional on use of debugging ; This means that in most cases the entire file transfer will fit on PX-8 lcd scrend: lda dbgflg ora a jz scr1nd lxi b,prplin*100H+1 ; debugging in use [29] jmp scr2nd scr1nd: lxi b,rplin*100H+1 ; no debugging scr2nd: call csrpos clreos: lxi d,tj jmp prtstr ; [29] and nop out the rest for now... ; ;scrend: lxi b,prplin*100H+1 ; call csrpos ;clreos: lxi d,tj ; jmp prtstr ENDIF;NOT (osi OR crt ) [26] IF osi OR crt ; no cursor control scrnp: mvi e,' ' jmp outcon scrnrt: mvi e,' ' call outcon mvi e,'%' jmp outcon scrfln: screrr: scrst: scrend: jmp prcrlf ;Print CR/LF [Toad Hall] rppos: lxi d,prpack jmp prtstr sppos: lxi d,pspack jmp prtstr ENDIF;osi OR crt ; Some frequently-used routines (duplicates of those in CPSMIT): ; prcrlf - output a CR/LF ; prtstr - output string in DE ; rskp - return, skipping over error return prcrlf: lxi d,crlf prtstr: ; [17] added this to avoid prtstr.. emulate function 9 call. ; Works on most machines. IF (torch OR px8 OR z80mu) ; ; Modified print string as the CP/N (for Nut) system traps control ; characters in a function 9 call.. rot its cotton socks. push h push d push b prtst1: ldax d inx d cpi '$' ; if a dollar then end of string jz prtst2 push d mov e,a mov c,a ; also to c if its via conout in BIOS call outcon ; send it to the screen pop d jmp prtst1 prtst2: pop b pop d pop h ret ; regs restored.. just in case ENDIF ;(torch OR px8 OR z80mu) IF NOT (torch OR px8 or z80mu) ;ie any machine that can send ctrl chrs via dos call 9 PUSH H PUSH D push b mvi c,9 ; Dos call 9 (print a string) call bdos pop b POP D POP H ret ; all done for good machines ENDIF ;NOT (torch OR px8 OR z80mu) ; ; rskp - return to calling address + 3. rskp: pop h ; Get the return address inx h ; Increment by three inx h inx h pchl ; Copy block of data ; source in HL, destination in DE, byte count in BC ; called by: cpxsys, mfname ; mover: ;IF NOT z80 ; 8080's have to do it the hard way ;OBS assume its an 8080 for now - this will work on Z80s anyway. mov a,m stax d inx h inx d dcx b mov a,b ora c jnz mover ;ENDIF;NOT z80 ;IF z80 ; db 0EDh,0B0h ; Z80 LDIR instruction ;ENDIF;z80 ret ; ; Miscellaneous messages ; crlf: db cr,lf,'$' cfgmsg: db 'configured for $' witmsg: db ' with $' ; Its included if we get here ('with terminal') IF NOT (osi OR crt OR px8) ; [29] got cursor control? outln2: db ']',cr,lf,cr,lf,'Number of packets:' db cr,lf,'Number of retries:' db cr,lf,'File name:$' ENDIF;NOT (osi OR crt OR px8) IF px8 ; [29] outln2: db '] Number of retries:', cr, lf db 'Number of packets: File name:$' ENDIF ; px8 [29] IF NOT (osi OR crt) ; [29] outln3: db cr,lf,cr,lf ; debugging messages db cr,lf,'Rpack:' db cr,lf ; Blank line in case of long packet db cr,lf,'Spack:$' ENDIF ; NOT (osi OR crt) [29] IF lasm LINK CPXSWT.ASM ENDIF ;lasm