; PLINK.ASM ver 6.4 ; (revised 6/14/81) ; ;PLINK is a CP/M transient command which allows the user to ;PLINK currently supports two way transfer of text files between ;the CP/M disk and the remote computer. The following control ;codes may be initiated from the console keyboard: ; ;Control-E Exit PLINK to CP/M "warm-boot". ; ;Control-T Transmit ASCII file to remote system, asks for ; drive (A, B, etc.) and filename.typ. ; ;Control-C Aborts transmission of file to remote system. ; ;Control-Y Switches between saving and ignoring ; incoming ASCII data in RAM buffer, ; for later transfer to disk. ; ;Control-W Writes RAM buffer to disk, and asks for drive ; and filename.typ. ; ;Del (delete) Backspace when in command mode (e.g. ^T or ^W). ; ;Control-U Aborts current line when in command mode. ; ;(Note: all other control codes are passed to modem output, and ;may be interpreted by the remote system as various control ;functions.) ; ; ;bdos entry point and function codes ; base equ 0 ;<<-- set to offset of CP/M for your ;system, standard systems are 0, some ;'alternate' systems are 4200H ; bdos equ base+5 resdsk equ 13 ;reset disk system offc equ 15 ;open file cffc equ 16 ;close file dffc equ 19 ;delete file rrfc equ 20 ;read record wrfc equ 21 ;write record mffc equ 22 ;make file ; ;TRS80 pickles and trout sio calls ;offset by -3 that is add 3 to all calls ; setsio equ 30h ;set up z80 sio siotst equ 33h ;read sio status sioinp equ 36h ;input a char sioout equ 39h ;output a char ; ; ;default fcb and field definitions ; fcb equ base+5ch fn equ 1 ;file name field (rel) ft equ 9 ;file type field (rel) ex equ 12 ;file extent field (rel) nr equ 32 ;next record field (rel) dbuf equ base+80h ;default disk buffer address ; ;ascii control characters ; cr equ 0dh ;carriage return lf equ 0ah ;line feed del equ 7fh ;delete (rubout) bell equ 07h ;bell signal tab equ 09h ;horizontal tab xon equ 11h ;x-on character null equ 00h ;null char ; ;the following "trigger" equate is set to "lf" (linefeed) ;by default. an optional trigger char may be passed via fcb1 ; ; ie: PLINK B will set trigger to "bell" ; ;the following options are allowed ; ; 1. B = bell 07h ; 2. X = xon 11h ; 3. U = upload no trigger check at all ; ;any other ascii character may be passed through fcb1 ; ; trigger equ LF ;default value ; ; ;warning character for low memory ; wrnsig equ BELL ;if you have one, put 'BELL' here ;...else put '*' here. ; ; ; **main program** ; org base+100h ; jmp 4000h org base+4000h lnki: lxi sp,stack+64 ;create local stack lhld base+1 ;point to CP/M jmp table lxi d,3 ;get ready to add 3 dad d ;point to con status jmp shld citcal+1 ;modify call adrs dad d ;point to con in jmp shld rccal+1 ;modify call adrs dad d ;point to con out jmp shld wccal+1 ;modify call adrs lda fcb+1 ;see if optional trigger char cpi 20h ;blank.. ? jz skp ;..blank so use default "lf" cpi 'B' ;bell wanted jz trgbel cpi 'X' ;xon wanted jz trgxon cpi 'U' ;uploading no checking for trigger jz trgupl ; settrg sta overly+1 ;store the character as is then jmp skp ; trgbel mvi a,bell jmp settrg ; trgxon mvi a,xon jmp settrg ; trgupl xra a ;zero out jump sta overl1+1 ;change check for c/r to null sta overl2+1 ;and send linefeeds as well jmp skp ; skp: equ $ ; ; cont: xra a ;clear char buffers sta inch sta outch sta flag ;clear text save flag lxi h,tbuf ;set ptr to tbuf shld ptr lxi h,0 ;size = 0 shld size lxi h,lnkims ;print sign-on message call wcs ; ;main loop ; lnki3: call citest ;jump if no data from console jz lnki4 call rcc ;else read console data cpi 20h cc pcc ;call pcc if control char jc lnki4 ;jump if pcc handled char ori 80h ;else set valid data bit sta inch ;and store in input char buffer ; lnki4: lda outch ;jump if no data for console ora a jp lnki5 ani 7fh ;else discard valid data bit call wcc ;send char to console xra a ;then clear output char buffer sta outch ; lnki5: call mitest ;jump if no data from modem jz lnki6 call rmc2 ;else read modem data call save ;save char in text buffer if flag on ori 80h ;set data valid bit sta outch ;store in output char buffer ; lnki6: call motest ;jump if modem xmit buffer busy jnz lnki7 lda inch ;jump if no data for modem ora a jp lnki7 ani 7fh ;discard valid data bit call wmc ; xra a ;...then clear input char buffer sta inch ; lnki7: jmp lnki3 ;end of main loop ; lnkims: db cr,lf,'PLINK ver 6.4' db cr,lf,cr,lf db '[^T]ransmit, [^Y]ank, [^W]rite, [^E]xit, [^C]ancel' db cr,lf,'Ready',cr,lf,lf,0 ; ;pcc - process control character ; pcc: cpi 'E'-40h ;jump out if ctrl e jnz pcc1 push h lxi h,ays ;print 'are you sure' call wcs pop h call rcc ;get answer call wcc ;echo it ani 5fh ;make upper case cpi 'Y' ;yes? jz pccex ;exit call wccr ;crlf stc ;tell plink to ignore this character ; ; pcc1: cpi 'T'-40h ;jump if not control-t jnz pcc2 call stf ;transmit text file to modem stc ;tell plink to ignore this character ret ; pcc2: cpi 'Y'-40h ;jump if not control-y jnz pcc3 lda flag dcr a ;was it zero? jnz pcc2a ;yes sta flag ;no, was 1, now 0 lxi h,pcmnix ;print ignore incoming stuff jmp pcc2b ; pcc2a: mvi a,1 ;turn on text save flag sta flag lxi h,pccmr ;print 'saving incoming text in memory' ; pcc2b: call wcs stc ;tell plink to ignore this character ret ; pcc3: cpi 'W'-40h ;jump if not control-W jnz pcc4 xra a ;turn off text save flag sta flag call wtb ;write text buffer to disk stc ret ; pcc4: stc ;let plink handle all other cont. codes cmc ret ; pccex: lxi h,disms ;print 'modem not disconnected' call wcs jmp base ;exit to warm boot ; ays: db cr,lf,'Exit to CP/M - are you sure (Y or N)? ',0 ; disms: db cr,lf,'+++ Exit to CP/M +++',cr,lf,0 ; pccmr: db cr,lf,'Saving incoming text in memory',cr,lf,0 pcmnix: db cr,lf,'Ignoring incoming text',cr,lf,0 ; ;stf - send text file (to modem) ; stf: call gfn ;get name of disk file to send jc stf6 ;jump if file name error call open ;try to open specified file cpi 255 ;jump if file not found jz stf7 ; stf1: call read ;read next record into dbuf cpi 1 ;jump if end-of-file jz stf5 lxi h,dbuf ;point to disk buffer mvi c,128 ; stf2: mov a,m ;fetch next char from dbuf inx h cpi 'Z'-40h ;jump if end-of-file character jz stf5 ; overl2 cpi lf ;ignore line feeds jz stf4 call wmc ;write character to modem call wcc ;write character to console ; overl1 cpi cr ;jump if not carriage return jnz stf4 ; stf3: call citest ;check console data ready jz stf3a ;no data there call rcc ;get console character cpi 'C'-40h ;control c aborts it jz stf8 ; stf3a: call mitest ;wait for next modem character jz stf3 call rmc2 ;check modem for trigger char. ; overly cpi trigger jnz stf3 call wccr ;send crlf to console ; stf4: dcr c ;loop thru rest of dbuf jnz stf2 jmp stf1 ;go get next record from disk ; stf5: lxi h,stfsm ;print 'file send complete' call wcs ret ; stf6: lxi h,stfs1 ;print 'file name error' call wcs ret ; stf7: lxi h,stfs2 ;print 'file not found' call wcs ret ; stf8: lxi h,stfsa ;print 'file send aborted' call wcs ret ; stfsm: db 'File send complete',cr,lf,0 stfs1: db 'File name error or abort',cr,lf,0 stfs2: db 'File not found',cr,lf,0 stfsa: db cr,lf,'File send aborted',cr,lf,0 ; ;save - save char in text buffer if flag on ; ; entry conditions ; a - character to save ; save: push psw lda flag ora a jnz save1 pop psw ret ; save1: pop psw cpi del ;rubout (del) ? rz ;yes, ignore it cpi 20h ;test for control characters jnc save2 ;jump if not control char. cpi cr ;allow cr to be saved jz save2 cpi lf ;allow lf to be saved jz save2 cpi tab ;allow tab to be saved jz save2 ret ;ignore all other control chars. ; save2: push h lhld size ;size = size + 1 inx h shld size lhld ptr mov m,a inx h shld ptr push psw lda base+7 ;get system size sui 1 ;so we dont crash CP/M cmp h ;are we out of room? jz saveab ;yes, abort sui 4 ;leave some room (1k) cmp h mvi a,wrnsig ;signal console running out of space cc wcc pop psw pop h ret ; ;saveab - ran out of room, issue message and flow ; through to disk save routine ; savend: db bell,cr,lf,'Aborting - no room left',0 ; saveab: lxi sp,stack+64 ;reinitialize stack lxi h,savend ;print 'aborting - no room left' call wcs lxi h,lnki ;set up return address push h ;leave it on the stack ; ;wtb - write text buffer to disk ; wtb: lhld size ;jump if text buffer empty mov a,l ora h jz wtb5 mvi c,resdsk ;reset in case read-only call bdos call gfn ;get file name jc wtb6 ;jump if file name error call delt ;delete old file, if any call make ;make new file lhld size ;de = tbuf size xchg lxi h,dbuf ;top of stack points to dbuf push h lxi h,tbuf ;hl points to tbuf ; wtb1: mvi c,128 ;disk buffer size ; wtb2: mov a,m ;fetch next byte of tbuf inx h xthl mov m,a ;store in dbuf inx h xthl dcx d ;size = size - 1 mov a,d ;exit loop if size = 0 ora e jz wtb3 dcr c ;loop until dbuf full jnz wtb2 call write ;write full dbuf to disk xthl ;top of stack points to dbuf lxi h,dbuf xthl jmp wtb1 ;loop until end of tbuf ; wtb3: pop h ;hl points to current place in dbuf ; wtb4: mvi m,'Z'-40h ;store eof code inx h dcr c ;loop thru rest of dbuf jnz wtb4 call write ;write last sector to disk call close ;clean up act and go home lxi h,tbuf ;clear text buffer shld ptr lxi h,0 shld size lxi h,wtbsm ;print 'buffer saved on disk' call wcs ret ; wtb5: lxi h,wtbs1 ;print 'text buffer empty' call wcs ret ; wtb6: lxi h,wtbs2 ;print 'file name error' call wcs ret ; wtbsm: db cr,lf,'Buffer saved on disk',cr,lf db 'Memory save cancelled',cr,lf,0 wtbs1: db 'Text buffer empty',cr,lf,0 wtbs2: db 'File name error or abort',cr,lf,0 ; ;wcs - write console string ; ; entry conditions ; hl - points to string (term by zero byte) ; wcs: mov a,m inx h ora a rz call wcc jmp wcs ; ;wccr - write console carriage return (and line feed) ; wccr: mvi a,cr call wcc mvi a,lf ; ;wcc - write console character ; ; entry conditions: ; a - character to write ; wcc: push psw push b push d push h mov c,a ;get character for cbios wccal: call $-$ ;modified by init. pop h pop d pop b pop psw ret ; ;rcs - read console string (with echo) ; ; exit conditions ; b - number of characters read (<255) ; hl - points to last char stored (cr) ; rcs: lxi h,ibuf mvi b,0 ; rcs1: call rcc ;read next char from console cpi del ;jump if not del jnz rcs2 inr b ;ignore del if ibuf already empty dcr b jz rcs1 dcx h ;else discard last char mov a,m ;echo discarded char to console call wcc dcr b ;decrement count jmp rcs1 ; and loop ; rcs2: cpi 'U'-40h ;jump if not control u jnz rcs3 call wccr ;else abort current line jmp rcs ; and start over ; rcs3: call wcc ;echo char to console mov m,a ;store char in ibuf inr b ;increment count cpi cr ;jump if carriage return jz rcs4 inx h ;else advance pointer jmp rcs1 ; and loop ; rcs4: mvi a,lf ;issue line feed and return call wcc ret ; ;rcc - read console character ; ; exit conditions ; a - character read ; rcc: push b push d push h rccal: call $-$ ;modified by init. pop h pop d pop b ret ; ;wmc - write modem character ; ; entry conditions ; a - character to write ; ; wmc: ani 7fh sta xyz di out 0 mvi a,0 sta 0ef08h lda xyz sta 2a01h out 1 mvi a,1 sta 0ef08h ei ret ; ;rmc - read modem character ; ; exit conditions: ; a - character read di out 0 mvi a,0 sta 0ef08h rmc: lda 2a00h ani 01h cpi 00h jz rmc out 1 mvi a,1 sta 0ef08h ei rmc2: di out 0 mvi a,0 sta 0ef08h lda 2a01h sta xyz out 1 mvi a,1 sta 0ef08h ei lda xyz ani 7fh ret ; ; ; ;gfn - get file name ; gfn: lxi h,gfnsd ;print 'which drive?' call wcs call rcc ;get answer from console call wcc ;echo it to console ani 5fh ;make upper case cpi 'C'-40h ;^C means abort jz gfn6 sui 'A'-1 jc gfn ;require alphabetic jz gfn cpi 17 ;allow 16 drives (as in CP/M 2.x) jnc gfn sta fcb ; gfnb: lxi h,gfns1 ;print 'filename? ' call wcs call rcs ;read response into ibuf lxi h,fcb+fn ;blank fill fn and ft fields mvi c,11 ; gfn1: mvi m,' ' inx h dcr c jnz gfn1 lxi h,ibuf ;point to input buffer lxi d,fcb+fn ;scan off fn field mvi c,9 ; gfn2: mov a,m ;fetch next char from ibuf inx h cpi 61h ;if lc, convert to uc jc gfn2a sui 20h ; gfn2a: cpi cr ;jump if end of line jz gfn5 cpi '.' ;jump if end of name jz gfn3 stax d ;else store char in fn field inx d dcr c ;loop if 8 or less chars so far jnz gfn2 jmp gfn6 ;else take error exit ; gfn3: lxi d,fcb+ft ;scan off ft field mvi c,4 ; gfn4: mov a,m ;fetch next char from ibuf inx h cpi 61h ;if lc, convert to uc jc gfn4a sui 20h ; gfn4a: cpi cr ;jump if end of line jz gfn5 stax d ;else store char in ft field inx d dcr c ;loop if 3 or less chars so far jnz gfn4 jmp gfn6 ;else take error exit ; gfn5: xra a sta fcb+ex ;set extent number to zero sta fcb+nr ;set record number to zero stc ;clear error flag and return cmc ret ; gfn6: stc ;set error flag and return ret ; gfnsd: db cr,lf,'Which drive? ',0 gfns1: db cr,lf,'Filename? ',0 ; ;open - open disk file ; open: push h push d push b lxi d,fcb mvi c,offc call bdos pop b pop d pop h ret ; ;read - read record from disk file ; read: push h push d push b lxi d,fcb mvi c,rrfc call bdos pop b pop d pop h ret ; ;close - close disk file ; close: push h push d push b lxi d,fcb mvi c,cffc call bdos pop b pop d pop h ret ; ;delt - delete disk file ; delt: push h push d push b lxi d,fcb mvi c,dffc call bdos pop b pop d pop h ret ; ;write - write record to disk ; write: push h push d push b lxi d,fcb mvi c,wrfc call bdos pop b pop d pop h ret ; ;make - make new disk file ; make: push h push d push b lxi d,fcb mvi c,mffc call bdos pop b pop d pop h ret ; ;citest - check console input status ; citest: push b push d push h citcal: call $-$ ;modified by init. ora a ;set zero flag pop h pop d pop b ret ;zero flag carries answer ; ;mitest - check modem input status mitest: di out 0 mvi a,0 sta 0ef08h lda 2a00h sta xyz out 1 mvi a,1 sta 0ef08h ei lda xyz ani 01h cpi 00h jnz mitst1 ora a jmp mitst2 mitst1 cma ora a mitst2 ret ; ; ;motest - check modem output status ; motest: di out 0 mvi a,0 sta 0ef08h lda 2a00h sta xyz out 1 mvi a,1 sta 0ef08h ei lda xyz xri 02h ani 02h mvi a,0 jz motst1 ;zero flag carries answer cma ; motst1: ora a ;set zero flag if ready ret ; ;data area ; xyz: db 00 inch: ds 1 ;input char buffer (to cyber) outch: ds 1 ;output char buffer (from ciber) stack: ds 80 ;local stack ibuf: ds 256 ;input buffer ; ;text buffer ; flag: ds 1 ;text save flag ptr: ds 2 ;text buffer pointer size: ds 2 ;text buffer size tbuf: equ $ ;start of text buffer ; end