From 5161e8461797f2da2315af24a5b3355f21f36f6c Mon Sep 17 00:00:00 2001 From: acn Date: Mon, 5 Oct 2020 14:03:34 +0200 Subject: [PATCH] Source code of QTerm 4.3e added --- README.md | 2 + source/BAUD.Z | 271 +++++++++++ source/CATCH.Z | 178 +++++++ source/CF.Z | 859 +++++++++++++++++++++++++++++++++ source/CHAT.Z | 993 +++++++++++++++++++++++++++++++++++++++ source/DIR.Z | 144 ++++++ source/FILE.Z | 434 +++++++++++++++++ source/FLAGS.Z | 133 ++++++ source/KERMIT.I | 20 + source/KUTIL.Z | 1121 ++++++++++++++++++++++++++++++++++++++++++++ source/MAKEQT.SUB | 10 + source/ODDS.Z | 658 ++++++++++++++++++++++++++ source/QT43SRC.LBR | Bin 0 -> 75136 bytes source/QTERM.Z | 903 +++++++++++++++++++++++++++++++++++ source/RECV.Z | 77 +++ source/RECVK.Z | 259 ++++++++++ source/RECVX.Z | 359 ++++++++++++++ source/SEND.Z | 84 ++++ source/SENDK.Z | 262 +++++++++++ source/SENDX.Z | 402 ++++++++++++++++ source/SHRINK.Z | 48 ++ source/SRSCRN.Z | 226 +++++++++ source/SRUTIL.Z | 468 ++++++++++++++++++ source/TERMCAP.I | 11 + source/TERMIO.Z | 512 ++++++++++++++++++++ source/VARS.I | 31 ++ source/VERSION.I | 49 ++ source/VT100.Z | 508 ++++++++++++++++++++ source/XMODEM.I | 34 ++ source/XUTIL.Z | 178 +++++++ 30 files changed, 9234 insertions(+) create mode 100644 source/BAUD.Z create mode 100644 source/CATCH.Z create mode 100644 source/CF.Z create mode 100644 source/CHAT.Z create mode 100644 source/DIR.Z create mode 100644 source/FILE.Z create mode 100644 source/FLAGS.Z create mode 100644 source/KERMIT.I create mode 100644 source/KUTIL.Z create mode 100644 source/MAKEQT.SUB create mode 100644 source/ODDS.Z create mode 100644 source/QT43SRC.LBR create mode 100644 source/QTERM.Z create mode 100644 source/RECV.Z create mode 100644 source/RECVK.Z create mode 100644 source/RECVX.Z create mode 100644 source/SEND.Z create mode 100644 source/SENDK.Z create mode 100644 source/SENDX.Z create mode 100644 source/SHRINK.Z create mode 100644 source/SRSCRN.Z create mode 100644 source/SRUTIL.Z create mode 100644 source/TERMCAP.I create mode 100644 source/TERMIO.Z create mode 100644 source/VARS.I create mode 100644 source/VERSION.I create mode 100644 source/VT100.Z create mode 100644 source/XMODEM.I create mode 100644 source/XUTIL.Z diff --git a/README.md b/README.md index 24d4eac..e782e38 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ The file [QTERM43F.LBR](qterm43f.lbr) is the original QTerm distribution file. The directory [files/](files/) contains all files from the .LBR file. There, the documentation for QTerm, [QTERM.DOC](files/QTERM.DOC) can be found. +In the directory [source/](source/), the source code of QTerm 4.3e can be found (as files and QT43SRC.LBR library). + ### Applying a patch The patches are applied using ```ZSM``` and ```ZPATCH```, which are included in the QTerm distribution (```QTERM43F.LBR```). diff --git a/source/BAUD.Z b/source/BAUD.Z new file mode 100644 index 0000000..1e225ef --- /dev/null +++ b/source/BAUD.Z @@ -0,0 +1,271 @@ +; baud.z - alter baud rate, and data word format + +.var cmdlin 0x80 + +.macro baudtb speed + dw speed + dw b`speed +.endm + +.extern baud +baud: + call prompt + db 'Baud rate? \0' ; see what is wanted + ld hl,cmdlin - 1 +scanp: inc hl ; first look for +ch + ld a,(hl) + or a + jr z,noplus ; didn't find one, on to the next bit + xor '+' + jr nz,scanp ; not this char, on to the next + ld (hl),a ; replace the '+' so we have a revised end + inc hl + ld bc,0x0200 ; b = 2: 2 chars, c = 0 for delimiter + ld de,cschr ; save them in cschr and cqchr + call parst ; crunch up the string +noplus: ld hl,cmdlin - 1 +scansl: inc hl ; then look for a -del + ld a,(hl) + or a + jr z,nodel ; no '-' found: process normally + xor '-' + jr nz,scansl ; this was not a '-', on to the next + ld (hl),a ; put a null in to make the '-' vanish + call incbyp ; skip leading space + or a ; anything there? + jr z,nodel ; no - go crunch the rest + cp '*' ; '*' as a place holder? + jr z,nocsdl ; skip if no ^S delay + ld de,1 + call scanm ; get a number, but default if none given + ld a,e + ld (csdely),a ; save in ^S delay +nocsdl: call incbyp + or a + jr z,nodel ; exit now if nothing left + cp '*' + jr z,nochdl ; skip if no character delay + sub '0' + cp 10 ; check for a number + ld de,ecflg + ld (de),a ; set echo check flag + jr nc,lcnum ; jump if not a number - this sets flag + dec hl ; hl down to account for coming inx + xor a ; clear acc + ld (de),a ; and thus the echo check flag +lcnum: inc hl + ld de,0 + call scanm ; get another + ld a,e + ld (chd),a ; and save a character delay +nochdl: call incbyp + or a + jr z,nodel ; exit now if nothing left + cp '*' + jr z,nodel ; and exit if a star + ld de,lfecho + ld (de),a ; set linefeed echo flag + sub '0' + cp 10 ; first char a digit + jr nc,lfnum ; jump if not + dec hl ; otherwise backstep hl + xor a + ld (de),a ; and clear linefeed echo flag +lfnum: inc hl + ld de,300 + call scanm + ld (nld),de ; finally a newline delay +nodel: ld hl,cmdlin + call byp + ld b,h + ld c,l ; point bc at first non-white-space in line + ld a,(bc) + or a + ret z ; end of line so do nothing + cp '*' ; was a '*' skip over baud rate set + jp z,bmode + dec bc +scan: inc bc ; now bypass leading zeros + ld a,(bc) + cp '0' + jr z,scan + ld hl,0 ; hl gets numeric value of rate + ld ix,rate ; ix points to where string will live +loop: ld a,(bc) + sub '0' + cp 10 ; get the next character & check it's a digit + ld e,l + ld d,h + jr nc,gotit ; nope - we've finished + add hl,hl ; * 2 + add hl,hl ; * 4 + add hl,de ; * 5 + add hl,hl ; * 10 + ld e,a + ld d,0 ; new digit to de + add hl,de ; add it in + ld a,(bc) ; get the character again + ld (ix),a ; save in string + inc ix + inc bc ; move both pointers + jr loop ; and do it again +gotit: ld a,d + or e + jr z,usage ; zero baud rate means we should complain + ld a,' ' + push ix + pop hl ; copy string save pointer to hl + ld (hl),a + inc hl + ld (hl),a + inc hl + ld (hl),a ; add three spaces + ld a,(bc) ; look at input char following rate + or a + jr z,cmdok ; zero byte is ok + cp ' ' + jr z,cmdok ; and so is a space +usage: call ilprt ; but we complain about anything else + db 'Specify \r\n\0' + ret +cmdok: ld hl,table ; point at the table +.dseg +table: baudtb 38400 + baudtb 19200 + baudtb 9600 + baudtb 4800 + baudtb 2400 + baudtb 1200 + baudtb 600 + baudtb 300 +endtbl: + +.cseg + push bc ; save address in input string + ld b,{endtbl - table} / 4 +scntbl: ld a,(hl) + cp e + inc hl ; check low byte of baud rate + jr nz,bad2 ; skip if no match + ld a,(hl) + cp d ; check high byte + jr z,gottbl ; match! - we got it +bad2: inc hl ; this entry no good + inc hl + inc hl ; skip over it + djnz scntbl ; and loop till we run out +badbau: call ilprt ; go complain + db 'Error: invalid baud rate\r\n\0' + pop hl ; clean up the stack + ret ; and exit +gottbl: inc hl + ld e,(hl) + inc hl ; get address of baud value entry in + ld d,(hl) ; configuration area + ld a,(de) ; get the baud byte itself + inc de + ld b,a ; save in b + ld a,(de) ; get the flag byte + or a ; is it zero? + jr z,badbau ; yes - go complain + ld a,b + call setbd ; go set the baud rate + call ilprt ; and say what we did + db 'Baud rate set to ' + +.extern rate +rate: db ' \r\n\0' + pop bc ; get the string address back to bc + dec bc +bmode: inc bc + ld a,(bc) + cp ' ' + jr z,bmode ; do a byp on bc + or a + ret z ; return now on end of string + ld e,0 ; set e zero for mode selection + ld ix,modsav ; point ix at save area + ld (ix + 0),a ; save first byte in data bit count + cp '7' + jr z,seven ; better be '7' + cp '8' + jr z,eight ; or '8' +badmod: call ilprt ; complain if it's wrong + db 'Modes are [87][EON][12]\r\n\0' + ret +eight: inc e ; set bit 0 in e for 8 bit options +seven: inc bc + ld a,(bc) ; get the next character + and 0x5f ; force upper case + ld (ix + 2),a ; save it away + cp 'N' + jr z,nopty ; 'N' => no parity + cp 'O' + jr z,odd ; 'O' => odd parity + cp 'E' + jr nz,badmod ; 'E' => even parity: complain if no match + set 2,e + jr nopty ; set bit 2 to select even parity options +odd: set 3,e ; set bit 3 to select odd parity options +nopty: inc bc + ld a,(bc) ; look at the last character + ld (ix + 4),a ; and save it away + cp '1' + jr z,one ; better be '1' + cp '2' + jr nz,badmod ; or '2' - complain if it's neither + set 1,e ; set bit 1 for 2 data bit options +one: inc bc + ld a,(bc) + cp ' ' + jr z,one ; skip any trailing spaces + or a + jp nz,usage ; but complain if there was more junk + ld d,0 ; extend index in e to 16 bits + ld hl,modtab + add hl,de ; index into table in configuration area + ld a,(hl) ; get the byte + call setmod ; go set the modes + call ilprt + db 'Mode set to ' + +.extern modsav +modsav: db ' , , \r\n\0' ; and say what we did + ret + +scanm: ld a,(hl) + sub '0' + cp 10 ; did we get a digit? + jr nc,donenm ; no - use default in de + ld de,0 ; set de zero for answer +scanlp: ld a,(hl) + sub '0' + cp 10 ; another digit? +donenm: jp nc,unbyp ; exit if not - answer in de + push hl ; save pointer + ld h,d + ld l,e + add hl,hl + add hl,hl + add hl,de + add hl,hl ; hl = de * 10 + ld e,a + ld d,0 + add hl,de ; + new digit + ex de,hl ; back to de + pop hl + inc hl ; look at next character + jr scanlp + +.dseg +.extern csdely +csdely: db 1 +.extern nld +nld: dw 300 +.extern chd +chd: db 0 +.extern cschr +cschr: db 's' & 0x1f +.extern cqchr +cqchr: db 'q' & 0x1f + \ No newline at end of file diff --git a/source/CATCH.Z b/source/CATCH.Z new file mode 100644 index 0000000..23453ef --- /dev/null +++ b/source/CATCH.Z @@ -0,0 +1,178 @@ +; catch.z - open & close catch files, also put them on hold + +.incl "c:vars" + +.extern catch +catch: + ld a,(cflg) + or a + jr z,tfileo + ld a,2 + ld (cflg),a + +.extern hold +hold: + ld hl,cflg + ld a,(hl) + or a ; file open? + ret z ; no - so do nothing + xor 3 + ld (hl),a ; toggle hold mode + push af + call ilprt + db '\r\nCatch file \0' + pop af + dec a + jr z,reac + call ilprt + db 'on hold\r\n\0' + ret +reac: call ilprt + db 'reactivated\r\n\0' + ret + +.extern cclose +cclose: + ld a,(cflg) + or a + ret z + call doclos + call ilprt + db '\r\nFile closed\r\n\0' + ret + +tfileo: call getfcb + ret c + jp z,nowc + ld a,(fcb + 2) + cp ' ' + scf + ret z + call reset + ld hl,fcb + ld de,cfcb + push de + ld bc,34 + ldir + pop de + push de + ld c,open + call usrbds + inc a + pop de + jr z,isok +appndq: ld hl,10 + add hl,de + bit 7,(hl) + jr z,appok + call ilprt + db 'Can\'t append file\r\n\0' + ret +appok: ld c,cfsize + push de + call usrbds + pop de + ld hl,36 + add hl,de + ld a,(hl) + dec hl + or (hl) + dec hl + or (hl) + jr z,creok +declp: ld a,(hl) + dec (hl) + inc hl + or a + jr z,declp + push de + ld de,(cbuff) + ld c,setdma + call bdos + call fill1a + pop de + ld c,redrnd + call usrbds + ld hl,(cbuff) + dec hl +eofl: inc hl + ld a,(hl) + cp 0x1a + jr nz,eofl + ld (cptr),hl + jr setcfo +isok: ld c,create + call usrbds + inc a + jr nz,creok + call ilprt + db 'Can\'t create file\r\n\0' + ret +creok: call fill1a +setcfo: call ilprt + db 'File opened\r\n\0' + xor a + inc a +setcfl: ld (cflg),a + ret + +doclos: call flushc + ld de,cfcb + ld c,close + call usrbds + call ctlq + xor a + jr setcfl + +.extern flushc ; enter here to flush any pending write of +flushc: ; catch buffer + ld hl,(cptr) + ld de,(cbuff) ; point at buffer + or a + sbc hl,de ; how many chars in buffer + dec hl + add hl,hl + inc h + ld b,h ; converted to sector count + ret z ; nothing to write - exit now + push bc + push de + call ctls ; hold up the other end + pop de + pop bc +wrtlp: push bc + push de ; save registers + ld c,setdma + call bdos ; set dma for next 128 bytes + ld de,cfcb + ld c,write + call usrbds ; out they go + pop de + ld hl,128 + add hl,de ; move dma + ex de,hl + pop bc + djnz wrtlp ; loop till all done + +fill1a: ; enter here to fill catch buffer with 0x1a's + ; and reset catch pointer + ld hl,(cbuff) + ld (cptr),hl ; adjust catch pointer as well + ld d,h + ld e,l + inc de ; copy to de & add 1 + ld bc,(cbfsiz) ; get buffer size + ld (hl),0x1a ; first one + ldir ; and the rest + ret + +.dseg +.extern cflg +cflg: db 0 + +.useg +.extern cptr +cptr: ds 2 ; pointer to next save address +.extern cfcb +cfcb: ds 37 ; fcb for catching + \ No newline at end of file diff --git a/source/CF.Z b/source/CF.Z new file mode 100644 index 0000000..dcbb91f --- /dev/null +++ b/source/CF.Z @@ -0,0 +1,859 @@ +; cf.z - creeping featurism (i.e. file operations in chat scripts) +; plus stuff to look after numbers in chat scripts + +.incl "c:vars" + +; stest - compare two strings !% = .s1.s2. `label jump if equal +; !% _ .s1.s2. `label jump if equal case insens. +; !% # .s1.s2. `label jump if not equal + +.extern stest +stest: xor a + ld (casefl),a + call areabp + cp '#' + jr z,ckseq ; '#' for inequality + xor '=' + jr z,ckseq ; '=' for equality + ld (casefl),a ; set case flag true + xor '_' ^ '=' ; '_' for equality case insensitive + ret nz +ckseq: or a ; z equality, nz inequality + push af ; save test value + call incbyp ; step up to strings + push hl + ld hl,work2 + ld de,work2 + 1 + ld bc,255 + ld (hl),b + ldir ; clear space where strings will live + pop hl + or a + jr z,testzz + ld de,work2 + 1 + ld b,2 +seqslp: push bc + call scanst ; scan a string + inc de + pop bc + jr c,testzz ; exit if end of input + djnz seqslp ; otherwise loop till both read + inc hl ; skip trailing terminator + push hl ; save input pointer + ld hl,work2 + 1 + ld de,work2 + 42 ; get the two strings +csl: ld b,0x7f + call cmptst ; check a characters + jr nz,phltz ; different, so finish test operation + ld a,(hl) + or a ; end of string? + jr z,phltz ; yes - finish test this way too + inc hl + inc de ; next chars ..... + jr csl + +; the !~ creeping featurism entry + +.extern cf +cf: + call areabu ; what's wanted? + inc hl + cp '-' + jr z,cf_era ; '-' erases + cp '=' + jr z,cf_ren + cp '+' + jr z,cf_copy ; '+' copies + cp 'Y' + jr z,cf_filq ; 'Y' file query: jump if file exists + xor 'N' + ret nz ; 'N' file query: jump if file doesn't exist + +; fall into file query routine + +; file query: check see if a file exists or not, and then jump to a label if +; it does. + +cf_filq: + or a ; z for 'N', nz for 'Y' + push af + call scnfcb ; get a fcb + ld de,fcb + ld c,open ; try to open it - note that this will also + ; work with wildcards + push hl + call usrbds ; open + inc a +phltz: pop hl +testzz: jr z,nofil ; not there - do test one way round + pop af ; 'Y' / 'N' result back + ret z ; 'N' and file exists => no skip + jr dskp1 ; 'Y' and file => skip +nofil: pop af ; no file ..... + ret nz ; 'Y' and no file => no skip +dskp1: jp doskip ; go skip to the new line + +; rename: du: is taken from the first name - a1:x = b7:y will rename a1:y + +cf_ren: call gettwo + ret c ; exit on an error + ld hl,(auxfcb) + ld (fcb),hl ; xfer drive / user info + ld de,fcb + ld c,open + call usrbds ; open + inc a + ret z ; not there - exit + ld a,(fcb + 10) + add a,a + ret c ; if it's R/O stop now + ld de,auxfcb + ld c,open + call usrbds ; open + inc a + ret nz ; replacement _IS_ there - so exit + ld hl,auxfcb + 1 + ld de,fcb + 17 + ld bc,16 + ldir ; shift stuff for rename + ld de,fcb + ld c,rename + jp usrbds ; go do it + +; erase subroutine: no wildcards, single file erase + +cf_era: call scnfcb + ret z ; no wildcards allowed + ld de,fcb + ld c,open + call usrbds ; open the file + ld a,(fcb + 10) + add a,a + ret c ; if it's R/O stop now + ld de,fcb + ld c,erase + jp usrbds ; otherwise erase it + +; copy file routine + +cf_copy: + call gettwo ; get two fcb's - one to fcb, one to auxfcb + ret c ; exit on an error + ld de,fcb + ld c,open + call usrbds ; open file1 + inc a + ret z ; not there: exit + ld de,auxfcb + ld c,open + call usrbds ; open file2 + inc a + ret nz ; is there: exit + ld de,auxfcb + ld c,create + call usrbds ; create file2 + inc a + ret z ; create failed: exit + ld de,xbuff + ld c,setdma + call bdos ; set dma address +cplp: ld de,fcb + ld c,read + call usrbds ; read a sector + or a + jr nz,copied ; all done + ld de,auxfcb + ld c,write + call usrbds ; write it back + or a + jr z,cplp ; exit if write failed +copied: ld de,auxfcb + ld c,close + jp usrbds ; close output file + +; parse two filenames, separated by an '=' sign + +gettwo: call scnfcb ; get one + scf + ret z ; exit w/ carry if wildcards + call byp + cp '=' ; need an '=' + scf + ret nz ; not there: exit + push hl + call xferax ; shift it to auxfcb + pop hl + inc hl + call scnfcb ; get a second + scf + ret z ; exit with carry on an error + or a + ret ; else clear carry for OK + +; !@ evaluation subroutine + +.extern eval +eval: + call areabu ; set to read the string + sub 'A' ; get the variable number + cp 26 + jr nc,eerr0 ; not legal, exit now + call ivar + push de ; save it's address + inc hl + call scan ; scan a term + jr c,eerr1 ; no good - exit + push af ; save the value + call byp + cp '-' + jr z,opok + xor '+' ; check the operator + jr nz,eerr2 +opok: push af ; save the operator (zero for '+', nz for '-') + inc hl + call scan ; get a second term + jr nc,s2ok ; all OK, go set the variable + pop de +eerr2: pop de +eerr1: pop de ; clean up the stack +eerr0: call ilprt + db 'Bad expression: \0' + call dim ; tell the user + ld hl,0x80 + call prtslp + jp crlf ; and return +s2ok: ld l,a ; second term to l + pop bc ; operator to b + pop af ; first term to a + dec b + inc b ; zero => '+' + jr z,addem ; go do it + sub l + sub l ; setup for subtraction +addem: add a,l ; do the addition + pop hl ; restore variable address + ld (hl),a ; save result + ret + +; !# test routine: compare variables with other variables or numbers + +.extern test +test: + call areabu + sub 'A' ; get the variable + cp 26 + jr nc,eerr0 ; exit on an error + call ivar + ld a,(de) ; get te value + push af ; save it + call incbyp ; get the operator + cp '<' + jr z,cmpok + cp '>' + jr z,cmpok + cp '#' + jr z,cmpok + xor '=' + jr nz,eerr1 ; exit if it's no good +cmpok: push af ; save the operator + inc hl + call scan ; get the term + jr c,eerr2 ; something wrong, exit + ld e,a ; term to e + pop af ; restore the operator + or a + jr nz,nocpe ; z => '=' => equality + pop af + cp e ; test for equality + ret nz ; exit if no good + jr doskip ; go do the goto + +; this is here for jr length reasons + +cplt: pop af + cp e ; compare +skipnc: ret nc ; if it failed exit now + +; jump to a line number + +doskip: call scnnum ; get the number to go to + pop hl ; get return address back + pop de ; get the next line + dec a ; save it away (needs to be -1) + push af ; save the new line away + jp (hl) ; jump to return address + +nocpe: cp '#' ; '#' => inequality + jr nz,nocpne + pop af ; restore variable value + cp e + ret z ; exit if test failed + jr doskip ; else do the goto +nocpne: cp '>' + jr nz,cplt ; '>' => greater than + pop af ; var to a + ld d,a ; and to d + ld a,e ; term to e + cp d ; and compare + jr skipnc ; go check the result + +; initch - do odd bits and pieces of setup for chat operation + +.extern initch +initch: ld a,1 + ld (opentr),a ; clear number of open tries + +.extern clrvar +clrvar: + ld a,'A' + ld (_check_),a + ld hl,vars ; nuke all variables to zero + ld de,vars + 1 + ld bc,26 + 9 + 26 ; 26 numbers, plus 35 strings 0 - 9 & a - z + ld (hl),b ; start with a zero from b + ldir + dec b ; b comes out of the ldir w/ zero: dec to -1 + ld (hl),b ; -1 byte to end strings + ld bc,512 - 9 - 27 + ldir ; fill remaining space with -1's + ret + +ivar: push hl ; save hl + ld l,a + ld h,0 ; offset in a to hl + ld de,vars + add hl,de ; index into table + ex de,hl ; swap to de + pop hl ; restore hl + ret + +scan: call byp + or 0x20 + sub 'a' ; was it a letter? + cp 26 + jr nc,scnnum ; no, go see about a number + inc hl ; step over it + call ivar ; index into array + ld a,(de) ; get the value + ret ; and return - carry cleared by ivar + +scnnum: ld e,0 + call byp ; find the number +snlp: ld a,(hl) ; get char back + sub '0' + cp 10 ; number? + ccf + ret c ; return if not + ld d,a ; save in d + ld a,e + add a,a + add a,a + add a,e + add a,a ; a = e * 10 + add a,d ; + d + ld e,a + inc hl + ld a,(hl) ; next letter + cp '-' + jr z,rete + cp '+' + jr z,rete ; + and - are legal delimiters + or ' ' + xor ' ' ; so are ' ' and null +rete: ld a,e ; get value from e + jr nz,snlp + ret ; return, carry clear from compare + +; mattog - handle !& toggles: 'm' & 'l' for matches, 'o' for modem output echo + +.extern mattog +mattog: call areabu ; get command letter and upper case it + cp 'O' + ld c,op_bit ; 'O' - do output bit + jr z,dotog + cp 'L' + ld c,lf_bit ; 'L' - do looking for message bit + jr z,dotog + cp 'M' ; 'M' - do match bit + ret nz + ld c,mat_bit +dotog: call incbyp ; now look for a char to say what to do + ld hl,mode ; point hl at mode byte + or a + jr z,xorit ; nothing: toggle the bit + cp '0' + jr nz,clrit ; not '0': clear the bit to enable + ld a,(hl) + or c ; otherwise set it + jr smret +clrit: ld a,c + cpl ; toggle bit in c + and (hl) ; clear the bit in questin + jr smret +xorit: ld a,(hl) + xor c ; flip the bit +smret: ld (hl),a ; save and we're done + ret + +; messag - process a !> printout + +.extern messag +messag: call areabp ; point at first non-white + ld de,packet ; somewhere to put it + push de + ld bc,128 << 8 ; 128 chars max, and eoln is the delimiter + call parst ; crunch up the string + pop hl + jp prtslp ; and dump it out + +; fileio - process !( r/w commands to read / write variables from the file + +.extern fileio +fileio: + call areabu ; get R or W + cp 'R' + jr z,setrd + cp 'W' + ret nz + ld a,34 ; write random + db 0x11 ; ld de,XXXX - kills ld a below +setrd: ld a,33 ; read random + ld (hibit),a ; save it away for later + call incbyp + or 0x20 + sub 'a' + cp 26 + ret nc ; check valid string name + push af ; save string index + call incbyp + sub '0' ; number zero to nine for file record + + ld hl,lbrs ; get /QTERM.LBR as a filename + push af + call scnfcb ; parse into 0x5b + pop af + ld hl,['T' << 8] + 'S' + ld (0x65),hl ; convert extent from LBR to STR + ld hl,(chtusr) + inc h + ld (0x5b),hl ; set drive / user + ld hl,0x68 ; point hl / de at nulls + ld de,0x69 + ld bc,0xff - 0x68 ; clear to end of name + ldir + ld (0x7d),a ; save record number now + + ld de,0x5b + ld c,15 + call usrbds ; open it + pop de + inc a + ret z ; exit if no good + ld a,d + add a,9 + call fparam ; point at string + ld de,0x80 + push de ; save string address to read from + push hl ; save string address in table + push de ; and read address again + ld b,d + ld c,0x7f + ldir ; shift the string + pop de ; address to de + ld c,26 + call bdos ; set dma + ld bc,(hibit) ; r/w BDOS opcode to c + ld de,0x5b + push de + call usrbds ; do the operation + pop de + ld c,16 + call usrbds ; close the file + pop hl + jr repstr ; replace string in table + +; sinput - handle two cases of input processing for !< + +.extern sinput +sinput: call areabp ; get the command character + ld d,a ; command char to d + call incbyp ; get variable / string letter + call ucsa ; upper case it + sub 'A' + cp 26 ; is it valid? + ret nc ; nope + ld e,a ; letter code to e + ld a,d ; command char back from d + xor '-' + jr z,strin ; '-' for string input + xor '.' ^ '-' + ret nz ; '.' for hot key input + ld d,a ; make code in e fill de + push de ; save + call kbdin ; go get a character + pop de + ld hl,vars + add hl,de ; index into variables + ld (hl),a ; save the letter + ret + +strin: ld d,e + push de + ld de,work2 ; point at input area + ld a,40 + ld (de),a ; 40 chars maximum + ld c,10 + call bdos ; go get them + call crlf ; throw a new line just for the hell of it + ld hl,work2 + 1 + ld e,(hl) ; get the length + inc hl ; point at real text + pop af + push hl + ld d,0 ; extend length to de + add hl,de ; point at end of string + ld (hl),d ; add a null terminating byte +savstr: add a,9 ; get string index to a + call fparam ; point at string +repstr: call kilstr ; kill it + xor a + pop de + jp insstr ; and replace it + +; now starts the middle of the "read string variable from modem" code - this +; is kinda cut to shreds for jr length reasons - we enter at the bottom and +; leave via savstr above + +; the very end - we've read the string, now it's time to save it + +gotit: inc de ; point back to start + push de ; save this point for later + ld h,d + ld l,e ; copy to hl +kcclp: ld a,(hl) ; get a char + and 0x7f ; nuke bit 7 + ld (de),a ; save it + inc hl ; bump read ptr + or a + jr z,hsav ; exit on a null + cp ' ' + jr c,kcclp ; ditch control chars + cp 0x7f + jr z,kcclp ; and deletes + inc de ; otherwise step up to allow saved character + jr kcclp + +hsav: ld a,(hibit) ; get string number + jr savstr ; go and save it + +; now the loop to find the start + +stlp: call redsch ; check a character + jr z,wasec ; check for end character + ld a,(de) ; no - we'll want to save original char back +wasec: ld c,a ; save char to save + push hl + ld h,l ; load start char into h + call chksch ; did we find it? + pop hl + ld a,c + ld (de),a ; drop terminator in + jr z,gotit ; yes - go save it away + djnz stlp ; count chars + ret ; return when we run out chars + +; ![ ^ a c1c2 +; +; After ![ - and ![ : and normal lines have left text in w2 (a la stufw2), +; parse a string starting with c1 (but not including it), and ending with c2 +; (again not including it) - save in string a. If c1 (or c2) are a null +; then the string becomes any whitespace delimited string: whitespace is +; taken as ' ', tab, cr, lf. Note also that all control characters are +; thrown away. + +ipstr: call ucsahl + sub 'A' ; convert ..... + cp 26 ; and test string index + ret nc + ld (hibit),a ; save it + call incbyp ; step up to delimiter chars + push hl + ld hl,work2 + 257 + ld de,packet + ld bc,66 + ldir + pop hl + ld de,pack + 1 ; save them in pack + ld b,2 ; two, null terminates + xor a + ld (de),a ; preload chars with two nulls + dec de + ld (de),a + call parst ; crunch up the string + ld hl,(pack) ; load delimiters from where parst put them + ld bc,64 << 8 ; max of 64 chars to look at / c == 0 + ld de,packet + 65 ; point de at saved text + +; now loop looking for the end + +endlp: call redsch ; check a character + jr nz,noend ; check for end character + inc c ; flag we saw an end char + ld (de),a ; add a zero to terminate + jr cntend ; jump to count +noend: dec c + inc c + jr nz,stlp ; we've seen an end - go do it +cntend: djnz endlp ; count chars + ret ; return when we run out chars + +; handle ![ multiple choice thingies from the modem + +.extern multi +multi: + call areabp ; what's wanted? + ld c,a ; save the second character + call incbyp ; strip spaces + xor a + ld (casefl),a + ld a,c + cp '^' + jr z,ipstr + cp ':' + jp z,silnce + cp '-' ; '-' input ...... + jr z,minput + xor '+' ; '+' for 8 bit string compare + jr z,str8b + cp '=' ^ '+' ; '=' for 7 bit + jr z,str7b + cp '_' ^ '+' ; '_' for 7 bit and case insensitive + ret nz ; oops - don't grok it. + ld (casefl),a ; set case flag +str7b: ld a,0x7f ; set 7 bit flag + db 0x06 ; makes the next byte the operand of a ld b,xx +str8b: dec a ; really ld a,0xff since a == 0: + ; set all 8 bits + push af ; save comparison mask + ld de,packet ; somewhere to put match string + ld bc,[40 << 8] + ' ' ; 40 chars max, space is the delimiter + call parst ; crunch up the string + pop bc ; comparison mask back to b + ret c ; oops - bad string + push hl + ld hl,work2 + 322 ; point at stuff we read ..... (don't ask why +mclp: dec hl ; it's there :-) + ld a,(hl) ; end of string yet? + or a + pop de ; keep stack safe + ret z ; end of string: exit + push de ; save word back on stack + ld de,packet + call cmptst ; do we have a match?? + jr nz,mclp ; skip if no good + push hl +cmplp: ld a,(de) + or a ; end of string we're searching for? + jr nz,mtchmo ; yes - we got a hit + pop hl + pop hl ; get string pointer back to hl +doskp1: jp doskip +mtchmo: call cmptst + jr nz,mchbad ; oops - didn't match, so exit now + inc hl + inc de + jr cmplp ; and back for more +mchbad: pop hl ; restore pointer + jr mclp ; and back for more + +minput: call scnnum ; get a time + or a + jr nz,timok + ld a,15 ; none given, default to 15 seconds +timok: push af + push hl + ld hl,work2 + ld de,work2 + 1 + ld bc,512 + ld (hl),c + ldir ; clear space where strings will live + pop hl + call byp + or a + jr z,nostr ; no strings, all done + ld de,work2 + 1 + ld b,4 +sslp: push bc + call scanst ; scan a string + inc de + pop bc + jr c,nostr ; exit if end of input + djnz sslp ; otherwise loop till 4 read +nostr: ld hl,work2 + 1 + call finstr ; get address of it's end + ld (work2 + 240),hl ; and save it + ld hl,work2 + 42 + call finstr + ld (work2 + 242),hl ; and for string 2 + ld hl,work2 + 83 + call finstr + ld (work2 + 244),hl ; and 3 + ld hl,work2 + 124 + call finstr + ld (work2 + 246),hl ; and 4 + pop af ; get time back + ld c,a ; save it +second: dec c + ret z ; all out of time - exit + ld de,600 + call setspd ; set the speed +qrtrms: ld b,96 ; hang loose a while +qmslp: djnz qmslp + push hl + push bc + call procch + jr c,nochar + call stufw2 ; save the char + inc de + ld hl,(work2 + 240) + call chkbak ; did we get string1 + jr z,gotone ; exit if so + ld hl,(work2 + 242) + call chkbak ; string 2? + jr z,gotone ; exit if so + ld hl,(work2 + 244) + call chkbak ; string 3 + jr z,gotone ; exit if so + ld hl,(work2 + 246) + call chkbak ; and lastly check string 4 + jr z,gotone +nochar: pop bc + pop hl + dec hl ; count down second timer + ld a,h + or l + jr nz,qrtrms + jr second ; loop back + +gotone: pop bc + pop hl + ret + +.extern stufw2 +stufw2: ld hl,work2 + 258 + ld de,work2 + 257 + ld bc,64 + ldir + ld (de),a ; save the character + ret + +.extern clerw2 +clerw2: ld hl,work2 + 256 + ld de,work2 + 257 + ld bc,70 + ld (hl),b + ldir + ret + +finstr: ld a,(hl) + or a + ret z + inc hl + jr finstr + +chkbak: dec hl + ld a,(hl) + inc hl + or a + jr nz,isastr + inc a + ret +isastr: push de +cklp: dec de + dec hl + ld a,(hl) + or a + jr z,pdret + ld a,(de) + xor (hl) + and 0x7f + jr z,cklp +pdret: pop de + ret + +cmptst: ld a,(casefl) + or a ; case insensitive? + push af ; save z flag + ld a,(de) + jr z,noucs1 ; nope - use the character as is + and 0x7f + call ucsa ; force upper case +noucs1: ld c,a + pop af ; get z flag back for case insensitive + ld a,(hl) + jr z,noucs2 + and 0x7f + call ucsa ; mask and force upper case +noucs2: xor c ; get in the rest of it + and b ; mask as necessary + ret + +silnce: call scnnum ; get a time + or a + jr nz,stimok + ld a,15 ; none given, default to 15 seconds +stimok: call clerw2 + ld b,a + inc b +setsil: ld c,b +xsecnd: dec c + ret z + push bc + ld de,600 + call setspd ; set the speed +xrtrms: ld b,96 ; hang loose a while +xmslp: djnz xmslp + push hl + call procch + pop hl + jr c,xnchar + call stufw2 + pop bc + jr setsil +xnchar: dec hl ; cound down second + ld a,h + or l + jr nz,xrtrms + pop bc + jr xsecnd ; loop back + +; read a char, and see if it matches the delimiter in c + +redsch: dec de +chksch: ld a,(de) + and 0x7f ; nuke parity + inc h + dec h ; is h zero? + jr nz,nowsp ; no - don't do white space check + call iswa ; is it white space? + scf + ccf ; clear carry + ret nz ; return nz if not (no match) + xor a ; clear a so 'cp h' below forces zero +nowsp: xor h ; see if char matches delimiter + ret + +.dseg +.extern mode +mode: db lf_bit + +.useg +casefl: ds 1 ; case insensitive string match? +.extern vars +vars: ds 26 ; space for 'a' through 'z' +.extern strngs +strngs: ds 512 ; space for 35 strings +.extern _check_ +_check_: + ds 1 ; check space + \ No newline at end of file diff --git a/source/CHAT.Z b/source/CHAT.Z new file mode 100644 index 0000000..0e7002d --- /dev/null +++ b/source/CHAT.Z @@ -0,0 +1,993 @@ +; chat.z - handle send/receive chat scripts + +.incl "c:vars" + +.var _sends 0 ; send string +.var _expect 40 ; expect string +.var _explen 80 ; length of expect string +.var _sndlen 81 ; length of send string +.var _time 82 ; time in seconds to try +.var _tries 83 ; number of times to try before failing +.var _yes 84 ; new state if success +.var _no 85 ; new state if failure +.var _slow 86 ; should we slow down outgoing string +.var _curtry 87 ; current try at this one + +.useg +.extern area +area: ; area where parsed line lives +sends: ds 40 ; send string +expect: ds 40 ; expect string +explen: ds 1 ; length of expect string +sndlen: ds 1 ; length of send string +time: ds 1 ; time in seconds to try +tries: ds 1 ; number of times to try before failing +yes: ds 1 ; new state if success +no: ds 1 ; new state if failure +.extern slow ; externed for hangup +slow: ds 1 ; should we slow down outgoing string +curtry: ds 1 ; current try at this one + +.cseg + +.macro table byte,addr + dw addr + db byte +.endm + +pchat: + pop hl ; needed for chaining + pop hl + jr ncchat ; don't clear vars when we chain + +.extern chat +chat: + call initch ; set up +ncchat: ld a,1 + ld (opentr),a ; clear number of open tries + ld hl,scning + dec (hl) + call gofil ; get and open a file + ld hl,scning + inc (hl) + jr nc,dochat ; open OK, go do it + ld hl,cmdlin + call byp ; look at first char on command line + or a ; no line - give up right now + ret z +retryo: ld hl,(chtusr) + inc h ; turn to a fcb type drive + ld (auxfcb),hl ; replace drive / user in fcb + jr tryaux ; try it again + +.extern ichat ; enter here with command tail at 80: this +ichat: ; is used to handle files given to qterm when + ; initially invoked + call initch + ld hl,buffer + 1 + call scnfcb ; go parse an fcb + call byp + ld (ppp),hl ; save base of parameter strings + call xferax ; stuff it in auxfcb +tryaux: ld a,(opentr) + neg + ld (scning),a + call opnaux ; and open it + ld hl,scning + ld (hl),0 ; reset scanning flag + inc hl ; how many tries + dec (hl) ; second time - must be the .LBR failing + jr nc,dochat ; opened OK, go use it + ret nz ; give up if open failed + inc hl + call scnfcb + ld hl,fcb + ld de,auxfcb ; set pointers to fcbs + ld b,34 ; 34 bytes to shift +swplp: ld a,(de) + ld c,a + ld a,(hl) + ld (de),a + ld (hl),c ; swap bytes at hl and de + inc hl + inc de ; move pointers + djnz swplp ; loop till done + jr retryo ; and retry the open +dochat: call nz,setlbr ; if .LBR file open, then set to read it + jp c,fnferr ; file not found - complain & exit + ld hl,(ppp) + ld de,0x80 ; move the parameters to 0x80 + ld b,d + ld c,e ; also 0x80 bytes to move + ldir ; shift them down + call prepclv ; prepare the command line variables + ld hl,script + ld (ppp),hl ; save address where lines will go + ld de,script + 1 + ld bc,4096 + 1536 - 1 + xor a + ld (hl),a + ldir ; clear out the script and work areas + ld a,0x7f + ld (b7flag),a ; flag to zap bit 7 +loop: push bc ; save line number in bc + call getlin ; get a line + pop bc + jr c,cnvrt ; eof - now convert labels and parameters + ld hl,auxlin + ld a,(hl) + or a + jr z,loop ; ditch blank lines + inc hl + cp '!' ; first char a '!' + jr nz,nobang ; no - so process normally + ld e,(hl) + call incbyp + ld a,e + cp ';' ; comment?? + jr z,loop ; yes - throw it away _RIGHT_NOW_ + cp ':' ; label?? + push af + call z,label ; yes - save it away + pop af + ld hl,auxlin + 1 + cp '$' ; parameter default? + jr nz,nodolr + ld a,0x81 ; illegal value - not normally seen + ld (hl),a ; set so we don't change +nodolr: cp '@' + jr nz,nobang + ld a,0x82 ; ditto + ld (hl),a +nobang: ld de,auxlin ; where the line is + ld hl,(ppp) ; where we want it to go +xferlp: ld a,(de) + ld (hl),a ; move a byte + inc hl + inc de ; bong the pointers + or a + jr nz,xferlp ; loop till whole line is moved + ld (ppp),hl + inc c + ld de,work - 4 + sbc hl,de ; did we overflow? + jr c,loop ; no - back for more +toobig: call ilprt + db 'Script is too large (4K maximum)\r\n\0' + ret + +; come here when script has been read, labels noted and parameters set + +cnvrt: xor a + ld hl,(ppp) + ld (hl),a ; add an empty line to terminate + dec a + ld (lbrcnt),a ; disable library count + jr doscr + +perr: call ilprt ; print an error msg + db 'Bad line in file\r\n\0' + call dim ; set dim mode + ld hl,auxlin ; point hl at string +erplp: ld a,(hl) ; get a character + or a + jr z,doneer ; exit if done + push hl + ld c,a + call scrout ; send it + pop hl + inc hl + jr erplp +doneer: call crlf ; print a newline + jp main ; and exit to terminal mode + +doscr: ld a,0xff + ld (b7flag),a ; clear bit 7 zap flag + ld (cvtp),a + ld a,1 ; initially state 1 +scrlp: or a + jr nz,moresc + push bc +finisp: pop bc +finis: call ilprt ; state zero means we're done + db '\r\nDone\r\n\0' + jp main ; straight to main to avoid a second redraw +moresc: ld c,a ; save state in c for command recovery + dec a + ld b,a + ld hl,script ; point at script + jr z,rps ; if line 1, we're all set +srlp: ld a,(hl) + inc hl + or a + jr nz,srlp ; loop till we hit a null + djnz srlp +rps: ld (redptr),hl ; set up read pointer + push bc ; save line number in c + call getwl + jr c,finisp ; all out of script, exit + call parse ; chop it up + jr c,perr ; drop on an error + pop bc ; line number back to c + xor a + ld (curtry),a ; zero out current try +retry: ld ix,area + ld a,(sndlen) + ld b,a + or (ix + _explen) ; if both strings are empty + jr z,finis ; we fell off end of script: return + jp m,commnd ; explen == -1 => command type line: go do it + push ix + pop hl ; address to hl == address of send string + ld a,b + or a ; zero length? + call nz,sendcs + ld a,(ix + _explen) ; get expect length + or a + jr z,expok ; not expecting anything, match by default. + call clerw2 ; clear the work buffer for incoming chars + ld a,(mode) + and lf_bit + jr nz,scanex + push ix + call pexstr + db '\r\nLooking for: \0' + pop ix +scanex: ld c,(ix + _time) ; get time to c +second: ld de,600 + call setspd ; set the speed +qrtrms: ld b,96 ; hang loose a while +qmslp: djnz qmslp + push hl + push bc + call procch ; ok, see what characters are waiting + jr c,nochar ; nothing waiting - bypass all this mess + call stufw2 ; and save in the other buffer + ld hl,work2 + 257 + 64 + 1 + ld c,(ix + _explen) + or a + sbc hl,bc ; get that point in buffer + ld de,expect ; expect string address to de +chekxp: ld a,(de) + cp (hl) ; did we match a byte? + jr nz,nochar ; no - skip and try again + ldi ; move pointers, adjust and test bc + jp pe,chekxp ; not done: check some more +gotit: pop bc ; got a match! + pop de ; clean up stack first +expok: ld a,(yes) ; get success state transition + push af + ld a,(explen) ; see if we actually had anything to match + or a + jr z,pjs ; nope, so skip all of this + ld a,(mode) + and mat_bit + jr nz,pjs ; don't print if match disabled + call pexstr + db '\r\nMatch: \0' ; tell that we matched +pjs: pop af ; restore success state + jp scrlp +nochar: pop bc + pop hl + dec hl ; count down second timer + ld a,h + or l + jp nz,qrtrms + dec c ; second timeout done? + jp nz,second ; loop back if not + ld a,(mode) + and mat_bit + jr nz,nopf ; don't print if match disabled + push ix + call ilprt + db '\r\nFail\0' ; failed + pop ix +nopf: inc (ix + _curtry) ; bump try count + ld a,(curtry) + cp (ix + _tries) ; did we exceed allowed tries? + jr nc,failed ; yes - complete fail - do state transition + ld a,(mode) + and mat_bit + jr nz,jrt ; don't print if match disabled + push ix + call ilprt + db ', retry\r\n\0' + pop ix +jrt: jp retry +failed: ld a,(no) ; get fail state transition + push af + call crlf ; throw a new line + jr pjs + +.extern canscr +canscr: call ilprt ; quit if so + db '\r\nCancelled\r\n\0' + jp main ; long jump to main since we don't know what + ; state the stack is in + +commnd: ld a,c ; restore line number from c + ld (prmpfl),a ; use non-zero value to set prompt flag + push af ; save line number + ld hl,cmdret + push hl ; push a return address to get back here + ld a,(time) ; get the command letter + call ucsa ; force upper case + ld hl,chttbl +.dseg +chttbl: table '.',break + table 0x2c,hangup ; we'd like to say ',',hangup but ZSM barfs + table 0x82,eval ; was @, but converted during readin to avoid + ; getwl substitute problems + table 0x81,setstr ; was $, changed for the same reason. + table '#',test + table '%',stest + table '&',mattog + table '<',sinput + table '>',messag + table '~',cf + table '[',multi + table '(',fileio + table 'B',baud + table 'C',catch + table 'E',echo + table 'H',hdxtog + table 'J',jctog + table 'K',ldfnk + table 'L',lftog + table 'M',msbtog + table 'N',newdsk + table 'O',optog + table 'P',print + table 'Q',quit + table 'R',recv + table 'S',send + table 'U',0x0276 + table 'V',vttog + table 'W',witog + table 'X',pchat + table 'Y',hold + table 'Z',cclose +endctb: +.cseg + ld b,{endctb - chttbl} / 3 +tbllp: ld e,(hl) + inc hl + ld d,(hl) ; get next table entry to de + inc hl + cp (hl) ; check byte in a vs. table letter + inc hl + push de ; push entry point + ret z ; if we matched this takes us to the code + pop de ; restore stack + djnz tbllp ; loop till we run out of table + pop hl ; clean up stack +cmdret: xor a + ld (prmpfl),a ; clear prompt flag + pop af ; get line number back + inc a ; bump by one: commands always succeed + jp scrlp ; loop back for more + +.extern sendcs +sendcs: + inc b ; clear the zero flag, and account for an + ; extra djnz + push bc + push hl ; stack these for later popping + jr sendi ; and jump to where we delay a bit + +sendcl: push bc + ld a,(hl) ; get next character + inc hl ; bump + push hl ; and save pointer + cp 0xff ; was it a -1? + jr nz,nosbrk ; no - check for -2 + call break ; send a break + jr chrsnt +nosbrk: cp 0xfe ; check for -2 + jr nz,mdmchr ; no - send char normally + ld de,1200 ; set for a 1 second delay + call msip ; 1000 1/1000th of a second == 1 second + jr chrsnt ; and check for incoming chars +mdmchr: push af + call modop ; send the character + pop af ; restore back to a +chrsnt: call lstmod ; keep tabs on incoming chars + call lstmod + ld a,(slow) + or a ; do we need to slow it down +sendi: call nz,tenth ; wait a while if so + pop hl + pop bc + djnz sendcl ; and keep on sending + ret + +parse: ld hl,area ; point de at this record + push hl + ld de,area + 1 + ld bc,_curtry + ld (hl),b + ldir ; nuke parse area + pop de ; restore input pointer to de + ld hl,auxlin + ld a,(hl) + cp '!' ; bang? + jr nz,nopb ; nope - parse as usual + inc hl + ld a,(hl) ; get command letter + inc hl + ld bc,_explen ; move enough stuff to fill to explen + ldir + ex de,hl + ld (hl),b ; null terminate just in case + inc hl + ld (hl),-1 ; set -1 in sndlen as a flag + inc hl + ld (hl),a ; and save the letter + ret +nopb: ld hl,slow + ld (hl),b + ld a,(auxlin) ; get delimiter + cp 'z' + 1 ; greater than 'z'? + jr c,noslow ; skip if not + ld (hl),a ; else set slow flag +noslow: ld hl,auxlin ; point hl at incoming line + call scanst ; scan send string + ret c + ld (sndlen),a ; save length + call scanst ; scan expect string + ld (explen),a + ret c ; exit if error + xor a + call rednum ; parse time + ret c + or a + jr nz,gottim + ld a,15 ; if no time or zero, default to 15 +gottim: ld de,time + ld (de),a ; save the time + inc de + xor a + call rednum ; scan tries + ret c + or a + jr nz,gottry + inc a ; if none or zero set to 1 +gottry: ld (de),a + inc de + pop bc + ex (sp),hl + ld a,l ; get cur line num to a + ex (sp),hl + push bc + inc a ; add one to get default yes + call rednum ; read success + ret c + ld (de),a + inc de + xor a + call rednum ; and finally the fail value + ret c + ld (de),a + inc a + ret + +.extern scanst +scanst: ld b,40 ; count max of 40 chars + ld a,(hl) ; get the delimiter to a + ld (de),a + or a + ret z ; return with z to show empty line + ld c,a ; copy to c + inc hl ; point to next char + +.extern scnstp +scnstp: call parst ; chomp up the string + ret c +donest: ld a,40 + sub b ; get length to a + dec b + inc b ; test b for zero + jr z,bzero ; if b not zero +setde: inc de ; bump de + djnz setde ; till b runs out +bzero: or a ; clear the carry + ret + +.extern parst +parst: + xor a + ld (de),a ; add a trailing null + ld a,(hl) ; get next char + cp c ; delimiter? + ret z ; yes - all done on this string + or a + ccf + ret z ; handle error + inc hl + cp '\\' ; backslash gets special treatment + call z,backsl ; parse the backslash escape + inc b + dec b ; any space left? + jr z,parst ; no - just get end of line + dec b + ld (de),a ; save the char away + inc de + jr parst + +.extern backsl +backsl: ld a,(hl) ; get char after backslash + inc hl + or a ; end of string? + scf ; flip carry to true + ret z ; return on zero w/ error + cp 'k' + jr nz,nobrk + ld a,0xff + ret +nobrk: cp 'd' + jr nz,nodel + ld a,0xfe + ret +nodel: cp 'f' + jr nz,noff + ld a,'\f' + ret +noff: cp 'b' + jr nz,nobksp + ld a,'\b' + ret +nobksp: cp 't' + jr nz,notab + ld a,'\t' + ret +notab: cp 'n' + jr nz,nonl + ld a,'\n' + ret +nonl: cp 'r' + jr nz,nocr + ld a,'\r' + ret +nocr: cp 'e' + jr nz,noesc + ld a,'\e' + ret +noesc: cp 'x' + jr nz,nohex + push bc + ld bc,0x0200 +gethex: ld a,(hl) + sub '0' + cp 10 + jr c,hexok ; valid digit - use it + sub 'A' - '0' + cp 6 + jr c,hexlok ; valid A-F + sub 'a' - 'A' + cp 6 + jr c,hexlok ; valid a-f + ld a,b + add a,0xfe ; check if b was still 2 + jr endoct +hexlok: add a,10 ; letter values need 10 added +hexok: inc hl ; bump pointer + sla c + sla c + sla c + sla c ; c *= 16 + or c ; a += c + ld c,a + djnz gethex + jr endoct +nohex: sub '0' ; check for octal digit + cp 8 + jr c,octal ; got one - handle it + add a,'0' ; restore character + or a ; clear carry + ret +octal: push bc + ld b,2 ; 2 more chars to get + ld c,a ; save current value in c +getoct: ld a,(hl) ; get another char + sub '0' + cp 8 ; convert and test + jr nc,endoct ; no good - skip + inc hl ; now we move the pointer + sla c + sla c + sla c ; c *= 8 + or c ; a += c (and clear the carry) + ld c,a ; back to c + djnz getoct ; loop till three chars done +endoct: ld a,c ; char back from c + pop bc + ret + +rednum: ex af,af' ; save default value in a' + ld a,(hl) + or a ; get and test a delimiter + jr z,usedef ; end of string - use default + inc hl + ld c,a ; save it away + ld a,(hl) + cp c ; check see if anything in field + jr nz,isnum ; yes - go parse it +usedef: ex af,af' + ret +isnum: ld b,0 +scnnum: ld a,(hl) + or a + jr z,usedef ; end of string: exit + cp c + jr z,gotnum ; found delimiter: exit + inc hl + sub '0' + cp 10 ; did we find a digit? + ccf ; flip carry: set => error + ret c ; so return + push af ; save converted digit + ld a,b + add a,a + add a,a + add a,b + add a,a + ld b,a ; b *= 10 + pop af + add a,b + ld b,a ; b += new digit + jr scnnum +gotnum: ld a,b + ret + +; label saves a label / line number pair in the symbol table + +label: push hl ; save input pointer + ld hl,work + 1016 ; look in symbol table + ld de,8 ; step 8 at a time +findlb: add hl,de ; move to next + ld a,(hl) + or a ; end of table? + jr nz,findlb ; no - look at next one + pop de ; input pointer back to de + ld b,7 ; 7 bytes of label +scanit: ld a,(de) ; get an input byte + or a + jr z,elbl ; null + cp ' ' + jr z,elbl ; or space terminates it + ld (hl),a + inc de + inc hl + djnz scanit ; loop till 7 bytes moved + jr addnum ; go add the line number +elbl: ld (hl),0 + inc hl ; zero fill + djnz elbl +addnum: ld (hl),c ; save the line number + xor a + inc hl + ld (hl),a ; zero fill end of symtab + ld (work + 1528),a ; prevent overflow + ret + +; flabel - find a label / line number pair in the symbol table + +flabel: ex de,hl ; label pointer to de + ld hl,work + 1016 ; look in symbol table +flblp: ld bc,8 ; step 8 at a time + add hl,bc ; move to next + ld a,(hl) + or a ; end of table? + jr z,exdert ; yes - undefined labels do odd things + ld a,(de) + cp (hl) ; check first char + jr nz,flblp ; nope - try again + push hl + push de ; save pointers + ld b,7 ; 7 bytes of label +fscnit: ld a,(de) ; get an input byte + or a + jr z,nomtch ; end of input + cp (hl) + jr nz,nomtch ; no match, but it may be end of label + inc de ; bump pointers +endok: inc hl + djnz fscnit ; all 7 bytes done: we got it + ld a,(hl) ; get line number to a + pop hl + pop hl ; clean stack +exdert: ex de,hl ; input pointer back to hl + ret ; and home we go +nomtch: ld a,(hl) + or a + jr z,endok ; aha - end of entry in symtab, fake it + pop de + pop hl ; no good, restore pointers + jr flblp ; back to look at the next one + +; fparam - find parameter from array at 0x80 whose number is in a + +.extern fparam +fparam: ld hl,strngs ; point at strings +fplp: or a ; finished? + ret z ; return if so - hl points to string + ld e,a ; save a +byppl: ld a,(hl) ; step over non-null characters + inc hl + or a + jr nz,byppl + ld a,e ; get string number back + dec a ; one more done + jr fplp + +; kilstr - kill string parameter hl points to + +.extern kilstr +kilstr: ld a,(hl) + or a + ret z ; if it's already empty, we're done + push hl ; save hl for later + ld e,l + ld d,h +ksel: inc hl ; loop to find end of old string + ld a,(hl) + or a + jr nz,ksel +shftlp: ld a,(hl) ; by now hl points to end, de to string + ldi ; move another byte + inc a ; test for end of data + jr nz,shftlp ; loop till all done + ex de,hl +xfill: ld (hl),0xff ; replace all the 0xffs + inc hl + ld a,(hl) + cp 'A' ; we've got an 'A' at the end as a stopper + jr nz,xfill + pop hl ; restore pointer to string + ret ; all done + +; setstr - set a string variable + +setstr: call areabu ; point hl at command tail + call pnum + ret nc + inc hl ; skip over letter + push hl ; and save address of source + push af ; save parm number + call fparam ; get ... + pop af + cp 9 + jr nc,repl ; 9 or above is letter string - force replace + ld a,(hl) + or a ; anything there yet? + jr nz,pbcret ; yes - do nothing +repl: call kilstr ; and kill current string + ex (sp),hl ; save target, restore source + call byp ; strip white space + ex de,hl ; source to de + pop hl ; dest back to hl + xor a ; want this null terminated + ; and fall into insstr to put it in place + +; insstr - shift string addressed by de to string var at hl, use char in +; a (or null) to terminate string at de + +.extern insstr +insstr: push bc ; save bc + ld b,a ; term char to b + ld c,0 ; count in c + push de ; save source in de +fslen: ld a,(de) + or a ; null + jr z,estr1 ; ends the string + cp b ; term char? + jr z,estr ; yup end of string as well + inc c ; count + inc de ; and move pointer + jr fslen +estr: xor a +estr1: ld b,a ; set len to word in bc + cp c + jr z,pop2r + push hl ; save target in hl + push bc ; and length as well + inc bc + ld hl,strngs + 511 ; start from very top of string space + cpdr ; look for 0 on end of last string + pop bc + jr nz,isok ; nz means we didn't find it which is OK + pop hl ; get hl back +pop2r: pop bc + pop bc ; clean up stack, de points to end already + ret ; exit right now +isok: pop de ; target back to de + inc hl + push hl ; source of lddr on stack + sbc hl,de ; hl contains count to move up + push bc ; length back on stack + ld b,h + ld c,l ; count to move to bc + pop hl ; length back to hl + ex (sp),hl ; resave length, get lower move point + push de ; target back on stack + ld de,strngs + 511 + inc bc ; why in the name of H*LL we have to inc this + inc bc ; twice, I don't know. However, it works. + lddr ; shift it all up to make the hole + pop de ; target back + pop bc ; length of string back + pop hl ; source + push de ; save target + ldir ; move string into place + ex de,hl ; updated source pointer back to de + pop hl ; original hl back +pbcret: pop bc ; restore bc + ret + +pnum:; call ucsa + sub '1' + cp 9 ; is it valid + ret c ; default param + sub 'A' - '1' + cp 26 ; valid letter? + ret nc ; return if not + add a,9 ; convert above parameters + scf ; set carry to show it's OK + ret + +; prepclv - convert command line args to $1 through $9 + +prepclv: + ld de,0x80 ; point at command line params + ld b,0 ; set counter to zero +pcvlp: push de + ld a,b + call fparam ; address this parameter + call kilstr ; get rid of the old + pop de + ex de,hl + call byp ; find text + ex de,hl + ld a,' ' + call insstr ; drop the string in place + inc b + ld a,b + cp 9 ; loop till nine are done + jr nz,pcvlp + ret + +; get a line from wherever we're reading, stuff it in auxlin + +getwl: ld hl,(redptr) ; pick up read pointer + ld a,(hl) + or a ; first byte zero? + scf + ret z ; return carry to show end of input + ld de,auxlin ; auxlin is where we'll put it +gwlp: ld a,(hl) ; get a byte + cp '$' + jr z,gwstr ; substitute strings, + cp '@' + jr z,gwnum ; numbers + cp '`' + jr z,gwlbl ; and labels +nullt: ld a,(hl) + ldi ; otherwise just transfer + or a ; and test the byte + jr nz,gwlp ; loop if more + ld (redptr),hl ; save updated read pointer + ret + +gwstr: inc hl ; point at letter code for string wanted + call ucsahl + ; ld a,(hl) ; get it + call pnum ; see if it's a valid string number + jr nc,nullt ; if not just copy the character as is + inc hl + push hl ; save string we're reading from + push de + call fparam ; get the string + pop de +scp: ld a,(hl) + or a + jr z,phlgwl + ldi + jr scp +phlgwl: pop hl + jr gwlp + +gwnum: inc hl ; point at variable letter + call ucsahl ; convert to upper case + sub 'A' ; make it into an index + cp 26 ; in range? + jr nc,nullt ; nope - convert as a straight letter + inc hl ; skip over the variable number + push hl ; save source + push de ; and target + ld e,a + ld d,0 + ld hl,vars + add hl,de + ld a,(hl) ; fetch the value + pop hl ; restore target to hl + jr dumpa + +gwlbl: inc hl + push de ; save target + call flabel ; find the label + inc a + ex (sp),hl ; restore target, save source + +dumpa: ld (hl),'0' - 1 ; put in hundreds digit + ld e,0 +hundlp: inc (hl) + sub 100 + jr nc,hundlp + add a,100 + call cinc + ld (hl),'0' - 1 +tenlp: inc (hl) + sub 10 + jr nc,tenlp + call cinc + add a,'0' + 10 + ld (hl),a + inc hl + ex de,hl ; target back to de + pop hl ; source to hl + jr gwlp ; and we're done + +; cinc - inc hl only if (hl) != 0: used to format numbers + +cinc: dec e + inc e ; test e + jr nz,docinc ; already set, force a save + ld c,a ; save a + ld a,(hl) + cp '0' ; pointing at a zero? + ld a,c ; restore a + ret z ; return if so, +docinc: inc hl ; otherwise bump hl + inc e ; and set e + ret + +; print expect string, but ignore control characters + +pexstr: call dim ; dim mode + pop hl + call prtslp ; print inline string + push hl ; resave return address + ld hl,expect + ld bc,(explen - 1) ; get explen to b +prtncc: ld a,(hl) ; get a character + cp ' ' + jr c,nopcc ; less than space, don't print + cp 0x7f ; check >= delete + push bc + push hl + ld c,a ; char to c for printout + call c,scrout ; out it goes if legal + pop hl + pop bc +nopcc: inc hl ; move pointer + djnz prtncc ; loop till all done + jp crlf + +.dseg +.extern scning +scning: db 0 +.extern opentr +opentr: db 0 ; should be useg, but need to bump hl for lbrs +.extern lbrs +lbrs: db '/QTERM.LBR\0' ; name of qterm script library + +.useg +redptr: ds 2 +.extern ppp +ppp: ds 2 ; parameter pointer +.extern chtusr +chtusr: ds 1 ; extra drive / user to try for chat scripts +.extern chtdrv +chtdrv: ds 1 +cvtp: ds 1 ; do we convert params and numbers in getwl + \ No newline at end of file diff --git a/source/DIR.Z b/source/DIR.Z new file mode 100644 index 0000000..7c568d3 --- /dev/null +++ b/source/DIR.Z @@ -0,0 +1,144 @@ +; dir.z - print directories for qterm + +.incl "c:vars" + +.extern dir +dir: + call gauxfc ; get a fcb into auxfcb + ld hl,auxfcb + 2 + ld a,(hl) ; get first actual character of filename + cp ' ' + jr nz,isfil ; not a space - there was something + ld de,auxfcb + 3 + ld bc,10 + ld (hl),'?' ; else fill with '?'s to do *.* + ldir +isfil: ld c,26 + ld de,auxlin + call bdos + call dim ; set dim mode + call clrpsp ; set up to page output + ld c,17 + xor a + ld (nlp),a + push af +dslp: ld de,auxfcb + call usrbds + inc a + jr z,dirxit + push af + ld a,(auxfcb + 1) ; get the drive code + add a,'@' ; convert to a letter + call tabexp ; and print it + ld hl,(auxfcb) ; get the user number + call decob ; print it too + ld a,':' + call tabexp ; and a colon + pop af + rrca + rrca + rrca + ld hl,auxlin - 31 + ld e,a + ld d,0 + add hl,de + ld b,8 ; 8 chars filename + call pdir ; print filename + ld a,'.' + push hl ; save fcb address + call tabexp ; print a period + pop hl + ld b,3 + call pdir ; and print the extension + dec hl + dec hl + ld a,(hl) ; let's see about R/O files + add a,a + dec hl + ld a,(hl) + adc a,a + adc a,a ; get r/o and sys bits to bottom + ld hl,rostr ; look at characters we have +.dseg +rostr: db ' -+*' +.cseg + and 3 ; get just the bits we want + ld e,a + ld d,0 + add hl,de ; index into characters + ld a,(hl) ; get one + call tabexp ; and print it + pop af + inc a + push af + and 3 + ld hl,sstr +.dseg +sstr: db ' \0' ; 3 spaces to separate +.cseg + jr nz,sepspc + ld hl,crlfm +sepspc: call prtsl1 +; for ^X to can a directory, add a 'jr c,dirxit' here +ds18: ld c,18 + jr dslp + +dirxit: pop af ; restore file count + or a + jr z,nfm + and 3 + call nz,crlf ; print newline if needed + jp bright ; otherwise restore bright mode +nfm: call ilprt + db 'No files' +crlfm: db '\r\n\0' ; complain if no files + ret + +; pdir - print chars from hl, count in b + +pdir: ld a,(hl) ; get next char + inc hl + and 0x7f ; get rid of top bit + push hl ; save regs + push bc + call tabexp ; print it + pop bc + pop hl + djnz pdir ; loop till all done + ret + +; nxtnam - get next name from work to fcb, return z if no files left + +.extern nxtnam +nxtnam: + ld hl,(fnbspt) ; get last address saved to + ld de,(fnbrpt) ; current read address + or a + sbc hl,de ; test them + ret z ; return z if equal + ex de,hl ; read address to hl + ld de,fcb ; fcb address to de + ld bc,16 ; 16 chars to go + ldir ; move them + ld (fnbrpt),hl ; and save read pointer + ld a,(fcb) ; get lead byte + cp 0xe5 ; check for erasure + jr z,nxtnam ; loop back if gone + ld a,b + ld (fcb + 13),a + ld (fcb + 33),a ; zero out extent and block numbers + ret ; ret with no z flag + +; data area + +.useg +.extern newusr +newusr: ds 1 ; new user for dir command +.extern newdrv +newdrv: ds 1 ; new drive for dir command +nlp: ds 1 ; number of lines on current page +.extern fnbspt +fnbspt: ds 2 ; fnbuff save pointer +.extern fnbrpt +fnbrpt: ds 2 ; fnbuff read pointer + \ No newline at end of file diff --git a/source/FILE.Z b/source/FILE.Z new file mode 100644 index 0000000..140087c --- /dev/null +++ b/source/FILE.Z @@ -0,0 +1,434 @@ +; file.z - file handling odds and ends + +.incl "c:vars" + +.extern gofil ; get a filename, and open into auxfcb +gofil: + call gauxfc ; get a filename, parse in aux fcb + ret c ; esc - exit now + jr z,nowc ; no wildcards + +.extern opnaux +opnaux: + xor a + call clrw2 ; set pointers for read + ld de,auxfcb + ld c,open ; open the file + call usrbds + add a,1 + ret nc ; return now if open ok + ld a,(scning) + add a,a + ret c + +.extern fnferr +fnferr: call ilprt ; beef and complain about file not found + db 'File not found\r\n\0' + scf + ret + +.extern nowc +nowc: +.extern nowcp +nowcp: + call ilprt + db 'Can\'t handle wildcards\r\n\0' + scf + ret + +.extern gauxfc +gauxfc: + call getfcb ; get a filename to fcb at 0x5b + push af ; save return status in carry + call byp + ld (ppp),hl ; save pointer for chat + pop af ; restore carry + +.extern xferax +xferax: ; transfer fcb at fbc to auxfcb + ld bc,34 ; load the registers + +.extern xferaz +xferaz: + ld hl,fcb + ld de,auxfcb + ldir ; and copy to auxfcb + ret + +.extern getfcb +getfcb: + call prompt + db 'Filename? \0' ; prompt and input + ld a,(ipbuf) + cp '\e' ; carry? + scf + ret z ; return now with carry if so + ld hl,ipbuf + +.extern scnfcb ; parse a fcb at hl to 0x5b +scnfcb: + call byp ; step over any spaces + push hl + ld hl,fcb + ld de,fcb + 1 + ld bc,33 + ld (hl),b + ldir ; fill fcb with zeros + ld bc,(curusr) + inc b ; bump logged drive for fcb + ld (fcb),bc ; drop in user and drive now + pop hl ; get input pointer to hl + push hl ; but save it again + call ucsahl ; get (possible) drive spec to a + cp 'A' + jr c,trynum + cp 'P' + 1 ; valid drive letter? + jr nc,respar + sub '@' + ld b,a ; save the drive in b + inc hl + ld a,(hl) ; get the next letter + cp ':' ; is it a ':'? + jr z,usebc ; yes, only drive was given: use what we have +trynum: sub '0' + cp 10 ; do we have a valid digit? + jr nc,respar ; no - reset and ignore whole mess +scnusr: ld c,a ; save in c + inc hl + ld a,(hl) ; get the next letter + cp ':' ; is it a ':'? + jr z,usebc ; yes - we got a user: go process it all + sub '0' + cp 10 + jr nc,respar ; oops - not a number: reset and ignore + ld e,a ; current digit to e + ld a,c + add a,a + add a,a + add a,c + add a,a ; a = c * 10 + add a,e ; + e + and 0x1f ; and 0x1f gives updated user number + jr scnusr ; loop back for another go +usebc: inc hl ; point at char after ':' + ex (sp),hl ; save on stack + ld (fcb),bc ; save new drive / user codes away +respar: pop hl ; hl points at filename + ld de,fcb + 2 ; point de at name area + ld b,8 + call scanfn ; parse the filenane + ld a,(hl) + cp '.' ; '.' => extension + jr nz,nodot + inc hl ; step over it +nodot: ld b,3 + call scanfn ; parse it + push hl ; save pointer to text + ld hl,fcb + 2 + ld a,'?' ; check for '?'s in the fcb (wildcards) + ld bc,11 + cpir + pop hl ; restore text pointer + ret ; with z flag set for a match + +scanfn: call ucsahl ; get a char + or a ; exit on a zero byte + jr z,gotnam + cp '.' ; or a '.' + jr z,gotnam + cp ' ' ; or a ' ' + jr z,gotnam + inc hl ; move input pointer + dec b + inc b ; test b + jr z,scanfn ; loop back if zero + ld (de),a ; save char in fcb + cp '*' ; '*'? + jr z,gotstr ; handle elsewhere if so + inc de ; move output pointer + dec b ; one less char to go + jr scanfn +gotnam: dec b ; decrease b till all done + ret m + ld a,' ' ; get a space + ld (de),a ; put it in fcb + inc de ; and move pointer + jr gotnam +gotstr: ld a,'?' ; put in a '?' + ld (de),a + inc de ; move pointer + djnz gotstr ; loop till all full of '?'s + jr scanfn ; continue parse + +.extern areabu +areabu: call areabp ; do an areabp, and ucsa the char + +.extern ucsahl +ucsahl: ld a,(hl) ; get char at hl + +.extern ucsa +ucsa: cp 'a' + ret c ; return if it's below 'a' + cp 'z' + 1 + ret nc ; or if it's above 'z' + and 0x5f ; force lower case + ret + +; scnwld - enter with hl pointing at a filename, and add the list of +; matching filenames to the list at fnbrpt / fnbspt + +.extern scnwld +scnwld: + call byp ; strip leading space + push hl ; save hl + call scnfcb ; get a filename + call xferax ; transfer to auxfcb + ld hl,(auxfcb) + ld (newusr),hl ; keep a copy of the drive and user + ld de,auxlin + ld c,setdma + call bdos ; set dma to auxlin (seems like a safe place) + ld c,srchf ; do a search first first time +scnlp: ld de,auxfcb + call usrbds ; any more names? + inc a + jr z,scannd ; exit if not + rrca + rrca + rrca ; a *= 32 + ld de,(fnbspt) + ld hl,work + 1024 ; out of room? + or a + sbc hl,de + jr z,nosav ; skip if so - that's 64 filenames anyway! + ld hl,(newusr) ; get drive and user + ex de,hl + ld (hl),e + inc hl + ld (hl),d ; and install them + inc hl + ex de,hl ; save save pointer in de + ld l,a + ld h,0 + ld bc,auxlin - 31 ; -32 because of the inc a above + add hl,bc ; hl now points at name portion + ld bc,14 ; 11 chars to transfer, + 3 for spacing + ldir ; move them characters + ld (fnbspt),de ; put save pointer back away +nosav: ld c,srchn ; go look for next name + jr scnlp +scannd: pop hl ; restore string pointer + ; and step over filename to next one + +.extern unbyp ; move a string pointer in hl to the end of +unbyp: ; the current word + ld a,(hl) + cp ' ' + ret z ; stop when we hit a space + or a + ret z ; or a zero + inc hl + jr unbyp + +.extern getlin ; read a line of data from file +getlin: + ld de,auxlin ; we'll save data in auxlin +gallp: xor a + ld (de),a ; set zero for end of line + push de + call getw2c ; get a character + pop de + ret c ; return carry on physical end of file + cp 0x1a + scf + ret z ; or logical end of file + cp 0x0d + jr z,glstrp ; end of line - stip trailing blanks + cp 0x7f + jr z,gallp ; ignore deletes + cp ' ' + jr c,gallp ; and other control characters + ld (de),a ; save it + ld hl,auxlin + 127 + or a + sbc hl,de ; check line length + jr z,gallp + inc de ; and bump pointer if not at end of line + jr gallp +glstrp: ld hl,auxlin + or a + sbc hl,de ; did we hit the start of the line? + ret z ; return if so + dec de + ld a,(de) ; look at previous character + xor ' ' ; is it a space? + ret nz ; no - leave it and return + ld (de),a ; else turn it into a zero + jr glstrp + +.extern print ; print a local file to the remote +print: + call gofil ; get and open file + ret c ; exit now on an error + ld a,0x7f + ld (b7flag),a ; set flag to strip bit 7 + ld a,2 + ld (cscqfl),a ; enable ctrl ^S ^Q spotting +plp: call lstmod ; check incoming chars + call kbdcc ; check the keyboard + cp 'x' & 0x1f ; quit on a typed ^X + jr z,prsent + ld a,(cscqfl) ; ^S seen? + rrca + jr c,plp ; yes, so loop for a while + call getw2c ; get next char to send + jr c,prsent ; exit on physical end of file + and 0x7f + cp 0x1a + jr z,prsent ; or logical end of file + cp '\n' + jr nz,nonlin ; not a linefeed + ld a,(lflg) + or a ; are we sending these? +plpz: jr z,plp ; it gets thrown if disabled + ld a,'\n' +nonlin: + cp ' ' + jr c,sendit ; if it's a control char, no echo check + ld hl,ecflg + inc (hl) + dec (hl) ; echo check enabled? + jr z,sendit ; nope - send as is + inc hl + ld (hl),a ; save echo check value +sendit: push af ; save the char + call modop ; send it + ld a,(chd) ; get the character delay + ld e,a + ld d,0 ; to de + pop af + cp '\r' ; did we just send a return? + jr nz,pwait ; nope - handle char delay / echo check + ld de,(nld) ; get new line delay + ld a,(lfecho) ; linefeed echo enabled? + ld (ecval),a ; save it if so + ld (lfwflg),a ; set linefeed wait flag if needed +pwait: call setspd ; make a clock dependant value in hl + add hl,hl ; and double it + ld a,h + or l + jr nz,pwlp + inc l ; minimum 1 +pwlp: push hl + call lstmod ; keep an eye on returning chars + pop hl + ld a,(ecval) ; echo check in progress? + dec a + jr z,ecok ; we got the needed char - break + dec hl + ld a,h + or l ; count hl down + jr nz,pwlp +ecok: ld (ecval),a ; set echo check value to zero + ld hl,lfwflg ; look at linefeed wait flag + ld a,(hl) + ld de,100 ; 100 ms delay in de (prototype) + ld (hl),d ; turn linefeed wait flag off + or a ; test original value + jr z,plpz ; back to send next character if not there + jr pwait ; back to wait again for the extra 1/10th sec. + +prsent: call ilprt + db '\r\nSent\r\n\0' + jr cb7 + +.extern type ; type a file locally +type: + call gofil ; get and open the file + ret c ; quit now on an error + ld a,0x7f + ld (b7flag),a ; set flag to strip bit 7 + call dim ; dim mode + call clrpsp +tlp: call getw2c ; get a character + jr c,teof ; exit on eof + cp 0x1a + jr z,teof ; and on CP/M eof char + call limitc + jr c,tlp + ld (lco),a ; save in last char + call tabexp ; type it + jr nc,tlp ; loop unless ^X cancelled +teof: ld a,(lco) + cp '\n' + call nz,crlf ; output a newline if needed + call bright ; back to bright +cb7: xor a + ld (cscqfl),a ; clear ^S ^Q flag + dec a ; effectively ld a,0xff + ld (b7flag),a ; clear bit 7 zap mask + ret + +.extern newdsk +newdsk: + call prompt + db 'Drive? \0' ; prompt and input + ld hl,ipbuf ; parse a full fcb, although we only want + call scnfcb ; drive and user + ld hl,(fcb) ; get new values + dec h ; set drive correct + ld (curusr),hl ; and save them + +.extern reset ; reset: simply reset CP/M to remove R/O +reset: ; status on changed disks + ld a,(cflg) + or a ; catch file open? + ld c,rescpm + jr z,zbdos ; nope, full reset + ld a,(cfcb + 1) ; get catch file drive + ld hl,fcb + 1 + cp (hl) ; same as reset drive? + ret z ; yes, exit now + dec a + ld e,a + ld c,seldrv ; select chat file drive + call bdos + ld a,(fcb + 1) ; get reset drive + ld hl,1 ; set hl to 1 + ld c,logdrv +shlp: dec a ; count down +zbdos: jp z,bdos ; jump to bdos when done + add hl,hl + jr shlp + +.dseg +.extern b7flag +b7flag: db 0xff +.extern cscqfl +cscqfl: db 0 + +.extern lfecho +lfecho: db 0 +.extern ecflg +ecflg: db 0 +.extern ecval +ecval: db 0 +lfwflg: db 0 + +.useg +lco: ds 1 +.extern curusr +curusr: ds 1 +.extern curdrv +curdrv: ds 1 +.extern auxfcb +auxfcb: ds 37 +.extern auxlin +auxlin: ds 128 +.extern script +script: ds 1024 +.extern scrwrk +scrwrk: ds 3072 +.extern work +work: ds 1536 + \ No newline at end of file diff --git a/source/FLAGS.Z b/source/FLAGS.Z new file mode 100644 index 0000000..b73f553 --- /dev/null +++ b/source/FLAGS.Z @@ -0,0 +1,133 @@ +; flags.z - code to handle trivial flag toggles + +.incl "c:termcap" + +.var ipbuf 0x80 + +.extern msbtog +msbtog: call force ; go see what we're doing to the flag + ld hl,bmask ; address where mask lives + jr z,domsbt ; zero => straight toggle + sub '0' ; was it a '0'? + res 7,(hl) + jr z,domsbt ; bit 7 off (so it gets turned on) if so + set 7,(hl) ; else bit 7 on, so it gets turned off +domsbt: ld a,(hl) + xor 0x80 ; toggle the bit + ld (hl),a + inc a ; now 0xff => no mask becomes zero + ld de,bit7ms ; point de at the "label" +.dseg +bit7ms: db 'Bit 7 masking\0' +.cseg + jr pdisen ; and print whether we turned it on or off + +.extern echo +echo: ld hl,eflg ; point at echo flag + ld de,ecomsg ; and message +.dseg +ecomsg: db 'Remote echo\0' +.cseg + jr toggle ; go do the toggle + +.extern hdxtog +hdxtog: ld hl,hflg ; same as echo + ld de,hdxmsg +.dseg +hdxmsg: db 'Half duplex\0' +.cseg + jr toggle + +.extern jctog +jctog: ld hl,jflg ; same as echo + ld de,jcmsg +.dseg +jcmsg: db 'Control character discard\0' +.cseg + jr toggle + +.extern lftog +lftog: ld hl,lflg ; same as echo + ld de,lfmsg +.dseg +lfmsg: db 'Line feed in print\0' +.cseg + jr toggle + +.extern optog +optog: ld hl,oflg ; same as echo + ld de,opmsg +.dseg +opmsg: db 'Output to printer\0' +.cseg + jr toggle + +.extern vttog +vttog: call initv + ld hl,vflg + ld de,vtmsg +.dseg +vtmsg: db 'VT-100 emulation\0' +.cseg +toggle: call togflg ; flip the flag as needed +pdisen: push af ; save flag state + push de ; and message + call crlf ; throw a new line + call dim ; go into dim mode + pop hl + call prtslp ; print the message + pop af ; restore the flag state + jr z,pdis ; zero - it got disabled + call ilprt + db ' enabled\0' ; print the enabled message + jr finis +pdis: call ilprt + db ' disabled\0' ; or disabled +finis: jp crlf + +.extern togflg +togflg: push hl + push de ; save some registers + call force ; get input state depending on if we're in + pop de ; a chat script + pop hl + jr z,doftog ; zero => straight toggle + sub '0' ; '0' => turn it off + set 0,(hl) ; turn the bit on so the xor turns it off + jr z,doftog + res 0,(hl) ; else clear bit - xor will set it +doftog: ld a,(hl) + xor 1 ; flip the bit + ld (hl),a + ret + +force: ld a,(prmpfl) + or a + ret z ; return zero if we're not in a chat script + call areabp ; get the character + or a ; see if one was given + ret + +.dseg +.extern bmask +bmask: db 0xff +.extern jflg +jflg: db 0 +.extern lflg +lflg: db 0 +.extern oflg +oflg: db 0 + +; These next to _MUST_ remain adjacent, and in this order + +.extern vflg +vflg: db 0 +.extern wflg +wflg: db 0 + +.extern hflg +hflg: db 0 + +.extern eflg +eflg: db 0 + \ No newline at end of file diff --git a/source/KERMIT.I b/source/KERMIT.I new file mode 100644 index 0000000..da46146 --- /dev/null +++ b/source/KERMIT.I @@ -0,0 +1,20 @@ +; kermit.i - definitions for kermit file transfer code + +.var MAXPSIZ 90 ; Maximum packet size +.var MINPSIZ 20 ; minimum packet size +.var SOH 1 ; Start of header +.var DEL 0x7f ; Delete + +.var MAXTRY 10 ; Times to retry a packet +.var MYQUOTE '#' ; Quote character I will use +.var MYHIBIT '&' ; char I use for hi-bit sending +.var MYPACK '~' ; char I use for repeat packing +.var MYPAD 0 ; Number of pad characters I need +.var MYPCHAR 0 ; Padding character I need (NULL) + +.var MYTIME 12 ; Seconds before I'm to be timed out +.var MAXTIM 30 ; Maximum timeout interval +.var MINTIM 4 ; Minumum timeout interval + +.var MYEOL '\r' ; End-Of-Line character I need + \ No newline at end of file diff --git a/source/KUTIL.Z b/source/KUTIL.Z new file mode 100644 index 0000000..874dba3 --- /dev/null +++ b/source/KUTIL.Z @@ -0,0 +1,1121 @@ +; kutil.z - kermit utilities, and all the data storage + +.incl "c:kermit" + + +.var EOF -1 +.var PACK -2 + +.extern spack +spack: + ld hl,packet + push hl + push af + ld a,(pad) + or a + jr z,nospad + push bc + ld b,a +padlp: ld a,(padchr) + push bc + call modopc + pop bc + djnz padlp + pop bc +nospad: ld a,SOH + call cputc + xor a + ld h,a + ld l,a + ld (hdchk),a + ld (chksum),hl + dec b + inc b + jr nz,sxthdr + ld a,(cktyp) ; '1' '2' or '3' + add a,e + add a,0xf2 - ' ' +sxthdr: call cputcs ; len + ld a,c + call cputcs ; number + pop af + call cputc ; type + ld a,b + or a + jr z,nolhdr + push de + ex de,hl + ld a,(cktyp) + sub '0' +setxpl: inc hl + dec a + jr nz,setxpl + ld de,-95 + ld a,d +div95: inc a + add hl,de + jr c,div95 + sbc hl,de + push hl + call cputcs + pop de + ld a,e + call cputcs + ld hl,hdchk + call finchk + call cputcs + pop de + ld a,d +nolhdr: or e + jr z,scksum +sdatlp: pop hl + ld a,(hl) + inc hl + push hl + call cputc + dec de + ld a,d + or e + jr nz,sdatlp +scksum: pop hl + call fincrc + ld a,(cktyp) + cp '3' + jr nz,sck2 + ld a,(chksum + 1) + rrca + rrca + rrca + rrca + and 0x0f + add a,' ' + call modopc + ld a,'2' +sck2: cp '2' + jr nz,sck1 + ld hl,(chksum) + add hl,hl + add hl,hl + ld a,h + and 0x3f + add a,' ' + call modopc +sck1: ld a,(chksum) + and 0x3f + add a,' ' + call modopc + call flsrng + ld a,(eol) + call modopc + ld a,'q' & 0x1f + jp modopc + +.extern rpack +rpack: + call kgettc + jr c,retf3 + cp SOH + jr nz,rpack +recplp: xor a + ld h,a + ld l,a + ld (chksum),hl + ld (hdchk),a + call cinchr + jr c,retf3 + cp SOH + jr z,recplp + ld hl,cktyp + sub (hl) + sub 0xf2 + ld e,a ; length to de + ld d,0 + call cinchr + jr c,retf3 + cp SOH + jr z,recplp + sub ' ' + ld c,a ; number to c + call cinchr + jr c,retf3 + cp SOH + jr z,recplp + ld b,a ; type to b + ld a,e + and 0x80 ; extended packet? + jr z,indat + call rxthdr +retf2: jr c,retf3 + jr z,recplp + ld a,(cktyp) + sub '0' +setlpl: dec de + dec a + jr nz,setlpl +indat: ld a,e + or d + ld hl,packet + jr z,nodat + push de +gdatlp: call cinchr + jr c,retf3 + cp SOH +recpl1: jr z,recplp + ld (hl),a + inc hl + dec de + ld a,d + or e + jr nz,gdatlp + pop de +nodat: ld (hl),a + call fincrc + ld a,(cktyp) + cp '3' + jr nz,rck2 + call kgettc +retf3: jr c,retf + cp SOH + jr z,recpl1 + ld e,a + ld a,(chksum + 1) + rrca + rrca + rrca + rrca + and 0x0f + add a,' ' + xor e + and 0x7f + jr nz,csmerr + ld a,'2' +rck2: cp '2' + jr nz,rck1 + call kgettc + jr c,retf + cp SOH + jr z,recpl1 + ld e,a + ld hl,(chksum) + add hl,hl + add hl,hl + ld a,h + and 0x3f + add a,' ' + xor e + and 0x7f + jr nz,csmerr +rck1: call kgettc + jr c,retf + cp SOH + jr z,recpl1 + ld e,a + ld a,(chksum) + and 0x3f + add a,' ' + xor e + and 0x7f + jr z,cksmok +csmerr: call dcheck +retf1: ld b,0 +adjsiz: ld hl,(cspsiz) + ld a,h + or a + ld de,-16 + jr nz,decsiz + ld a,l + cp MINPSIZ + 5 + jr c,nodec + ld e,-4 +decsiz: add hl,de + ld (cspsiz),hl +nodec: ld a,b + ret +retf: call dtime + jr retf1 +cksmok: call flsrng + ld a,b + cp 'Y' + jr nz,adjsiz + ld de,(cspsiz) + ld hl,(spsiz) + sbc hl,de + ret z + inc de + ld (cspsiz),de + ret + +rxthdr: call cinchr + ret c + cp SOH + ret z + sub ' ' + ld e,a + call cinchr + ret c + cp SOH + ret z + sub ' ' + call mul95 + ex de,hl + ld hl,hdchk + call finchk + push af + call cinchr + sub ' ' + pop hl + cp h + jr nz,rxerr + xor a + inc a + ret +rxerr: pop hl + jr csmerr + +cinchr: push de + push hl + call kgettc + push af + call kcrcch + pop af + pop hl + pop de + ret + +cputcs: add a,' ' +cputc: push de + push bc + push af + call modopc + pop af + pop bc + pop de + +kcrcch: push bc + ld c,a + ld hl,hdchk + add a,(hl) + ld (hl),a + ld a,(cktyp) + cp '3' + jr z,kcrc3 + ld hl,(chksum) + ld b,0 + add hl,bc + ld (chksum),hl + pop bc + ret + +kcrc3: push de + call dou1 + ld a,c + rrca + rrca + rrca + rrca + ld c,a + call dou1 + pop de + pop bc + ret + +dou1: ld hl,chksum + 1 + xor a + rrd + dec hl + rrd + xor c + and 0x0f + add a,a + push hl + ld l,a + ld h,0 + ld de,kctab + add hl,de + ex de,hl + pop hl + ld a,(de) + inc de + xor (hl) + ld (hl),a + inc hl + ld a,(de) + xor (hl) + ld (hl),a + ret + +fincrc: ld a,(cktyp) + cp '1' + ret nz + ld hl,chksum +finchk: ld a,(hl) + rlca + rlca + and 3 + add a,(hl) + and 0x3f + ld (hl),a + ret + +.extern bufill +bufill: ld bc,0 + ld hl,packet +bufilp: call getpc + jr nc,nopack + cp EOF + jr nz,noteof + ld a,b + or c + ret nz + dec a + ret +noteof: ld a,(pack) + ld (hl),a + inc hl + inc bc + call getpc + ld (hl),a + inc hl + inc bc + call getpc +nopack: ld e,a + ld a,(use8) + or a + jr nz,nohib + ld a,(hibit) + or a + jr z,nohib1 + bit 7,e + jr z,nohib + ld (hl),a + inc hl + inc bc +nohib1: res 7,e +nohib: ld a,e + and 0x7f + cp ' ' + jr c,xorqt + cp DEL + jr z,xorqt + ld a,(quote) + cp e + jr z,doquot + ld a,(hibit) + cp e + jr z,doquot + ld a,(pack) + cp e + jr z,doquot + jr noquot +xorqt: ld a,e + xor 64 + ld e,a +doquot: ld a,(quote) + ld (hl),a + inc hl + inc bc +noquot: ld (hl),e + inc hl + inc bc + push hl + ld hl,(cspsiz) + xor a + sbc hl,bc + ex de,hl + pop hl + or d + jr nz,bufilp + ld a,e + cp 6 + jr nc,bufilp + ld a,(status) + dec a + jr z,bufilp + dec a + jr z,bufilp + ret + +.extern getpc +getpc: + push hl + push bc + ld a,(pack) + or a + jr nz,canpak + call kgw2 + jr poprt1 +canpak: ld hl,status + ld a,(hl) + or a + jr nz,stsnz + ld (hl),3 + ld de,data + ld b,6 +fildat: push de + push bc + call kgw2 + pop bc + pop de + ld (de),a + inc de + sbc a,a + ld (de),a + inc de + djnz fildat +stsnz: ld hl,status + ld a,(hl) + cp 3 + jr z,stsno3 + dec (hl) +reteof: add a,a + ld l,a + ld h,0 + ld de,data + 1 + add hl,de + ld a,(hl) + add a,a + dec hl + ld a,(hl) +poprt1: jr popret +stsno3: ld hl,(data) + ld a,h + rlca + jr c,popret + ld a,l + ld hl,data + 2 + ld b,5 +ckpk: cp (hl) + jr nz,nopak + inc hl + bit 7,(hl) + jr nz,nopak + inc hl + djnz ckpk + ld a,2 + ld (status),a + ld bc,[MAXPSIZ - 6 << 8] + 6 +packlp: push bc + call kgw2 + pop bc + jr c,packed + ld hl,data + cp (hl) + jr z,nopkef + call gw2pb + jr packed +nopkef: inc c + djnz packlp +packed: ld a,c + add a,' ' + ld (data + 4),a + scf + ld a,PACK + jr popret +nopak: ld de,data + 1 + ld hl,data + 2 + ld bc,10 + ld a,(de) + add a,a + dec de + ld a,(de) + push af + ldir + call kgw2 + ld l,a + sbc a,a + ld h,a + ld (data + 10),hl + pop af +popret: pop bc + pop hl + ret + +kgw2: call getw2c + jr c,kgeof + ld hl,(ktxt) + inc l + dec l + ret nz + cp 0x1a + jr nz,clcra + scf +kgeof: sbc a,a + ret +clcra: or a + ret + +.extern kresgp +kresgp: + xor a + ld (status),a + ret + +.extern bufemp +bufemp: + ld hl,packet +bufmpl: ld a,(hl) + inc hl + or a + ret z + ld bc,0x0100 + ld e,a + ld a,(pack) + or a + jr z,nobepk + cp e + jr nz,nobepk + ld a,(hl) + inc hl + sub ' ' + ld b,a + ld e,(hl) + inc hl +nobepk: ld d,0xff + ld a,(use8) + or a + jr nz,nobehb + ld d,0x7f + ld a,(hibit) + cp e + jr nz,nobehb + ld c,0x80 + ld e,(hl) + inc hl +nobehb: ld a,e + and d + cp MYQUOTE + jr nz,nobeqt + ld a,(hl) + and d + ld e,a + and 0x7f + ld d,a + inc hl + cp MYQUOTE + ld a,e + jr z,nobeqt + ld a,(hibit) + cp d + ld a,e + jr z,nobeqt + ld a,(pack) + cp d + ld a,e + jr z,nobeqt + xor 64 +nobeqt: or c + push hl +oplp: push af + push bc + call putw2c + pop bc + pop af + djnz oplp + pop hl + jr bufmpl +.dseg +mydata: db MAXPSIZ + ' ' + db MYTIME + ' ' + db MYPAD + ' ' + db MYPCHAR ^ 64 + db MYEOL + ' ' + db MYQUOTE +hba: db MYHIBIT +cka: db '3' +pka: db MYPACK +capas: db '"' ; just long packets +windo: db ' ' ; no windowing +maxl1: db ' ' + [1015 / 95] ; packet length == 1015 +maxl2: db ' ' + [1015 % 95] ; packet length == 1015 +emdat: +.cseg + +.extern spar +spar: + ld de,packet + ld hl,mydata + ld bc,{emdat - mydata} + ldir + ld a,(ksflg) ; receive mode? + or a + jr z,sparr ; yes - handle spar stuff differently + ld a,(use8) + or a + ld a,'N' + jr z,sps7 +setss6: ld (packet + {hba - mydata}),a +sps7: ld a,(try2) + or a + jr z,splp + ld hl,packet + {cka - mydata} + dec (hl) + jr splp +sparr: ld a,(hibit) + or a + ld hl,use8 + ld de,hispfx + jr nz,useh + ld a,(de) + xor 'Y' + or (hl) + ld a,'N' + jr nz,setd6 +useh: ld (hl),0 + ex de,hl + ld a,'Y' + cp (hl) + jr nz,setd6 + ld a,MYHIBIT + ld (hibit),a +setd6: ld (packet + {hba - mydata}),a + + ld hl,cktypq + ld a,(hl) + and 0xfe + cp '2' + jr z,ckok + ld (hl),'1' +ckok: ld a,(hl) + ld (packet + {cka - mydata}),a + + ld a,(pack) + or a + jr nz,ispack + ld a,' ' +ispack: ld (packet + {pka - mydata}),a + +splp: ld a,(longpk) + or a + ret nz + ld hl,[' ' << 8] + ' ' + ld (packet + {capas - mydata}),hl + ld (packet + {capas - mydata} + 2),hl + ret + +.extern rpar +rpar: + ld hl,packet + push hl + ld b,16 +fillit: ld a,(hl) + or a + jr nz,filnxt + ld (hl),' ' + inc hl + ld (hl),0 + dec hl +filnxt: inc hl + djnz fillit + pop hl + ld a,(hl) + sub ' ' + cp MAXPSIZ + jr c,psizok + ld a,MAXPSIZ +psizok: ld (spsiz),a + ld (cspsiz),a + inc hl + ld a,(hl) + sub ' ' + cp MINTIM + jr nc,timok1 + ld a,MINTIM +timok1: cp MAXTIM + jr c,timok2 + ld a,MAXTIM +timok2: ld (timint),a + inc hl + ld a,(hl) + sub ' ' + ld (pad),a + inc hl + ld a,(hl) + xor 64 + ld (padchr),a + inc hl + ld a,(hl) + sub ' ' + or a + jr nz,eolok + ld a,MYEOL +eolok: ld (eol),a + inc hl + ld a,(hl) + call validq + jr nc,quotok + ld a,MYQUOTE +quotok: ld (quote),a + inc hl + ld a,(hl) + cp ' ' + ld (hispfx),a + jr z,clru8 + call validq + jr c,notvq + ld a,(ksflg) + or a + jr z,usehis + ld a,(use8) + or a + jr nz,clru8 + ld a,(hispfx) + cp MYHIBIT + jr nz,clru8 +usehis: ld a,(hispfx) + jr sethib +notvq: ld a,(hispfx) + cp 'Y' + jr nz,rpr7 + ld a,(use8) + or a + jr nz,rpr7 +sethmy: ld a,MYHIBIT +sethib: ld (hibit),a +clru8: xor a + ld (use8),a +rpr7: inc hl + ld a,(ksflg) + or a + ld a,(hl) + jr nz,rpr7s + cp '2' + jr z,setct + cp '3' + jr z,setct + ld a,'1' + jr setct +rpr7s: ld a,(try2) + or a + ld a,'2' + jr nz,ckrtyp + inc a +ckrtyp: cp (hl) + jr z,setct + ld a,'1' +setct: ld (cktypq),a +rpr8: inc hl + ld a,(ksflg) + or a + ld a,(hl) + jr nz,rpr8s + call validq + jr c,rpr9 + jr rp8sp +rpr8s: cp MYPACK + jr nz,rpr9 +rp8sp: ld (pack),a +rpr9: ld a,(longpk) + or a + ret z + inc hl + ld a,(hl) + and 2 + ret z +skipcp: ld a,(hl) + inc hl + rrca + jr c,skipcp + inc hl + ld a,(hl) + sub ' ' + ld e,a + inc hl + ld a,(hl) + sub ' ' + call mul95 + ld a,h + and ~ [1023 / 256] + jr z,rpszok + ld hl,1023 +rpszok: ld a,h + or a + jr z,cksml +sub10: ld de,-10 ; trim off some size for breathing room + add hl,de + jr spsz +cksml: ld a,l + cp MAXPSIZ + 1 + jr nc,sub10 + or a + ld hl,500 + jr z,spsz + ld hl,MINPSIZ + cp l + jr c,spsz + ld l,a +spsz: ld (spsiz),hl + ld (cspsiz),hl + ret + +validq: cp '!' + ret c + cp '<' + 1 + ccf + ret nc + cp '`' + ret c + cp '~' + 1 + ccf + ret + +mul95: ld l,e + ld d,0 + ld h,d + add hl,hl ; * 2 + add hl,de ; * 3 + add hl,hl ; * 6 + add hl,hl ; * 12 + add hl,hl ; * 24 + add hl,hl ; * 48 + add hl,hl ; * 96 + sbc hl,de ; * 95 + ld e,a + add hl,de ; + a + ret + +.extern kinit +kinit: + ld a,MYEOL + ld (eol),a + ld a,MYQUOTE + ld (quote),a + ld a,MYTIME + ld (timint),a + ld a,'1' + ld (cktyp),a + call clrflg + +kflp: ld a,(hl) + or 0x20 + cp 0x20 + jr z,donekf + cp 'b' + jr nz,nokfb + ld (ktxt),a + jr kflpi +nokfb: cp 'g' + jr nz,nokfg + ld (getflg),a + jr kflpi +nokfg: cp '8' + jr nz,nokf8 + ld (use8),a + jr kflpi +nokf8: cp '2' + jr nz,nokf2 + ld (try2),a + jr kflpi +nokf2: cp 'q' + jr nz,nokfq + ex de,hl + ld hl,qcount + inc (hl) + ex de,hl + jr kflpi +nokfq: cp 'a' + jr nz,nokfa + ld (beep),a + jr kflpi +nokfa: cp 'l' + jr z,savser + cp 'f' + jr nz,nokff +savser: ld (sercmd),a + jr kflpi +nokff: cp 'x' + jr nz,kflpi + ld (longpk),a +kflpi: inc hl + jr kflp +donekf: ld a,(getflg) + or a + jr z,noget + call byp + or a + jr nz,savget + call ilprt + db 'Need filename for get option\0' + jp jexit +savget: ld (getnam),hl +noget: ld hl,kabort + ld (abortf),hl + ld hl,kstr + jp initsc +.dseg +kstr: db 'Kermit\0' +.cseg + +.extern kerror +kerror: + ld a,e + ld de,(nn) + ld d,a + ld a,'E' + push hl + call spack + call mtprt + dw [14 << 8] + 10 + db 'Local error: \0' +phlpsl: pop hl + jp prtslp + +.extern prerrp +prerrp: + ld hl,packet + push hl + call mtprt + dw [14 << 8] + 10 + db 'Error from remote: \0' + jr phlpsl + +.extern scnfil +scnfil: + ld a,(de) + inc de + and 0x7f + cp ' ' + jr z,isspc + ld (hl),a + inc hl +isspc: djnz scnfil + ret + +.extern dbadp +dbadp: call diag + db 'Invalid packet type\0' + ret + +kabort: call gettc3 + jr nc,kabort ; let the line cool down + ld a,(ksflg) + ld de,(nn) + or a + jr z,nnok + inc e +nnok: ld d,0 + ld a,'A' + call spack + +.extern dabort +dabort: call diag + db 'Transfer aborted\0' +jexit: call crlf + ld sp,(ressp) + ret + +.extern exprp +exprp: + ld a,(pack) + cp (hl) + jr z,gotexp + ld a,(hl) + ldi + or a + jr nz,exprp + ret +gotexp: inc hl + ld a,(hl) + sub ' ' + ld b,a + inc hl + ld a,(hl) + inc hl +expfl: ld (de),a + inc de + djnz expfl + jr exprp + +.extern clrflg +clrflg: + push hl + ld hl,fill + ld de,fill + 1 + ld bc,{endfill - fill} - 1 + ld (hl),b + ldir + pop hl + ret + +.useg + +fill: +.extern mdm7b +mdm7b: ; modem 7 batch enabled +.extern hibit +hibit: ds 1 ; high bit quote char, or zero if none +.extern pack +pack: ds 1 ; repeat char pack flag, or zero if none +.extern longpk +longpk: ds 1 ; long packets requested +.extern pad +pad: ds 1 ; number of pad chars needed +.extern errors +errors: ; xmodem error count +.extern padchr +padchr: ds 1 ; what sort of padding needed +.extern crcerc +crcerc: +ktxt: ds 1 ; text mode (how do we do end of file?) +.extern crcmod +crcmod: +use8: ds 1 ; are we actually doing 8 bit transfers +.extern beep +beep: ds 1 ; beep when done +.extern ymdmb +ymdmb: ; y modem batch transfer +try2: ds 1 ; type 2 checksum requested +.extern qcount +qcount: ds 1 ; quiet value + + +spsiz: ds 2 ; max send packet size +cspsiz: ds 2 ; current send packet size +.extern getflg +getflg: ds 1 ; server get requested +.extern getnam +getnam: ds 1 ; string for names to get - overlays sercmd +.extern sercmd +sercmd: ds 1 + +endfill: + +.extern ressp +ressp: ds 2 +.extern size +size: ds 2 +.extern nn +nn: ds 1 +.extern timint +timint: ds 1 +.extern numtry +numtry: ds 1 +.extern oldtry +oldtry: ds 1 +.extern state +state: ds 1 +.extern eol +eol: ds 1 +.extern quote +quote: ds 1 +hispfx: ds 1 ; char he sent in prefix position +.extern cktyp +cktyp: ds 1 ; checksum type we're actually using +.extern cktypq +cktypq: ds 1 ; checksum type proposed +status: ds 1 +data: ds 12 +.extern ksflg +ksflg: ds 1 +hdchk: ds 1 ; checksum used for header + +.dseg +kctab: + dw 0x0000, 0x1081 + dw 0x2102, 0x3183 + dw 0x4204, 0x5285 + dw 0x6306, 0x7387 + dw 0x8408, 0x9489 + dw 0xa50a, 0xb58b + dw 0xc60c, 0xd68d + dw 0xe70e, 0xf78f + \ No newline at end of file diff --git a/source/MAKEQT.SUB b/source/MAKEQT.SUB new file mode 100644 index 0000000..8332f97 --- /dev/null +++ b/source/MAKEQT.SUB @@ -0,0 +1,10 @@ +xsub +zc +c:qterm.z c:termio.z c:vt100.z c:chat.z c:catch.z c:baud.z +c:dir.z c:flags.z c:file.z c:odds.z c:cf.z c:send.z c:recv.z +c:srutil.z c:sendk.z c:recvk.z c:kutil.z c:sendx.z c:recvx.z +c:xutil.z c:srscrn.z +c:shrink.z +-x -l -s -be + + \ No newline at end of file diff --git a/source/ODDS.Z b/source/ODDS.Z new file mode 100644 index 0000000..7170395 --- /dev/null +++ b/source/ODDS.Z @@ -0,0 +1,658 @@ +; odds.z - code that has no place else to live + +.incl "c:termcap" +.incl "c:vars" + +.var slow 86 ; should we slow down outgoing string + +.extern prompt ; print an inline prompt then get a buffer +prompt: ; full of data from the keyboard + ld a,(prmpfl) ; see if we're in a chat script or not + or a + jr z,nochat ; no - do normal input + push ix + pop hl ; get string address to hl + ld de,ipbuf + ld bc,116 ; reasonable amount to copy + ldir ; move it over, and we're done +skipze: pop hl ; get string address - we have to skip over it +skiplp: inc hl ; this relies on a non-empty string + ld a,(hl) + or a + jr nz,skiplp ; skip till we reach the end + jp (hl) ; and return to address in hl +nochat: call dim ; set dim mode + ld hl,crlfms + call prtslp ; send a newline + pop hl + call prtslp ; print the prompt + push hl + ld de,ipbuf - 2 ; point at where we'll read info + ld a,e + ld (de),a ; set count + ld c,buffin + call bdos ; read a line + ld hl,ipbuf - 1 + ld e,(hl) ; get count actually input + ld d,h + add hl,de + inc hl ; turn into a buffer pointer + ld (hl),d ; and zero terminate input data + +.extern crlf ; just print a newline +crlf: + call ilprt +crlfms: db '\r\n' +zero: db 0 + ret + +.extern ilprt +ilprt: call dim ; in line print - start by going dim + pop hl ; get string address + call prtslp ; out it goes + push hl ; restore return address - fall into bright + +.extern bright +bright: ld a,(tcbits) + and b_brite ; do we have a bright string? + ret z + ld hl,bflag ; currently bright? + ld a,(hl) + dec a + ret nz ; yes, don't resend + ld (hl),a + ld hl,brites ; get address of bright string + jr prtslx ; and send it + +.extern dim +dim: ld a,(tcbits) + and b_dim ; do we have a dim string? + ret z + ld hl,bflag ; currently dim? + ld a,(hl) + or a + ret nz ; yes, don't resend + inc (hl) + ld hl,dims ; get address of dim string + jr prtslx + +.extern dellin +dellin: + ld hl,dlstr ; get address of dellin string + jr prtslx ; and send it + +.extern inslin +inslin: + ld hl,ilstr ; get address of inslin string + jr prtslx ; and send it + +.extern cleol +cleol: + ld hl,ceol ; get address of cleol string + jr prtslx ; and send it + +.extern cleos +cleos: + ld hl,ceos ; get address of cleos string + jr prtslx ; and send it + +.extern clear +clear: ld hl,clrs ; get address of clear string + +prtslx: ld a,h + ld (optype),a + call prtslp + xor a + ld (optype),a + ret + +.extern prtslp +prtslp: + call clrpcp + +.extern prtsl1 +prtsl1: + ld a,(hl) + or a + ret z + inc hl + push hl + call tabexp + pop hl +; for ^X to can a long directory, add a 'ret c' here + jr prtsl1 + +.extern tabexp +tabexp: + cp '\n' + jr nz,nonl + ld a,79 + ld (pos),a + ld a,'\n' +nonl: cp '\r' + jr nz,nocr + ld a,0xff + ld (pos),a + ld a,'\r' +nocr: cp '\t' + jr z,tablp + call prtopc + jr chk80 +tablp: ld a,' ' + call prtopc + ld a,(pos) + and 7 + jr nz,tablp +chk80: ld hl,pos + ld a,(hl) + xor 80 + call z,newlin + ret + +prtopc: ld c,a + ld a,(optype) + or a + ld a,c + jp nz,scrout + ld hl,mode + ld e,(hl) + push hl + push de + ld (hl),lf_bit + call opchar + pop de + pop hl + ld (hl),e + ld hl,pos + inc (hl) + ret + +newlin: ld (hl),a ; set char count back to zero + inc hl ; point at line count + inc (hl) ; bump that too + ld a,(hl) + xor 23 ; 23 lines? + ret nz ; return if not + ld a,(pagerf) + or a + ret z ; and return if not paging + dec (hl) ; set back to 22 in case we get a return + ld hl,mmsg +.dseg +mmsg: db '[more]\0' +.cseg + call prtsl1 ; print more message + call kbdin ; get a character + ld hl,cmstr +.dseg +cmstr: db '\r \r\0' +.cseg + cp (hl) ; return? + jr z,clrmor ; yes - just remove '[more]', linec is set + xor 'x' & 0x1f + jr z,retcx ; special exit on a ^X + xor a + ld (linec),a ; else clear linc for a full page +clrmor: jp prtsl1 ; and remove the more message + +retcx: call prtsl1 ; come here after a ^X - we remove the message + scf ; and return carry to flag the exit + ret + +.extern clrpsp +clrpsp: + db 0x3e ; makes a ld a,xx + +.extern clrpcp +clrpcp: + xor a + ld (pagerf),a + push hl + ld hl,0 + ld (pos),hl + pop hl + ret + +.extern mtprt ; a call to this is followed by inline coords. +mtprt: ; and a string to be printed at the coords + pop hl + ld e,(hl) + inc hl + ld d,(hl) ; get moveto coordinates + inc hl + push hl ; save address of string + ex de,hl ; coords to hl + call moveto ; move to wherever + pop hl ; restore string to hl + call prtslp ; print it + jp (hl) ; and return + +.extern decob ; print number in l, by zeroing h +decob: + ld h,0 ; clear h and fall through + +.extern decout ; print number in hl in decimal +decout: + push de ; save running total + push bc ; save char count + ld bc,-10 + ld de,-1 +decou2: add hl,bc + inc de + jr c,decou2 ; divide hl by 10 + sbc hl,bc ; reset because of overflow + ex de,hl ; remainder to e, answer to hl + ld a,h + or l + pop bc ; restore char count for next call + call nz,decout ; answer non-zero: print recursively + ld a,e ; get remainder + add a,'0' + ld c,a + push bc + call scrout ; print to screen as a digit + pop bc + dec b ; keep count of printed chars in b + pop de ; restore total + ret + +.extern break +break: + call tenth ; wait a while before beginning + call sbreak ; start the break + call tenth3 ; idle for 3/10 of a second + jp ebreak ; finish off by ending the break + +.extern hangup +hangup: + ld hl,#dtroff + ld a,(hl) + cp 0xc9 ; return at dtroff => no code + jr z,strhup ; so send a string instead + call dtroff ; drop dtr - should make modem hang up + call tenth3 + call tenth3 ; wait a (long) while + jp dtron ; dtr back on +strhup: ld ix,hangup - slow + inc hl + ld b,(hl) ; get the string length + inc hl + jp sendcs ; and out it goes + +tenth3: call tenth + call tenth + +.extern tenth ; delay for 1/10 of a second, but continue to +tenth: ; save incoming chars + ld de,100 + call setspd ; get speed constant to hl +tnthlp: ld b,60 +tnthl1: djnz tnthl1 ; idle a while + push hl + call savrng ; check incoming chars, but only as far as + pop hl ; the ring buffer + dec hl ; count main timer + ld a,h + or l + jr nz,tnthlp + ret + +.extern tnthip ; delay for 1/10 of a second, but continue to +tnthip: ; check the modem for incoming chars + ld de,100 + +.extern msip ; delay for de msec, but continue to +msip: ; check the modem for incoming chars + ld a,d + or e ; anything in de? + ret z ; nope - return right now + call setspd ; get speed constant to hl +msil: ld b,60 +msl2: djnz msl2 ; idle a while + push hl + call lstmod ; process incoming chars and printer output + pop hl + dec hl ; count main timer + ld a,h + or l + jr nz,msil + ret + +.extern setspd ; take 1MHz value in de, get correct value in +setspd: ; hl based on cpu speed + ld hl,0 + ld a,(speed) + ld b,a ; get cpu speed to b +spdok: add hl,de ; multiply de by b + djnz spdok + ret + +.extern help +help: + call crlf ; start off with a new line + call dim ; and dim + call presc ; print the escape character + or a ; test the flag + ld c,' ' + call z,scrout ; print a space if no '^' + call ilprt ; and finish off + db ' - Transmit escape character\t ' + db '? - Print this help\r\n' + db '. - Send a break\t\t\t' + db ', - Hang up modem\r\n' + db 'B - Change baud rate and mode\t\t' + db 'C - Open an input catch file\r\n' + db 'D - Display local directory\t\t' + db 'E - Toggle remote echo\r\n' + db 'H - Toggle half duplex\t\t' + db 'I - Print settings information\r\n' + db 'J - Junk control characters\t\t' + db 'K - Load a function key\r\n' + db 'L - Toggle linefeed transmit for \'P\'\t' + db 'M - Toggle bit 7 mask\r\n' + db 'N - Select new default drive / user\t' + db 'O - Toggle output to printer\r\n' + db 'P - Print a file to remote\t\t' + db 'Q - Quit\r\n' + db 'R - Receive a file using protocol\t' + db 'S - Send a file using protocol\r\n' + db 'T - Type a local file\t\t\t' + db 'U - Invoke user function\r\n' + db 'V - Toggle VT100 Emulation\t\t' + db 'W - Toggle split window mode\r\n' + db 'X - Activate a chat script\t\t' + db 'Y - Put catch file on hold\r\n' + db 'Z - Close catch file\t\t\t' + db '0-9 - Send function key string\r\n' + db 0 + ret + +.extern info ; print current settings +info: + call ilprt + db '\r\nEscape character:\t\0' + call dim + call presc ; print the escape character + call ilprt + db '\r\nCurrent drive / user:\t\0' + call dim + ld hl,(curusr) ; get the current drive & user + ld a,h + add a,'A' ; convert drive to a letter + ld c,a + push hl + call scrout ; print the drive + pop hl + call decob ; and print the user in l + call ilprt + db ':\r\n\0' + ld a,(rate) + cp ' ' + jr z,norate + call ilprt + db 'Baud rate:\t\t\0' + call dim + ld hl,rate + call prtslp +norate: ld a,(modsav) + cp ' ' + jr z,nomode + call ilprt + db 'Communication Mode:\t\0' + call dim + ld hl,modsav + call prtslp +nomode: call ilprt + db '^S delay:\t\t\0' + call dim + ld hl,(csdely) ; get the delay count + call decob ; out it goes + call ilprt + db ' second(s)\r\nHalf duplex:\t\t\0' + ld a,(hflg) + call onoff ; say whether HDX is on or off + call ilprt + db 'Local echo:\t\t\0' + ld a,(eflg) + call onoff ; say what local echo is up to + call ilprt + db 'Control char. discard:\t\0' + ld a,(jflg) + call onoff ; say what local echo is up to + call ilprt + db 'Linefeed in \'P\':\t\0' + ld a,(lflg) + call onoff ; line feed in print + call ilprt + db 'Character delay:\t\0' + call dim + ld hl,(chd) ; get the character delay + call decob ; out it goes + call ilprt + db ' msec. \0' + ld a,(ecflg) + or a + jr z,noeci + call ilprt + db '\techo check\0' +noeci: call ilprt + db '\r\nNew line delay:\t\t\0' + call dim + ld hl,(nld) ; new line delay is a word + call decout + call ilprt + db ' msec. \0' + ld a,(lfecho) + or a + jr z,nolfe + push af + call ilprt + db '\tnewline echo: \'\0' + call dim + pop af + ld c,a + call scrout + ld c,'\'' + call scrout +nolfe: call ilprt + db '\r\nBit 7 masking:\t\t\0' + ld a,(bmask) + inc a + call onoff ; say what bit 7 masking is up to + call ilprt + db 'VT100 emulation:\t\0' + ld a,(vflg) + call onoff ; and vt100 emulation + call ilprt + db 'Printer output:\t\t\0' + ld a,(oflg) + call onoff ; and printer output + ld a,(cflg) + or a + jr nz,isctch ; catch file active? + call ilprt ; if not then say so + db 'Not catching\r\n\0' + ret +isctch: call ilprt + db 'Current catch file:\t\0' + call dim + ld hl,cfcb + call prtfl ; otherwise print the filename + ld a,(cflg) + dec a + jp z,crlf ; if active we're done + call ilprt ; else say it's on hold + db ' (on hold)\r\n\0' + ret + +onoff: or a ; is the flag active + jr z,proff ; jump if not + call ilprt + db 'on\r\n\0' ; say it's on + ret +proff: call ilprt + db 'off\r\n\0' ; or say it's off + ret + +.extern presc +presc: + ld b,0 + ld a,(escval) ; get the escape character + cp ' ' + jr c,prthat ; it's a control char - print the ^ + cp 0x7f + jr nz,nohat ; also do that for deletes +prthat: ld c,'^' + push af ; save the escape val + call scrout ; print the ^ + pop af + ld b,64 ; flag we printed the ^ + xor b ; and "uncontrollify" the char +nohat: ld c,a + push bc ; save flag + call scrout ; print the char + pop af ; flag back to a + ret + +.extern quit +quit: + call cclose ; close catch file if open + ld hl,23 * 256 ; blh corner of screen + call moveto ; go there + call exit ; call custom unwind code + rst 0 ; exit to cp/m + +.extern conin ; get char from function key or keyboard +conin: + ld a,(fkdely) ; check if we need to delay + or a + call nz,tnthip ; idle a while if we do + ld hl,(fkptr) + ld a,(hl) ; get the next char + inc hl + ld (fkptr),hl ; save revised pointer + ret ; char is in a + +.extern ctls +ctls: + ld a,(lxoff) ; get local xon / xoff status + or a + ld a,(cschr) + call nz,modopc ; send the xoff if needed + ld a,(csdely) + or a + ret z ; if it's zero, return right now + ld b,a ; get the delay value + add a,a + add a,a + add a,b + add a,a ; * 10 + ld b,a ; to b +waitlp: push bc + call tenth ; wait a while + pop bc + djnz waitlp + ret + +.extern gotxon ; flag that an xon was sent from the kbd, +gotxon: ; so we don't re-send + ld (lxoff),a ; set the local xoff flag + ret + +.extern gotxof ; flag that an xof was sent: this just clears +gotxof: ; the flag set in gotxon + ld hl,lxoff + ld (hl),0 + ret + +.extern areabp ; areabp used in chat scripts: point at first +areabp: ; non-white in text of a '!' command + ld hl,area - 1 ; point at chat parse area and fall into byp + +.extern incbyp +incbyp: ; inc hl, and then byp + inc hl + +.extern byp ; step over spaces in string pointed to by hl +byp: + ld a,(hl) + cp ' ' + ret nz ; return on the first char that is NOT a space + inc hl + jr byp + +.extern ldfnk ; this will prompt and then use the string +ldfnk: ; to program one of the 10 function keys + ld hl,zero ; point the readback pointer at a zero + ld (fkptr),hl ; in case we change the string under it + call prompt + db 'Key value: \0' ; get the string + ld hl,ipbuf + call byp ; strip spaces + or a + ret z ; skip if nothing to do + cp '\e' + ret z ; or if an escape typed + ld c,0 ; clear c (will become slow/fast flag) + sub '0' + cp 10 + jr c,fast ; 0-9: is a fast, key index in a + ld c,a ; non-digit: save a non-zero in c + inc hl + ld a,(hl) ; get the next character + sub '0' ; and turn it into an index +fast: cp 10 + jr c,fnkok ; check it's ok + call ilprt ; complain and return if not + db 'Invalid function key number\r\n\0' + ret +fnkok: inc hl ; point at string to be parsed + push hl ; save input string address + add a,a + add a,a + add a,a + add a,a ; turn index into table offset + ld e,a + ld d,0 ; goes in de + ld hl,fktbl + add hl,de ; get table entry address + ld (hl),c ; save speed flag + inc hl + ex de,hl ; string target address to de + pop hl ; text back to hl + ld bc,14 << 8 ; 14 chars, zero byte delimits + jp parst ; go parse the string, and we're done + +.extern cbios ; call the entry point in the bios witn number +cbios: ; in a, saving ix and iy as we go + push ix + push iy + call #bios + pop iy + pop iy + ret + +#bios: ; offset is in a + ld hl,(1) ; get the warm boot value to hl + ld l,a ; modify with function number + jp (hl) ; go do the code + +.dseg +optype: db 0 +bflag: db 0 +.extern lxoff +lxoff: db 1 +.extern prmpfl +prmpfl: db 0 +.extern fkptr +fkptr: dw zero + +.useg +pos: ds 1 +linec: ds 1 +pagerf: ds 1 +.extern fkdely +fkdely: ds 1 +.extern fktbl +fktbl: ds 16 * 10 + \ No newline at end of file diff --git a/source/QT43SRC.LBR b/source/QT43SRC.LBR new file mode 100644 index 0000000000000000000000000000000000000000..c930cf27e23cf9080a27b10d18ab62c517b4e960 GIT binary patch literal 75136 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

gm#; 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 j_@8syxY|1Y_Q!a|;!VK we are +state: dw 0 ; what state are we in? + +string: db ' .....\r\0' ; space string to break lines +retstr: db ' [end]' ; return string to end a paragraph +nlstr: db '\r\0' ; newline to end + +.endif + +.org 0x0400 + +; put this jump here, because decout may move, so we can't have it's address +; in the patch area + +decoj: jp decout + +; same again: prompt may move + +prmpjp: jp prompt + +; now the start of the main code + +start: ld sp,(memtop) ; set the stack + ld a,(biosjp + 1) ; get the bios base address + ld (#kbdsts + 2),a + ld (#kbdin + 2),a + ld (#scrout + 2),a ; fix the three jumps + call #subchk ; but now check for XSUB installed + ld a,(escape) + ld (escval),a ; set the escape character in the jump table + call setshr ; do shrink adjustment + ld a,(xfersz) + ld hl,512 +sizlp: add hl,hl ; set size in K in hl + rra + jr nc,sizlp ; loop till size is set in hl + ld de,work2 + add hl,de + ld (ew2p),hl ; and set pointer for transfers + ld hl,fktbl + ld de,fktbl + 1 + ld bc,16 * 10 - 1 + ld (hl),b + ldir ; clear fk table initially + call entry ; do the users custom code + ld sp,(memtop) ; reload the stack pointer in case things got + ; bashed in the entry hook + call ilprt ; print fixed part of signon + db 'QTERM V' + version + db '\r\n' + db '(C) Copyright DPG ' + year + db ' - All rights reserved\r\n' + db 'Version for \0' + call dim + ld hl,signon + call prtslp ; print the system signon message + call ilprt + db '\r\nEscape character is \0' + call dim + call presc ; remind what the escape character is + call crlf + call crlf ; throw a newline or two + ld a,(#dtroff) + cp 0xc9 + call nz,dtron ; enable dtr + ld c,getdrv ; now, in case we changed drive / user in the + call bdos ; entry hook, we get the current drive / user + ld (curdrv),a + ld (chtdrv),a ; chat drive + ld c,gsuser + ld e,0xff + call bdos ; find out which user + ld (curusr),a ; save it away as well + ld (chtusr),a ; chat user + ld hl,0x80 ; get command tail + ld e,(hl) ; get it's length + ld (hl),' ' ; add a space so that byp will grok it + inc hl + ld d,h ; extend length to de + ex de,hl + add hl,de + ld (hl),d ; stick a zero byte on the end of it + ex de,hl ; base address back to hl + call byp ; was there anything there? + or a + call nz,ichat ; yes - go process it + +; this loop provides terminal mode: it simply loops round polling the kdb & +; modem port - when it finds something, then it starts to get clever + +.extern main +main: ld sp,(memtop) ; since we long jump here occasionally + ld hl,main + push hl ; push "here" so a return gets us back + ld a,lf_bit + ld (mode),a ; reset mode + call lstmod ; keep the printer going and check modem port + ld hl,(fkptr) + ld a,(hl) + or a ; sending a function key? + jr z,nofkey ; nope, go try the keyboard + call conin ; get the key + jr gotcon +nofkey: ld (prmpfl),a + ld a,(more) + rra ; should we get a keyboard character + jr c,skipin ; skip if not + call kbdsts ; keyboard ready? + or a + ret z ; no, loop back + call kbdin ; get the char +skipin: ld b,0 ; set b so a return is a no-op + call #kbmap ; run through the keyboard window + srl b + ret c ; swallowed, start again +gotcon: ld c,a ; save in c + ld a,b ; copy more bit to a + ld (more),a ; and save it + ld de,escstt + ld a,(de) ; what state are we in? + dec a + ld a,c ; get char back + jr z,hadesc ; we've had an esc char, handle next + ld hl,cschr + cp (hl) + call z,gotxof ; note passing xoff's + jr z,noxon + ld hl,lxoff + inc (hl) + dec (hl) + ld hl,escval + jr nz,noxon + cp (hl) + call nz,gotxon ; and xon's +noxon: cp (hl) ; test the char for an escape + jr nz,modopj ; no it's not, send it to the modem + ld hl,(fkptr) + ld a,(hl) ; in the midst of a pfk send? + or a + ld a,(escval) ; get an escape char back into a +modopj: jr z,gotesc ; yes - skip the usual escape + ld a,(wflg) + or a + ld a,c + call nz,limitb ; legal character? + ret c ; nope - exit now +modjp: jp modop + +; gotesc - we just got the escape char in state 0: set state to 1 + +gotesc: ex de,hl + inc (hl) ; set state to 1 + ret + +; esctbl - table of special values for the escape handler + +.dseg +esctbl: dw modop +.extern escval +escval: db escchr + table '?',help + table '.',break + table 0x2c,hangup + table 'B',baud + table 'C',catch + table 'D',dir + table 'E',echo + table 'H',hdxtog + table 'I',info + table 'J',jctog + table 'K',ldfnk + table 'L',lftog + table 'M',msbtog + table 'N',newdsk + table 'O',optog + table 'P',print + table 'Q',quit + table 'R',recv + table 'S',send + table 'T',type + table 'U',#user + table 'V',vttog + table 'W',witog + table 'X',chat + table 'Y',hold + table 'Z',cclose +tblend: +.cseg + +; hadesc - we are in state 1 and a char arrived: process it as appropriate + +hadesc: ex de,hl + dec (hl) ; reset state +nowjp: ld a,c + ld hl,esctbl ; get address of table + ld b,{tblend - esctbl} / 3 + call ucsa ; force it upper case +escscn: ld e,(hl) + inc hl + ld d,(hl) ; get jump address to de + inc hl + cp (hl) ; test for a match + inc hl + push de + ret z ; got it: go and process + pop de + djnz escscn ; loop back & try again +nowpop: ld a,c ; get char from c (again) + sub '0' + cp 10 ; check for '0' thru '9' + ret nc ; didn't find it - give up + add a,a + add a,a + add a,a + add a,a ; * 16 gives table offset + ld l,a + ld h,0 + ld de,fktbl + add hl,de ; index into table + ld a,(hl) ; pick up + ld (fkdely),a ; and save delay flag + inc hl ; point at main string + ld (fkptr),hl ; save string address + ret + +.dseg +escstt: db 0 ; state of escape detection +more: db 0 ; keyboard window code + +; V4.1e rev +; +; we now save ix and iy through all patch and bios calls: these entries +; are used, and do all the work needed + +.cseg + +.macro access entry +.extern entry +entry: + push ix + push iy + call #`entry + pop iy + pop ix + ret +.endm + + access kbdsts + access scrout + access modist + access modin + access modost + access modout + access sbreak + access ebreak + access dtroff + access dtron + access setbd + access setmod + access moveto + +.extern kbdcc ; get a keyboard character if it's waiting +kbdcc: + call kbdsts + or a + ret z ; return 0 if nothing + + access kbdin + +.var #bdos 5 + + +.extern usrbds ; call bdos, but with a user switch +usrbds: + push bc ; save the function code + ld a,(de) ; get the user number to switch to + inc de ; point de at bdos's idea of the fcb + push de ; save the address + ld e,a + ld c,gsuser + call bdos ; set the user number + pop de ; restore the fcb address + pop bc ; and function number + +.extern bdos +bdos: + push ix + push iy + call #bdos + pop iy + pop ix + ret + +.extern #subchk +#subchk: + ld hl,(1) + ld a,l + ld de,3 + xor e + jr nz,chngit + ld l,a + ld b,6 +jplp: ld a,(hl) + cp 0xc3 + jr nz,chngit + add hl,de + djnz jplp + ret +chngit: ld hl,jptab + ld de,0x0103 + ld bc,9 + ldir + ret + +jptab: jp locst + jp locin + jp locout + +locst: ld hl,peekc + ld a,(hl) +.dseg +peekc: db 0 +.cseg + or a + ret nz + ld c,6 + ld e,-1 + push hl + call 5 + pop hl + ld (hl),a + or a + ret + +locin: call locst + jr z,locin + ld (hl),0 + ret + +locout: res 7,c + ld e,c + ld c,6 + jp 5 + \ No newline at end of file diff --git a/source/RECV.Z b/source/RECV.Z new file mode 100644 index 0000000..bcf7aea --- /dev/null +++ b/source/RECV.Z @@ -0,0 +1,77 @@ +; recv.z - protocol receive code for qterm + +.incl "c:vars" + +.extern recv +recv: + call pmode + jr z,modok + cp 'x' + jp nz,moderr ; k & x are the only legal modes (for now) +modok: inc hl + push hl ; save pointer + push af ; and mode character + call unbyp ; step over flags + call scnfcb ; parse name (or drive only for batch) + ld hl,(fcb) + ld (newusr),hl ; save receive drive and user + pop bc + pop hl + jp z,nowcp + push bc + push hl + call reset ; do a reset + pop hl + pop bc + ld a,'k' + cp b ; Kermit or Xmodem + jr nz,recvx + call recvk ; receive kermit + jr xferd ; and unwrap +recvx: call xmflgs ; parse xmodem flags + call initx + call initrc ; set up screen +dorecv: call xrfile ; receive a file + or a + jr nz,dorecv ; loop if more to come + ld a,(errorf) + or a ; flag if there was an error or not + +.extern xferd ; finish transfer - print completion message +xferd: + push af ; save z flag + ld a,(beep) + or a ; is a beep wanted? + call nz,ilprt ; go beep ONLY IF ASKED! + db 7,0 ; this ONLY works because 7 is a single byte + ; instruction: rlca. + call mtprt + dw [16 << 8] + 10 + db 'Transfer \0' ; start printout + pop af + ld hl,cplmsg ; assume all is well + jr z,pxfmsg ; z => no error: go tell about it + ld hl,ermsg ; else set up an error message +pxfmsg: jp prtslp + +.extern pmode +pmode: + pop hl + ld (ressp),sp + push hl + call prompt + db 'Mode? \0' ; how are we receiving? + ld hl,ipbuf + call byp ; see what first char is + or a ; see if anything at all + pop de + ret z ; return to next lower level if not + or 0x20 ; lower case + push de + cp 'k' ; check the 'k' of kermit + ret + +.dseg +cplmsg: db 'complete\r\n\n\0' +ermsg: db 'error\r\n\n\0' + \ No newline at end of file diff --git a/source/RECVK.Z b/source/RECVK.Z new file mode 100644 index 0000000..22e72a2 --- /dev/null +++ b/source/RECVK.Z @@ -0,0 +1,259 @@ +; recvk.z - code to receive kermit protocol transfers + +.incl "c:vars" +.incl "c:kermit" + +.extern recvk +recvk: + call kinit + call initrc + xor a + ld (nn),a + ld (numtry),a + ld (ksflg),a + ld a,(getflg) + or a + call nz,sndget + ld a,'R' +recvlp: ld (state),a + cp 'R' + jr nz,norr + call rinit + jr recvlp +norr: cp 'F' + jr nz,norf + call rfile + jr recvlp +norf: cp 'D' + jr nz,nord + call rdata + jr recvlp +nord: xor 'C' + ret + +rinit: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 + jr nc,reta + call rpack + cp 'S' + jr nz,noris + ld a,(nn) + cp c + jr nz,retan + call rpar + call spar + call pktok + ld a,'Y' + ld bc,(nn) + ld de,13 + ld b,d + call spack + ld hl,numtry + ld a,(hl) + ld (oldtry),a + xor a + ld (hl),a + ld (getflg),a + ld hl,nn + inc (hl) + res 6,(hl) + ld a,(cktypq) + ld (cktyp),a + ld a,'F' + ret +noris: or a + jr nz,norif + call dtime + ld a,(getflg) + or a + jr z,rinak + call sndget + jr rierr +rinak: ld bc,(nn) + ld de,0 + ld b,e + ld a,'N' +sretst: call spack +rierr: call pkterr + ld a,(state) + ret +norif: cp 'E' + jr nz,retab + call prerrp + jr reta +retan: call dnum + jr reta +retab: call dbadp + jr reta +retar: call dtries +reta: ld a,'A' + ret + +rfile: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 + jr nc,reta + call rpack + cp 'S' + jr nz,norfs + ld hl,oldtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 + jr nc,retar + ld a,(nn) + dec a + and 63 + cp c + jr nz,retan + push de + call spar + pop de + ld de,13 + ld b,d +cntsrs: xor a + ld (numtry),a + ld a,'Y' + jr sretst +norfs: cp 'Z' + jr nz,norfz + ld hl,oldtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 +retanc: jr nc,retar + ld a,(nn) + dec a + and 63 + xor c + jr nz,retan + ld e,a + ld d,a + ld b,a + jr cntsrs +norfz: cp 'F' + jr nz,norff + ld a,(nn) + cp c +retanz: jr nz,retan + ld hl,packet + ld de,packet + 500 + ld a,(pack) + or a + jr z,nopkfn + call exprp + ld hl,packet + 500 +nopkfn: call cpmaux + xor a + ld (ymdmb),a + call auxopn + ld de,-1 + ld hl,auxfcb + call prfile +pokd: call pktok + ld a,'Y' + ld bc,(nn) + ld de,0 + ld b,e + call spack + ld hl,numtry + ld a,(hl) + ld (oldtry),a + ld (hl),0 + ld hl,nn + inc (hl) + res 6,(hl) + ld a,'D' + ret +norff: cp 'B' +noris1: jp nz,noris + ld a,(nn) + xor c +retnz1: jr nz,retanz + call pktok + ld a,'Y' + ld bc,(nn) + ld de,0 + ld b,e + call spack + ld a,'C' + ret + +rdata: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 +retnc1: jp nc,retar + call rpack + cp 'D' + jr nz,nordd + ld a,(nn) + cp c + jr z,dnok +ackold: ld hl,oldtry + ld a,(hl) + inc (hl) + cp MAXTRY + 1 + jr nc,retnc1 + ld a,(nn) + dec a + and 63 + xor c +retnz2: jr nz,retnz1 + ld e,a + ld d,a + ld b,a + jp cntsrs +dnok: call bufemp + jp pokd +nordd: cp 'F' + jr z,ackold + cp 'Z' + jr nz,noris1 + ld a,(nn) + cp c + jr nz,retnz2 + call flshw2 + ld de,auxfcb + ld c,close + call usrbds + call pktok + ld hl,nn + ld c,(hl) + inc (hl) + res 6,(hl) + ld de,0 + ld b,e + ld a,'Y' + call spack + ld a,'F' + ret + +sndget: ld bc,(getnam) +ressg: ld hl,packet + ld de,0xff +sgelp: ld a,(bc) + inc bc + ld (hl),a + cp ':' + jr z,ressg + cp MYQUOTE + jr nz,noquot + inc hl + inc e + ld (hl),a +noquot: inc hl + inc e + or a + jr nz,sgelp + ld b,d + ld c,d + ld a,'R' + jp spack + \ No newline at end of file diff --git a/source/RECVX.Z b/source/RECVX.Z new file mode 100644 index 0000000..a7d63b9 --- /dev/null +++ b/source/RECVX.Z @@ -0,0 +1,359 @@ +; recvx.z - receive a file via xmodem protocol + +.incl "c:vars" +.incl "c:xmodem" + +.extern xrfile +xrfile: + xor a + ld (batch),a + ld (ymdmb),a + ld (fatal),a + ld (errors),a + ld (crcerc),a + ld (errorf),a + ld (openfl),a + ld (secip),a + ld (eotc),a + ld h,a + ld l,a + ld (sectnm),hl + ld (sntsec),hl + ld l,128 + ld (bufsiz),hl + call snint + jr mainlp +sndnk1: call flsnak +mainlp: xor a + ld (errorf),a +reget: call gettc3 + ld hl,eotc + jr c,nmoneh + cp EOT + jp z,soheot + ld (hl),0 + cp CAN + jr z,sohcan + cp STX + jr z,sohsh1 + cp SOH +sohsh1: jp z,sohsoh + cp ACK + jr nz,reget +sohack: ld hl,(sntsec) + ld a,h + or l + jr nz,reget + ld (chksum),a + ld de,auxfcb + 2 + ld b,NAMSIZ +acknlp: ld a,4 + call gettcp + jr c,nmonec + cp CAN + jr nz,nonmcn +sohcan: call chkcan + ret z +nmonec: jp ckserr +nmoneh: ld a,(hl) + or a + jp nz,ackeot + jr nmonec +nonmcn: cp EOT + jr nz,nonmet + ld hl,auxfcb + 2 + sbc hl,de + ld a,l + ret z + ld a,BADNAM +nonmet: cp BADNAM + jr z,nmonec +nmsavc: ld (de),a + inc de + ld hl,chksum + add a,(hl) + ld (hl),a + ld a,ACK + push bc + push de + call modopc + pop de + pop bc + djnz acknlp + call gettc3 + cp CTRLZ + jr nz,nmonec + ld hl,chksum + add a,(hl) + call modopc + call gettc3 + cp ACK + jr nz,nmonec + ld hl,(newusr) + ld (auxfcb),hl + ld hl,auxfcb + 13 + ld de,auxfcb + 14 + ld bc,20 + ld (hl),b + ldir + call auxopn + ld (batch),a + ld (openfl),a + call pktok + jp setb +sohsoh: ld hl,128 + cp SOH + jr z,gotbsz + ld hl,1024 +gotbsz: ld (bufsiz),hl + ld hl,(sntsec) + ld a,h + or l + jr nz,noinit + ld (errors),a +noinit: call gettc3 + push af + call gettc3 + pop bc + ld c,a + add a,b + inc a + jp nz,snerr + ld a,(sectnm) + inc a + xor b + jr nz,pnerr + inc a + ld (secip),a + call redsec + jp c,ckerr + call pktok + xor a + ld (errors),a + ld a,(bufsiz + 1) + or a + ld de,1 + jr z,updrsc + ld e,8 +updrsc: ld hl,(sntsec) + add hl,de + ld (sntsec),hl + ld hl,sectnm + inc (hl) + dec hl ; i.e. ld hl,openfl + ld a,(hl) + or a + jr nz,isopen + inc (hl) + ld bc,13 + call xferaz + ld de,auxfcb + 2 + ld a,(de) + cp ' ' + jr nz,isaux + ld hl,genfn + ld bc,11 + ldir +isaux: call auxopn + jr isopen +opener: call can3 + xor a + ret +isopen: ld hl,(bufsiz) + ld de,xbuff +wrtlp: ld a,(de) + push de + push hl + call putw2c + pop hl + pop de + inc de + dec hl + ld a,h + or l + jr nz,wrtlp +sndack: ld a,ACK + call modopc + jp mainlp +pnerr: ld hl,(sntsec) + ld a,h + or l + or b + jr z,ymname + ld a,(sectnm) + cp b + jr nz,numbad + call diag + db 'Duplicate sector flushed\0' +flsdup: call gettc3 + jr nc,flsdup + jr sndack +numbad: call dnum + ld a,1 + ld (fatal),a + call can3 + jr ckserr +ymname: call redsec + jr c,ckerr + call pktok + ld a,(xbuff) + or a + jr nz,isynam + ld a,ACK + call modopc + xor a + ret +isynam: ld (ymdmb),a + ld hl,xbuff + call cpmaux + call auxopn + ld a,ACK + ld (batch),a + ld (openfl),a + call modopc + ld b,20 +waitjn: push bc + call tenth + pop bc + djnz waitjn + jr setb +snerr: call dnum +ckserr: ld hl,errorf + inc (hl) +ckerr: call pkterr + ld hl,errors + inc (hl) + ld hl,(sntsec) + ld a,(secip) + or h + or l + jr z,setb + ld a,(errorf) + or a + jr z,noerr + ld a,(fatal) + or a + jp nz,killit +quiet: call gettc3 + jr nc,quiet + jr sndnak +setb: ld hl,errors + ld a,(hl) + xor CRCSW + jr nz,crcnak + ld (hl),a + inc hl + inc (hl) + ld a,(hl) + xor 4 + jp z,killit + inc hl + ld a,(hl) + or a + jr z,setcrc + ld (hl),0 + call diag + db 'Switching to checksum\0' + jr crcnak +setcrc: inc (hl) + call diag + db 'Switching to CRC\0' +crcnak: call snint + jr noerr +sndnak: call flsnak +noerr: ld a,(errors) + cp ERRMAX + jp c,mainlp +killit: call can3 + xor a + ret +soheot: ld a,1 + call gettoc + jr c,eotok + call diag + db 'EOT followed by character - ignored\0' + jp ckserr +eotok: ld hl,eotc + ld a,(hl) + or a + jr nz,ackeot + inc (hl) + ld a,(errors) + cp ERRMAX + jr nc,killit + jp sndnk1 +ackeot: call flshw2 + ld de,auxfcb + ld c,close + call usrbds + ld a,ACK + call modopc + ld a,(batch) + or a + ret + +redsec: ld hl,0 + ld (chksum),hl + ld hl,(bufsiz) + ld de,xbuff +rblp: push hl + push de + call gettc3 + pop de + jr nc,nordto + pop hl +rdtime: call dtime +seterr: ld hl,errorf + inc (hl) + scf + ret +nordto: ld (de),a + ld a,(crcmod) + or a + ld a,(de) + jr z,updcsm + push de + call updcrc + pop de + jr mvptr +updcsm: ld hl,chksum + add a,(hl) + ld (hl),a +mvptr: pop hl + inc de + dec hl + ld a,h + or l + jr nz,rblp + ld a,(crcmod) + or a + call nz,gettc3 + jr c,rdtime + ld d,a + push af + call gettc3 + pop de + jr c,rdtime + ld e,a + ld hl,(chksum) + xor a + sbc hl,de + ret z + ld a,(crcmod) + or a + jr nz,badcrc + call dcheck + jr seterr +badcrc: call dcrc + jr seterr + +snint: ld a,(crcmod) + or a + ld a,CRCCHR + jr nz,sendnc +flsnak: ld a,NAK + push af + call flsrng + pop af +sendnc: jp modopc + \ No newline at end of file diff --git a/source/SEND.Z b/source/SEND.Z new file mode 100644 index 0000000..adee580 --- /dev/null +++ b/source/SEND.Z @@ -0,0 +1,84 @@ +; send.z - protocol send code for qterm + +.var ipbuf 0x80 + +.extern send +send: + call pmode + inc hl + jr z,modokk + cp 'x' + jr z,modok ; k & x are the only legal modes (for now) + +.extern moderr +moderr: + call ilprt + db 'Unknown mode\r\n\0' + ret + +modokk: + ld a,(hl) + or 0x20 + cp 'f' + jr z,arefil + cp 'l' + jr z,arefil + xor a +modok: push hl ; save pointer + push af ; and mode character + call unbyp ; step over flags + call scnswl ; parse names + ld hl,(fnbspt) + ld de,work + pop af + sbc hl,de + pop hl + jr nz,arefil ; any files found? + call ilprt ; tell if not + db 'No files to send\r\n\0' + ret +arefil: ex af,af' + ld a,(xfersz) ; get proposed transfer size + ld e,4 +sizlp: sla e ; shift size around in e + rra + jr nc,sizlp ; loop till we hit a bit + ld a,e + ld (btxf),a ; and save it away + ex af,af' + cp 'x' ; Kermit or Xmodem + jr z,sendx + call sendk ; send kermit + jr xferdj ; and unwrap +sendx: call xmflgs ; parse xmodem flags + call initx + call initsn ; set up screen + ld hl,(mdm7b) + ld a,h + or l ; batch send? + jr z,nosb ; no - just send one file + ld a,(longpk) + ld (olp),a ; save 1K packet flag +sblp: call srnxt + jr z,doneb ; loop till all names done + call xsfile ; send a file + jr nz,xferdj ; terminate on an error + ld a,(olp) + ld (longpk),a ; reset 1K packet flag + jr sblp +doneb: xor a + ld (auxfcb + 2),a ; set flag for end of transfer + jr xjsf +nosb: call srnxt ; single file send - get it + jr nz,xjsf ; send it if it exists + call can3 ; kill the far end + xor a + inc a + jr xferdj ; and return error +xjsf: call xsfile ; send single file or batch close +xferdj: push af + ld a,(obtxf) + ld (btxf),a ; reset transfer size to whatever + pop af + jp xferd + \ No newline at end of file diff --git a/source/SENDK.Z b/source/SENDK.Z new file mode 100644 index 0000000..25b1e77 --- /dev/null +++ b/source/SENDK.Z @@ -0,0 +1,262 @@ +; sendk.z - kermit send xfer code + +.incl "c:vars" +.incl "c:kermit" + +.extern sendk +sendk: + call kinit + call initsn + ld a,(sercmd) + or a + ld l,'G' + jr nz,sendit + call srnxt + ret z + ld l,'S' +sendit: xor a + ld (nn),a + ld (numtry),a + ld a,l + ld (ksflg),a +sndklp: ld (state),a + cp 'S' + jr nz,nosini + call sinit + jr sndklp +nosini: cp 'F' + jr nz,nosfil + call sfile + jr sndklp +nosfil: cp 'D' + jr nz,nosdat + call sdata + jr sndklp +nosdat: cp 'Z' + jr nz,noseof + call seof + jr sndklp +noseof: cp 'B' + jr nz,nosbrk + call sbreak + jr sndklp +nosbrk: cp 'G' + jr nz,nossrv + call sserve + jr sndklp +nossrv: xor 'C' + ret + +sinit: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + call spar + call flsrng + ld a,'S' + ld bc,(nn) ; packet number + ld de,13 ; size + ld b,d ; b == 0 => not extended + call spack + call rpack + ld b,a + cp 'N' + jr nz,nosin +retstt: ld a,b + or a + jr z,perr + cp 'N' + jr nz,pbn + call dnak + jr perr +pbn: call dnum +perr: call pkterr + ld a,(state) + ret +nosin: or a + jr z,perr + cp 'E' + jr nz,nosie + call prerrp + jr sreta1 +sireta: call dbadp +sreta1: ld a,'A' + ret +nosie: cp 'Y' + jr nz,sireta + ld a,(nn) + cp c + jr nz,retstt +ipakok: call rpar + xor a + ld (numtry),a + ld hl,nn + inc (hl) + res 6,(hl) + call pktok + ld a,(cktypq) + ld (cktyp),a + ld a,'F' + ret + +sfile: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + call kresgp + ld de,auxfcb + 2 + ld hl,packet + ld b,8 + call scnfil + ld a,(de) + and 0x7f + cp ' ' + jr z,noext + ld (hl),'.' + inc hl + ld b,3 + call scnfil +noext: ld (hl),b + ld de,packet + sbc hl,de + ld bc,(nn) + ld e,l + ld d,0 + ld b,d + ld a,'F' +finsf: call spack + call rpack + ld b,a + cp 'N' + jr nz,nosfn + dec c + res 7,c + res 6,c + ld a,'Y' +nosfn: cp 'Y' + jp nz,nosin + ld a,(nn) + xor c + jp nz,retstt + ld (numtry),a + ld a,c + inc a + and 63 + ld (nn),a + call pktok + call bufill + ld (size),bc + inc a + ld a,'Z' + ret z + ld a,'D' + ret + +sdata: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + ld de,(size) + ld bc,(nn) + ld b,1 + ld a,e + cp MAXPSIZ + ld a,'D' + jr nc,finsf + ld b,d + jr finsf + +seof: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + ld a,'Z' + ld bc,(nn) + ld de,0 + ld b,e + call spack + call rpack + ld b,a + cp 'N' + jr nz,nosen + dec c + res 7,c + res 6,c + ld a,'Y' +nosen: cp 'Y' + jp nz,nosin + ld a,(nn) + xor c + jp nz,retstt + ld (numtry),a + ld a,c + inc a + and 63 + ld (nn),a + call pktok + call srnxt + ld a,'F' + ret nz + ld a,'B' + ret + +sbreak: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + ld a,'B' + ld de,0 +finsb: ld bc,(nn) + ld b,d + call spack + call rpack + ld b,a + cp 'N' + jr nz,nosbn + dec c + res 7,c + res 6,c + ld a,'Y' +nosbn: cp 'Y' + jp nz,nosin + ld a,(nn) + xor c + jp nz,retstt + ld (numtry),a + ld a,c + inc a + and 63 + ld (nn),a + call pktok + ld a,'C' + ret + +sserve: + ld hl,numtry + ld a,(hl) + inc (hl) + cp MAXTRY + ld a,'A' + ret nc + ld a,(sercmd) + and 0x5f + ld (packet),a + ld a,'G' + ld de,1 + jr finsb + \ No newline at end of file diff --git a/source/SENDX.Z b/source/SENDX.Z new file mode 100644 index 0000000..ba6f4b9 --- /dev/null +++ b/source/SENDX.Z @@ -0,0 +1,402 @@ +; sendx.z - file send using xmodem protocol + +.incl "c:vars" +.incl "c:xmodem" + +.extern xsfile +xsfile: + xor a + ld (closeo),a + ld (attmps),a + ld a,(auxfcb + 2) + or a + jr nz,noclos + inc a + ld (closeo),a + jr sbufsz +noclos: ld de,auxfcb + ld c,cfsize + call usrbds + ld hl,(auxfcb + 34) + ld (expsec),hl +sbufsz: ld a,(longpk) + or a + ld hl,128 + jr z,gotbsz + ld hl,1024 +gotbsz: ld (bufsiz),hl +waitsu: ld a,30 + call gettoc + cp 'K' + jr z,waitsu + cp CAN + jr z,sucan + cp CRCCHR + jr z,sucrcn + xor NAK + jr z,sucrcn + ld hl,attmps + inc (hl) + ld a,(hl) + cp NAKMAX + jr c,waitsu + call rnresp + jr m7err +sucan: call chkcan + jr nz,waitsu + jr m7err +sucrcn: ld (crcmod),a + ld a,(mdm7b) + or a + jp z,nom7 + ld a,(closeo) + or a + jr z,nom7c + ld a,ACK + call modopc + ld a,EOT + call modopc + xor a + ret +nom7c: ld (attmps),a + call sndnam + jr nc,m7nmok + call pkterr + ld a,(attmps) + inc a + cp RETRMX + jr c,nom7c + call diag + db 'MODEM7 batch filename error\0' +m7errc: call can3 +m7err: xor a + inc a + ret +m7nmok: call pktok +wack: ld a,5 + call gettoc + cp 'K' + jr z,wack + cp CRCCHR + jr z,setm7c + xor NAK + jr z,setm7c + call diag + db 'MODEM7 error: NAK/C expected\0' + jr m7errc +setm7c: ld (crcmod),a +nom7: ld hl,1 + ld (sectnm),hl + ld a,(ymdmb) + or a + jr z,noymf + ld hl,128 + ld (bufsiz),hl + ld l,h + ld (sectnm),hl +noymf: xor a + ld (errors),a + ld h,a + ld l,a + ld (sntsec),hl +packlp: xor a + ld (attmps),a + ld a,(ymdmb) + or a + jr z,noym + ld hl,(sectnm) + dec hl + ld a,h + or l + jr nz,noym + ld a,(closeo) + or a + jr z,noymc + xor a + ret +noymc: ld a,(longpk) + or a + jr z,getyfs + ld h,1024 >> 8 + ld (bufsiz),hl +getyfs: call gettc3 + cp CAN + jr z,yfscan + cp 'K' + jr z,getyfs + cp CRCCHR + jr z,yfscrc + ld hl,attmps + inc (hl) + ld a,(hl) + cp NAKMAX + jr c,getyfs + call diag + db 'YMODEM error: C expected\0' +yfserc: call can3 +yfserr: xor a + inc a + ret +yfscan: call chkcan + jr nz,getyfs + jr yfserr +yfscrc: ld (crcmod),a + xor a + ld (attmps),a +noym: ld a,(bufsiz 1) + or a + jr z,bsok + ld a,(errors) + cp KSWMAX + jr nz,noksw + call diag + db 'Too many errors - reducing packet size\0' + xor a + ld (olp),a + jr dropbs +noksw: ld hl,(expsec) + ld de,(sntsec) + or a + sbc hl,de + jr z,bsok + ld a,l + and ~7 + or h + jr nz,bsok + call diag + db 'End of file - reducing packet size\0' +dropbs: ld hl,128 + ld (bufsiz),hl + xor a + ld (longpk),a +bsok: ld hl,(sectnm) + ld a,h + or l + jr z,ymname + ld de,xbuff + ld hl,(bufsiz) +filbuf: push hl + push de + call getw2c + pop de + pop hl + jp c,pakbrk + ld (de),a + inc de + dec hl + ld a,h + or l + jr nz,filbuf + jr xmitlp +ymname: ld hl,xbuff + ld de,xbuff + 1 + ld bc,127 + ld (hl),b + ldir + ld a,(closeo) + or a + jr nz,noymnm + ld de,auxfcb + 2 + ld hl,xbuff + ld b,8 + call scnfil + ld a,(de) + and 0x7f + cp ' ' + jr z,noext + ld (hl),'.' + inc hl + ld b,3 + call scnfil +noext: ld hl,xbuff - 1 +lower: inc hl + ld a,(hl) + or a + jr z,setfs + cp 'A' + jr c,lower + cp 'Z' + 1 + jr nc,lower + set 5,(hl) + jr lower +setfs: ld hl,(expsec) + ld (xbuff + 126),hl +noymnm: xor a + ld (attmps),a +xmitlp: ld a,(bufsiz + 1) + or a + ld a,SOH + jr z,gotsoh + inc a +gotsoh: call modopc + ld a,(sectnm) + push af + call modopc + pop af + cpl + call modopc + ld hl,0 + ld (chksum),hl + ld hl,(bufsiz) + ld de,xbuff +snddat: ld a,(de) + push hl + push de + call modopc + pop de + ld a,(crcmod) + or a + ld a,(de) + jr z,updcsm + push de + call updcrc + pop de + jr sndcnt +updcsm: ld hl,chksum + add a,(hl) + ld (hl),a +sndcnt: pop hl + inc de + dec hl + ld a,h + or l + jr nz,snddat + call flsrng + ld a,(crcmod) + or a + ld a,(chksum + 1) + call nz,modopc + ld a,(chksum) + call modopc + ld hl,attmps + inc (hl) + ld a,10 + call gettoc + jr c,timeo + cp ACK + jr z,respok + cp CAN + jr nz,nocan + call chkcan + jp z,m7err +nocan: push af + call cvtnib + ld (char + 1),a + pop af + rrca + rrca + rrca + rrca + call cvtnib + ld (char),a + call diag + db 'Non-ACK: 0x' +char: db 'ZZ\0' + jr ackerr +timeo: call diag + db 'Timeout\0' +ackerr: call pkterr + ld hl,errors + inc (hl) + ld a,(attmps) + cp RETRMX + jr nc,retrer + ld a,(hl) + cp ERRMAX + jp c,xmitlp + call diag + db 'Too many errors\0' + jr rtrcan +retrer: call diag + db 'Too many retries\0' +rtrcan: jp m7errc +respok: call pktok + ld hl,(sectnm) + inc hl + ld (sectnm),hl + dec hl + ld a,h + or l + jr z,jppl + ld a,(bufsiz + 1) + ld de,8 + or a + jr nz,updss + ld e,1 +updss: ld hl,(sntsec) + add hl,de + ld (sntsec),hl +jppl: jp packlp +pakbrk: ld b,RETRMX +eotlp: push bc + ld a,EOT + call modopc + ld a,15 + call gettoc + pop bc + cp ACK + jr z,didit + cp NAK + jr z,eotlp +eotna: call diag + db 'EOT not acked\0' +badeot: djnz eotlp + call rnresp + xor a + inc a + ret +didit: xor a + ret + +.extern sndnam +sndnam: + ld hl,auxfcb + 2 + ld de,auxlin + push de + ld bc,11 + ldir + pop hl + ld bc,11 << 8 + ld a,ACK + call moppsh +snlp: call mopahl +cknak: ld a,6 + call gettcp + cp CAN + jr nz,nocan1 + ld a,3 + call gettcp + cp CAN + jr nz,cknak + scf + ret +nocan1: cp ACK + jr nz,badnam +acknam: inc hl + djnz snlp + ld (hl),0x1a + call mopahl + ld a,10 + call gettcp + cp c + jr nz,badnam + ld a,ACK + call modopc + or a + ret +badnam: ld a,BADNAM + call modopc + scf + ret + +mopahl: ld a,(hl) + add a,c + ld c,a + ld a,(hl) +moppsh: push bc + push hl + call modopc + pop hl + pop bc + ret + \ No newline at end of file diff --git a/source/SHRINK.Z b/source/SHRINK.Z new file mode 100644 index 0000000..0f4492a --- /dev/null +++ b/source/SHRINK.Z @@ -0,0 +1,48 @@ +; shrink.z - buffers and pointers that get small for a "shrunk" QTERM + +.var memtop 6 + +.extern setshr +setshr: + ld hl,(memtop) + ld de,work2 + 16384 + 1024 + or a + sbc hl,de + ex de,hl + ld hl,xfersz + set 3,(hl) + ret nc + set 2,(hl) + ld hl,work2 + 4096 + ld (cbuff),hl + ld hl,work2 + 8192 + ld (endcbf),hl + ld hl,4095 + ld (cbfsiz),hl + ld a,4096 / 128 + ld (btxf),a + ld (obtxf),a + ld hl,8192 + add hl,de + ret c + call ilprt + db 'Insufficient memory\r\n\0' + rst 0 + +.dseg +.extern cbuff +cbuff: dw work2 + 8192 +.extern endcbf +endcbf: dw work2 + 16384 +.extern cbfsiz +cbfsiz: dw 8191 +.extern btxf +btxf: db 8192 / 128 +.extern obtxf +obtxf: db 8192 / 128 + +.useg +.extern work2 +work2: + ds 16384 ; so that #end in the .SYM file is right + \ No newline at end of file diff --git a/source/SRSCRN.Z b/source/SRSCRN.Z new file mode 100644 index 0000000..90f740b --- /dev/null +++ b/source/SRSCRN.Z @@ -0,0 +1,226 @@ +; srscrn.z - screen routines for displaying status of a transfer + +.incl "c:termcap" + +.extern initsc ; initialise everything +initsc: + push hl + ld hl,0 + ld (packet),hl + ld (terr),hl + ld (perr),hl ; clear the variables + call clear ; and the screen + ld hl,30 + call moveto ; set up for the title + pop hl + call prtslp ; print protocol type + ld hl,m1 +.dseg +m1: db ' File Transfer\0' +.cseg + call prtslp ; finish title + call mtprt + dw [5 << 8] + 10 + db 'Packet:\0' + call mtprt + dw [5 << 8] + 26 + db '0\0' + call mtprt + dw [7 << 8] + 10 + db 'Packet Errors: 0\0' + call mtprt + dw [9 << 8] + 10 + db 'Total Errors: 0\0' + ret ; print rest of screen background + +.extern initsn ; finish setting up screen for send +initsn: + call mtprt + dw [3 << 8] + 10 + db 'Sending:\0' ; direction for where filename goes + call mtprt + dw [11 << 8] + 10 + db 'Sent:\0' ; set up for K transferred printout + ret + +.extern initrc ; finish setting up screen for receive +initrc: + call mtprt + dw [3 << 8] + 10 + db 'Receiving:\0' ; direction for where filename goes + call mtprt + dw [11 << 8] + 10 + db 'Received:\0' ; and for K transferred + ret + +.extern pktok ; register a good packet +pktok: + ld hl,kcnt ; point at k count flag + ld a,(hl) ; get value + or a + jr z,nokprt ; not set, so don't print + dec (hl) ; clear flag + call one_k ; and print a K +nokprt: ld hl,dcnt ; look at count till we need clear + ld a,(hl) + or a + jr z,nocdg ; no clear needed - bypass + dec (hl) + jr nz,nocdg ; not yet ..... skip it + call diag + db 0 ; OK there goes the message + xor a + ld (dcnt),a ; and reset dcnt back to zero +nocdg: ld hl,(perr) + ld a,h + or l + jr z,ncperr + call mtprt + dw [7 << 8] + 26 + db '0 \0' ; reset packet error count on screen + ld hl,0 + ld (perr),hl ; and in memory +ncperr: ld a,(qcount) + or a + ret nz + ld hl,[5 << 8] + 26 + call moveto ; moveto where packet number goes + ld hl,(packet) + inc hl + ld (packet),hl ; bump + jp decout ; and print + +.extern pkterr ; register a bad packet +pkterr: + ld hl,[7 << 8] + 26 + call moveto + ld hl,(perr) + inc hl + ld (perr),hl ; bump and print packet errors + call decout + ld hl,[9 << 8] + 26 + call moveto + ld hl,(terr) + inc hl + ld (terr),hl ; same for total errors + jp decout + +.extern prfile ; print the filename from auxfcb +prfile: + push hl + ld a,d + and e + inc a ; check if there's a real size + jr z,noprte ; skip if not - this is receive + push de + call mtprt + dw [11 << 8] + 34 + db 'out of \0' ; and file size print + pop hl + call decout ; and print it + ld hl,m2 + call prtslp ; and the 'K' +noprte: ld hl,0 + ld (kxfer),hl ; reset count of K transferred + call mtprt + dw [11 << 8] + 26 + db '0' ; and print 0K to start things +m2: db 'K \0' + ld hl,[3 << 8] + 26 + call moveto ; move to where filename gets printed + pop hl ; get fcb address to hl + call prtfl ; print the filename + ld b,14 ; 14 chars to nuke anything left over + jr cleol + +.extern prtfl +prtfl: + ld c,(hl) ; get user number to c + inc hl + ld a,(hl) ; get drive code + inc hl ; point at first char of name + push hl ; save fcb address + push bc ; save user number + add a,'@' ; convert to a letter + ld c,a + call scrout ; and print it + pop hl ; user number back to l + call decob ; and print it + ld c,':' + call scrout ; add a ':' + pop hl +nodrv: ld b,8 + call pfilnm ; print the name portion + ld a,(hl) + cp ' ' ; check for an extension + ret z ; no - all done + push hl + ld c,'.' + call scrout ; print a '.' + pop hl + ld b,3 + +pfilnm: ld a,(hl) ; get a char from fcb + inc hl + push hl + push bc + and 0x7f ; ditch attribute bit + cp ' ' ; is it printable? + ld c,a + call nz,scrout ; print if so + pop bc + pop hl + djnz pfilnm ; loop till all done + ret + +.extern diag +diag: + ld a,3 + ld (dcnt),a ; set so that 3 packets later we'll clear + ld hl,[14 << 8] + 10 + call moveto ; move to where messages go + pop hl ; string address to hl + push hl ; back on stack + call prtslp ; print it + pop de ; restore start to de + push hl ; save end + or a + sbc hl,de ; get length + ld a,50 + sub l ; get 50 - length + ld b,a ; to b +cleol: ld a,(tcbits) + and b_cleol ; clear to eol possible + jp z,cleol ; do it if so + ld c,' ' ; put a space in c +cleolp: push bc + call scrout ; and print it + pop bc + djnz cleolp ; till count runs out + ret + +.extern one_k +one_k: + ld a,(qcount) + or a + ret nz + ld hl,[11 << 8] + 26 + call moveto ; move to where K printout goes + ld hl,(kxfer) + inc hl + ld (kxfer),hl ; add one to count + call decout ; print the number + ld c,'K' + jp scrout ; and add a 'K' + +.dseg +.extern kcnt +kcnt: db 0 + +.useg +dcnt: ds 1 +packet: ds 2 +terr: ds 2 +perr: ds 2 +kxfer: ds 2 + \ No newline at end of file diff --git a/source/SRUTIL.Z b/source/SRUTIL.Z new file mode 100644 index 0000000..aaaadf6 --- /dev/null +++ b/source/SRUTIL.Z @@ -0,0 +1,468 @@ +; srutil.z - utilities for file transfer + +.incl "c:vars" + +.extern kgettc ; kgettc - get a character for kermit - we +kgettc: ; wait as prescribed by kermit's timint + ld a,(timint) ; get the timeout + ; fall into gettcp + +.extern gettcp ; call gettoc, but save registers as we do so +gettcp: + push hl + push de + push bc + call gettoc + pop bc + pop de + pop hl + ret + +.extern gettc3 ; calls gettoc with a timeout of 3 seconds +gettc3: + ld a,4 ; set up, and fall through + +.extern gettoc ; gettoc - get a character, noting timeout. +gettoc: ; enter with timeout (in seconds) in a, return + ; char in a, carry set <=> timeout + ld c,a ; seconds of timeout to c + push bc + call fstmc ; quick pre-check to make sure this has a + pop bc ; chance of working at 38400 + ret nc +gtlp: push bc + call kbdcc ; character from kbd? + xor 'x' & 0x1f ; was it a ^X? + jr nz,noab + ld hl,(abortf) ; abort function set up? + ld e,a + ld d,a + ld (abortf),de ; kill it whatever + ld a,h + or l + jr z,noab ; nope - don't do anything + jp (hl) ; go do the abort code +noab: pop bc + ld de,1100 + call setspd ; set delay value +qrtrms: push hl + push bc + call fstmc ; check for a char at the modem + pop bc + pop hl + ret nc ; yes - send it back + ld b,60 ; wait a while +qmslp: djnz qmslp + dec hl + ld a,h + or l + jr nz,qrtrms ; count down hl + dec c + jr nz,gtlp ; and seconds + scf ; timed out - return w/ carry + ret + +fstmc: call modist ; modem character available? + scf + ret z ; return with carry if not + call modin ; get one if so + or a ; and clear carry + ret + +.extern flsrng +flsrng: call fstmc ; get a char if there's one there + jr nc,flsrng ; loop while they're coming + ret ; exit - all flushed + +.extern srnxt ; get next name from batched list for send +srnxt: + call nxtnam ; get next name from buffer to fcb + ret z ; all done - return zero + call xferax ; move fcb from fcb to auxfcb + ld de,auxfcb + push de ; get and save it's address + ld c,open + call usrbds ; try to open the file + ld h,l + pop de + add a,a ; test a == -1 this way so we get a carry on + sbc a,a ; failure + ret nz ; open failed - return now + push de + ld c,cfsize + call usrbds ; how big is it? + ld de,(auxfcb + 34) + dec de + ld a,d + and e + inc a ; was de dec'ed from 0 to 0xffff + ld a,(auxfcb + 36) + jr nz,aok + dec a ; drop a by 1 if so (big file!) +aok: ld b,3 +mulk: rra ; convert 128 byte blocks to K + rr d + rr e ; shift right + djnz mulk + inc de ; reset de - we ditch what's left in a + pop hl ; fcb back to hl + call prfile ; print file + xor a ; clear carry + inc a ; and z flag + +.extern clrw2 +clrw2: + ld (onekp),a + ld hl,(w2ptr) + ld (endw2),hl ; set pointers so reads from getw2c work + ret + +.extern getw2c ; get a character from work2, via fcb at +getw2c: ; auxfcb, return c on eof + ld de,(w2ptr) ; get current read pointer + ld hl,(endw2) + or a + sbc hl,de ; compare with end of data + ld hl,work2 - 1 + jr z,fillw2 ; at end of buffer - go fill it up again + sbc hl,de ; see how far down buffer we are + ld a,h + and 3 + or l ; on a K boundary? + push de + jr nz,no1k + ld a,(onekp) ; printing k? + ld (kcnt),a +no1k: ld hl,b7flag ; zapping bit 7? + ld a,(hl) ; get the mask + pop hl + and (hl) ; mask a character (and clear the carry) + inc hl + ld (w2ptr),hl ; resave new pointer + ret +fillw2: ld a,(btxf) + ld b,a + inc hl + ld (w2ptr),hl ; get dma address & reset read pointer +filw2l: push bc + push hl + ex de,hl + ld c,setdma + call bdos ; set dma + ld de,auxfcb + ld c,read + call usrbds ; read + pop hl + pop bc + or a + jr nz,filled ; eof - exit now + ex de,hl + ld hl,lbrcnt + inc (hl) ; .LBR active? + jr z,cntok ; nope, don't check + dec (hl) ; adjust count +cntok: dec (hl) + ld hl,128 + add hl,de ; bump dma + jr z,filled ; exit if all done + djnz filw2l ; loop till count runs out +filled: ld (endw2),hl ; save end pointer + ld de,work2 + or a + sbc hl,de ; anything in buffer? + ccf + ret z ; return with carry if not + jr getw2c ; else jump back & get a character + +.extern gw2pb ; push a character back onto work2 +gw2pb: + ld hl,(w2ptr) ; get current read pointer + dec hl ; decrease + ld (hl),a ; and save character + ld (w2ptr),hl ; restore pointer + ret + +.extern putw2c ; output a character in a to file at auxfcb +putw2c: ; update K count as needed + ld hl,(w2ptr) + ld (hl),a ; save the character + inc hl + ld (w2ptr),hl + ld de,(ew2p) + sbc hl,de + jr z,wrtw2 + ld a,h + and 3 + or l + ret nz + jr jone_k +wrtw2: call flshw2 +jone_k: jp one_k + +.extern flshw2 ; flush pending write to auxfcb +flshw2: + ld hl,(w2ptr) ; what is in buffer + ld de,work2 + ld (w2ptr),de ; reset save pointer + or a + sbc hl,de ; see if anything there + ret z ; no - stop right now + dec hl + add hl,hl + inc h + ld b,h ; convert bytes to 128 byte blocks in b +flshlp: push bc + push de + ld c,setdma + call bdos ; set dma address + ld de,auxfcb + ld c,write + call usrbds ; write the data + pop de + ld hl,128 + add hl,de + ex de,hl ; move dma pointer + pop bc + djnz flshlp ; loop till all written + +.extern fill1a ; fill write buffer with 0x1a +fill1a: + ld hl,work2 + ld (w2ptr),hl + ld de,work2 + 1 + ld bc,(cbfsiz) + ld (hl),0x1a + ldir + ret + +; setlbr - getw2c is used to read text for a script. Under certain +; circumstances we need to adjust auxfcb to read from a .LBR, so setlbr +; does the necessary work + +.extern setlbr +setlbr: + ld de,xbuff ; point at buffer to use + push de ; save for later + ld c,setdma + call bdos ; set dma address + ld de,auxfcb + ld c,read + call usrbds ; read a sector + or a + scf + pop de ; restore address + ret nz ; return carry (i.e. error on eof) + ld hl,lbrcnt + bit 7,(hl) ; first time through? + jr z,scnlbr ; nope - go check the stuff + ld a,(xbuff + 14) ; get directory size + ld (hl),a ; save it away + jr add32 ; go move to first real entry +scnlbr: ld a,(de) ; get first byte of this entry + or a + jr nz,add32 ; no file here, move to next + push de ; save pointer to sector in xbuff + inc de ; point at .LBR filename entry + ld hl,fcb + 2 ; point at filename saved in fcb + ld b,11 ; 11 characters to check +nchk: ld a,(de) + xor (hl) ; did we match? + and 0x7f ; don't care about MS bit + jr nz,add32p ; failed the match, try the next name + inc hl + inc de ; move pointers + djnz nchk ; and loop + ex de,hl ; swap .LBR directory pointer to hl + ld e,(hl) + inc hl + ld d,(hl) ; get the starting index to de + inc hl + ld (auxfcb + 34),de ; save it in the fcb + ld a,(hl) ; get the record count + ld (lbrcnt),a + xor a + ld (auxfcb + 36),a ; zero fill top byte of auxfcb + ld de,auxfcb + ld c,redrnd + call usrbds ; seek as needed + or a ; any errors + pop de ; clean up the stack + ret z ; yeaaa - we got it - return w/ no carry + scf ; else set carry for error + ret +add32p: pop de +add32: ld hl,32 + add hl,de ; bump de by 32 + ex de,hl ; save result to de + ld hl,xbuff + 0x80 + sbc hl,de ; end of sector? + jr nz,scnlbr ; no - look for another + ld hl,lbrcnt + dec (hl) ; out of directory sectors? + jr nz,setlbr ; no, back for another chunk + dec (hl) ; turn off .LBR scans + scf ; carry for the error + ret + +; scnswl - parse a list of wildcard filenames into work buffer for subsequent +; transmission + +.extern scnswl ; generate list of filenames for batch send +scnswl: + ld de,work + ld (fnbrpt),de + ld (fnbspt),de ; set read and write pointers +swloop: call byp ; look at next one + or a + ret z ; return when all done + call scnwld ; parse the next selection + jr swloop ; and try again + +.extern cpmaux ; convert generic filename in (hl) to a +cpmaux: ; cp/m fcb in auxfcb + push hl ; save string + ld hl,auxfcb + 2 + ld de,auxfcb + 3 + ld bc,10 + ld (hl),' ' + ldir ; fill filename portion with spaces + ld bc,(newusr) ; get receive drive and user + pop de ; name string back to de + ld hl,auxfcb + 10 ; point hl at extent part of name + push hl + ld hl,auxfcb + ld (hl),c ; save user in place + inc hl + ld (hl),b ; and the drive + inc hl ; point at name part + ld a,3 ; 3 chars in extent + ex af,af' ; in a' + ld b,8 ; 8 chars in name +scanlp: ld a,(de) + inc de ; get next char + cp ' ' + jr nz,nospc ; space gets changed to '-' + ld a,'-' +nospc: or a ; end of string? + jr nz,more ; carry on going if not + pop hl + ret +more: cp '.' ; swap portions on a '.' + jr z,swap + inc b + dec b ; any space left? + jr z,scanlp ; no - loop back + cp '?' + jr z,scanlp ; ignore '?' + cp '*' + jr z,scanlp ; and '*'s + call ucsa ; convert to upper case + ld (hl),a ; finally got us a letter - save it away + inc hl ; move pointer + dec b ; keep count + jr scanlp ; and keep going +swap: ex (sp),hl ; swap pointer on top of stack + ld a,b ; count to a + ex af,af' ; swap w/ a' + ld b,a ; back to b + jr scanlp + +.extern auxopn +auxopn: + call fill1a ; prepare buffer & reset pointer + ld hl,auxfcb + 13 + ld de,auxfcb + 14 + ld bc,20 + ld (hl),b ; nuke out rest of fcb + ldir + ld de,auxfcb + ld c,open + push de + call usrbds ; open the file if we can + pop de + inc a + jr z,fileok ; failed - create & we're done + ld hl,genfn + inc de + inc de + ld bc,11 + ldir ; copy in "canned" filename +mvnaml: dec hl + inc (hl) ; keep filenames changing + ld a,(hl) + cp '9' + 1 + jr c,auxopn ; done - try this one + jr nz,ckltr ; greater than '9', check if it's a letter + ld (hl),'A' ; no - put in 'A' now +ckltr: cp 'Z' + 1 ; used all the letters? + jr c,auxopn ; no - back and try it + ld (hl),'0' ; reset to '0' and try next character + jr mvnaml +fileok: ld c,create ; file doesn't exist - create it + call usrbds + ld a,(ymdmb) ; ymodem name incoming? + ld de,-1 + or a + jr z,nosiz ; no - don't print a size + ld hl,(xbuff + 126); get the record count + ld de,7 + add hl,de ; adjust + ld b,3 +stok: sra h + rr l ; divide by 8 to get K + djnz stok + ex de,hl +nosiz: ld hl,auxfcb ; print filename from aux fcb + jp prfile + +.extern dnak ; call diag to complain about a NAK +dnak: + call diag + db 'NAK received\0' + ret + +.extern dnum ; ..... a bad packet number +dnum: + call diag + db 'Bad packet number\0' + ret + +.extern dtime ; ..... a timeout +dtime: + call diag + db 'Timeout error\0' + ret + +.extern dcheck ; ..... a checksum error +dcheck: + call diag + db 'Checksum error\0' + ret + +.extern dcrc ; ..... a crc error +dcrc: + call diag + db 'CRC error\0' + ret + +.extern dtries ; ..... too many retries +dtries: + call diag + db 'Too many retries\0' + ret + +.dseg +.extern genfn +genfn: db '--XFER--000' +.extern abortf +abortf: dw 0 +.extern lbrcnt +lbrcnt: db 0xff + +.useg +w2ptr: ds 2 +endw2: ds 2 +onekp: ds 1 +.extern ew2p +ew2p: ds 2 + \ No newline at end of file diff --git a/source/TERMCAP.I b/source/TERMCAP.I new file mode 100644 index 0000000..8586b10 --- /dev/null +++ b/source/TERMCAP.I @@ -0,0 +1,11 @@ +; termcap.i - bit values for telling which terminal capabilities are present + +.var b_brite 1 +.var b_dim 2 +.var b_delln 4 +.var b_insln 8 +.var b_delch 16 +.var b_insch 32 +.var b_cleol 64 +.var b_cleos 128 + \ No newline at end of file diff --git a/source/TERMIO.Z b/source/TERMIO.Z new file mode 100644 index 0000000..57117dd --- /dev/null +++ b/source/TERMIO.Z @@ -0,0 +1,512 @@ +; termio.z - subroutines for doing the terminal i/o part of qterm + +.incl "c:termcap" +.incl "c:vars" + +.macro table byte,addr + dw addr + db byte +.endm + +.extern procch +procch: call kbdcc ; get a char if it's waiting + or a + jr z,lstmod ; skip if no character + cp 'x' & 0x1f ; ctl x? + jp z,canscr ; quit if so + ld e,a ; save char in e + ld a,(escval) ; get the escape value + ld hl,pccs + inc (hl) + dec (hl) ; what state? + jr z,pccs0 ; state zero - look for escape + dec (hl) ; reset to 0 + cp e ; escape twice? + jr z,pccop ; yes - send one to modem + ld a,e ; get char back + cp ',' ; was it a ','? + call z,hangup ; hang up if so + cp '.' ; or a '.' + call z,break ; do a break + jr lstmod + +; This is here for branch length reasons + +.extern modop +modop: ; send a character to the modem, respecting + ; half duplex etc. + push af + call modopc ; send it to the modem first + pop hl + ld a,(wflg) ; window mode? + or a + ld a,h ; get character back + jr z,nowin + push hl + call winsnd ; go give it to window input code + pop af ; char back + cp '\r' + ret nz ; returns get special processing + ld a,(eflg) ; echo mode? + or a + ret z ; return if not + ld a,'\n' ; throw a linefeed into the system + jr modop +nowin: ld a,(hflg) ; half duplex? + or a + ret z + ld a,h ; get character again + cp '\r' + jr nz,ipchar ; pass non-return chars straight in + ld a,(eflg) ; echo mode too? + or a + ld a,h ; get the '\r' back + jr z,ipchar ; no echo mode, don't expand it + call ipchar ; go send return to receive code + ld a,'\n' + jr ipmchr ; throw a newline into system as well + +pccs0: cp e ; did we see an escape? + jr z,setesc ; yes, set the flag + ld a,e +pccop: call modop ; send char to modem + db 0x3e ; ld a,xx opcode +setesc: inc (hl) + +.extern lstmod +lstmod: + call wrtscn ; keep the printer going and check if a + ; character is waiting at the modem port. + ret c ; return if nothing arrived + + ld hl,ecval ; echo check needed? + dec (hl) + inc (hl) + jr z,noec ; jump if not + cp (hl) ; did we match? + jr nz,noec ; no - leave value in place + ld (hl),1 ; reset to say we got it +noec: ld hl,bmask + and (hl) ; mask if 7 bit mode + scf + ret z ; return on nulls as well + ld hl,cscqfl + inc (hl) + dec (hl) ; ^S ^Q spotting enabled? + jr z,nocscq + bit 0,(hl) + jr nz,nocs ; waiting for a ^Q? + cp 's' & 0x1f ; ^S ? + jr nz,nocscq ; nope, check status now + set 0,(hl) ; flag that a ^S arrived + scf + ret +nocs: cp 'q' & 0x1f ; was it a ^Q + jr z,wascq + cp 's' & 0x1f ; also let a second ^S toggle + jr nz,nocscq ; not yet - pass this character +wascq: res 0,(hl) ; clear the flag + scf + ret +nocscq: ld hl,eflg + inc (hl) + dec (hl) ; echo mode? +zipch: jr z,ipchar ; no, hand straight to input code + cp '\r' ; return? + jr nz,ipmchr ; no, echo and hand to input code + call ipmchr ; otherwise process, + ld a,'\n' ; and send a newline into the system as well + +ipmchr: push af + call modopc ; send character to modem to echo it + pop af + +.extern ipchar ; needed for sendcs in chat +ipchar: ld c,a ; save char in c + ld a,(jflg) + or a ; junking control characters? + ld a,c + call nz,limitb ; if so it gets tested + ret c ; and thrown if bad + push af ; save the character + call opchar + pop af + call oplst ; output to printer + or a ; clear the carry + ld hl,cflg + bit 0,(hl) ; catch mode enabled + ret z ; no - return + ld hl,(cptr) + ld (hl),a ; save char away + inc hl + ld (cptr),hl ; update pointer + ld de,(endcbf) + or a + sbc hl,de ; buffer full? + scf + ccf ; clear carry w/o affecting zero + ret nz ; no - keep going + push af + call flushc ; go flush data in catch buffer + call ctlq + pop af + ret + +.extern savrng +savrng: ; get a char and save in ring buffer + call modist ; char available? + ret z ; no. + call modin ; get it + ld de,(ringpw) ; pick up indices + ld hl,(ringpr) + inc de ; move write index + res 2,d + or a + sbc hl,de ; buffer full? + ret z ; return if so + ld (ringpw),de ; save revised index + ld hl,ring + add hl,de + ld (hl),a + ret + +wrtscn: call wrtlst ; keep printer output rolling + call savrng ; get any incoming characters + ld de,(ringpr) + ld hl,(ringpw) + xor a + sbc hl,de ; anything in the ring? + scf + ret z ; nope + inc de + res 2,d + ld (ringpr),de ; save new index + ld hl,ring + add hl,de ; index into buffer + ld a,(hl) ; get the char + ret ; and return it + +.extern oplst ; put a character in the list ring buffer +oplst: + ld hl,oflg ; are we saving? + bit 0,(hl) + ret z ; return if not + push bc ; save bc + ld hl,(lstpr) + ld de,(lstpw) ; get the list ring buffer indices + inc de ; move the write pointer + res 2,d ; wrap if it hits 1K + or a + sbc hl,de ; check for buffer full + jr nz,notful + push af ; save the character + call ctls ; send an xoff and wait for things to cool +mtloop: call wrtlst ; write a character if we can + jr nc,mtloop ; keep writing if we're not done + call ctlq ; OK - you can wake up again + pop af ; restore char again +notful: ld hl,(lstpw) + inc hl + res 2,h + ld (lstpw),hl ; save the revised write pointer + ld de,lstbuf ; address the list buffer + add hl,de + ld (hl),a ; and save the character + pop bc ; get bc back. + ret + +wrtlst: ld de,(lstpr) ; see if we can send a character to the lst + ld hl,(lstpw) ; get the ring buffer indices + or a + sbc hl,de ; anything in the buffer + scf ; set carry to say we're done + ret z ; nope - nothing to do + push de + ld a,0x2d + call cbios ; call to list status + pop de ; get read pointer back + or a + ret z ; can't write - return now + ld hl,lstbuf + add hl,de ; index into buffer + inc de + res 2,d ; wrap if over 1K + ld (lstpr),de ; save new index + ld c,(hl) + +.extern lstout +lstout: ld a,0x0f + call cbios + or a ; clear the carry + ret + +.extern ctlq +ctlq: + ld a,(lxoff) ; get local xon / xoff status + or a + ret z ; send nothing if xoff sent from keyboard + ld a,(cqchr) + +.extern modopc +modopc: push af ; save character on stack +modopl: call modost ; check if we can send + jr z,modopl ; can't send - loop back + pop af ; char back to a + jp modout ; and away it goes + +.extern limitb +limitb: ; like limitc, but includes backspace + cp '\b' + ret z + +.extern limitc +limitc: + cp 0x7f ; del or greater + ccf + ret c ; are invalid + +.extern iswa +iswa: + cp ' ' ; space to '~' + ret nc ; are valid + cp '\r' + ret z ; return is valid + cp '\n' + ret z ; newline also + cp '\t' + ret z ; and tab + scf ; rest are bad + ret + +.var #where 0 +.var #bot 1 +.var #top 2 +.var #col 6 +.var #row 7 +.var #lff 8 + +nocndo: call ilprt ; and finish the message + db '\r\nW ignored - insufficient terminal capabilities\r\n\0' + ret + +wend: ld hl,23 << 8 + jp moveto + +.extern witog +witog: ld a,(tcbits) ; check what terminal abilities we have + cpl + and b_delln | b_insln + ld (wtflag),a ; save window type value + jr z,cando ; got insert and delete - away we go + ld a,(tcbits) + and b_cleol ; alternatively check for clear to end-of-line + jr z,nocndo ; missing it as well - can't do this + +cando: ld hl,wflg + call togflg ; toggle the flag first of all + jr z,wend ; disabled - so return & do nothing + dec (hl) ; temporarily turn mode back off so prompt + push hl ; works right + call prompt + db 'Window size (b / s)? \0' + pop hl + inc (hl) ; turn flag back on + ld hl,ipbuf + call byp + or 0x20 + xor 's' ; did they say 's'? + +; we could probably optimise this even more, only two numbers actually change + + ld hl,sdat ; get small window data + jr z,gotdat ; jump if so + ld hl,bdat ; otherwise large window data +gotdat: ld de,wrdat + ld bc,6 + ldir ; go install it + ld h,d + ld l,e + inc de + ld c,5 + ld (hl),b ; clear the rest of the information + ldir + ld a,(wtflag) + or a ; what sort of windowing + jr nz,setrol ; rolling w/ clear to eol + ld hl,(rbot) ; normal - get bottom row + ld a,(sbot) ; and the other + jr wsetsc ; and install them +setrol: ld hl,(rtop) ; get top row + ld a,(stop) ; and the other +wsetsc: ld (srow),a ; save send window current row + ld a,l + ld (rrow),a ; and receive window + ld (where),a ; and set where so we'll do a moveto + call clear + ld hl,(rtop - 1) ; get top of receive window + dec h ; - 1 to move up a line + ld l,0 ; and the first positon + call moveto ; go there + call dim ; dim mode + ld bc,[80 << 8] + '-' +dashes: push bc + call scrout ; spit out 80 dashes + pop bc + djnz dashes + jp bright ; set bright mode and we're done + +istab: ld c,' ' + call opwc + ld a,(iy + #col) + and 7 + jr nz,istab + ret + +trybs: cp '\b' + jr nz,trytab + ld a,(iy + #col) + or a + ret z + dec (iy + #col) + call opbs + ld c,' ' + call scrout +opbs: ld c,'\b' + jp scrout + +trytab: cp '\t' + jr z,istab + +nobs: cp '~' + 1 + ret nc + cp ' ' + ret c +opwc: push hl + call scrout + pop hl + ld (iy + #lff),0 + inc (iy + #col) + ld a,(iy + #col) + xor 80 + jr z,scrl + ret + +.extern winrec +winrec: ld iy,wrdat + jr winpc + +winsnd: ld iy,wsdat + ld c,a + +winpc: ld hl,where + ld a,(iy + #where) + cp (hl) + jr z,posok + ld (hl),a + push hl + push bc + ld l,(iy + #col) + ld h,(iy + #row) + call moveto + pop bc + pop hl +posok: ld a,c + cp '\n' + jr z,scrl + cp '\r' + jr nz,trybs +scrl: xor a + cp (iy + #lff) + ret nz + ld (iy + #col),a + ld (hl),h + ld (iy + #lff),h + ld a,(wtflag) + or a + jr nz,roll + ld a,(iy + #top) + ld h,(iy + #bot) + ld l,22 + +; rollit - scroll a region on the screen - this is made external so the VT100 +; code can get at it + +.extern rollit +rollit: or a + push hl + jr nz,dell + ld hl,23 << 8 + call moveto + ld c,'\n' + call scrout + jr dontop +dell: ld l,0 + ld h,a + call moveto + call dellin +dontop: pop hl + ld a,h + cp l + ret z + ld l,0 + call moveto + jp inslin + +roll: ld a,(iy + #row) + call incrow + ld (iy + #row),a + call incrow + ld h,a + ld l,0 + call moveto + jp cleol + +incrow: cp (iy + #bot) + jr z,reload + inc a + ret +reload: ld a,(iy + #top) + ret + +.dseg +.extern pccs +pccs: db 0 +ringpr: dw 0 +ringpw: dw 0 +lstpr: dw 0 +lstpw: dw 0 + +.useg +ring: ds 1024 +lstbuf: ds 1024 + +wrdat: +rwhere: ds 1 +rbot: ds 1 +rtop: ds 1 + +wsdat: +swhere: ds 1 +sbot: ds 1 +stop: ds 1 + +rcol: ds 1 +rrow: ds 1 +rlff: ds 1 + +scol: ds 1 +srow: ds 1 +slff: ds 1 + +.extern where +where: ds 1 +wtflag: ds 1 + +.dseg + +sdat: db 0,22,12 + db 1,10,0 + +bdat: db 0,22,5 + db 1,3,0 + \ No newline at end of file diff --git a/source/VARS.I b/source/VARS.I new file mode 100644 index 0000000..b6e0698 --- /dev/null +++ b/source/VARS.I @@ -0,0 +1,31 @@ +; vars.i - global variables for qterm + +.var buffin 10 ; console buffered input +.var versn 12 ; get operating system version +.var rescpm 13 ; reset bdos to avoid R/O disk problems +.var seldrv 14 ; select drive +.var open 15 ; open a file +.var close 16 ; close a file +.var srchf 17 ; find the first occurance +.var srchn 18 ; find the next occurance +.var erase 19 ; erase file +.var read 20 ; read sequential +.var write 21 ; write sequential +.var create 22 ; create a file +.var rename 23 ; rename file +.var getdrv 25 ; get current drive +.var setdma 26 ; set the dma address +.var dpbadr 31 ; get disk parameter block address +.var gsuser 32 ; get or set user code +.var redrnd 33 ; read random +.var cfsize 35 ; compute the filesize +.var logdrv 37 ; reset individual drive + +.var fcb 0x5b ; fcb address +.var ipbuf 0x80 ; input buffer +.var buffer 0x80 ; buffer used for wildcard scans +.var cmdlin 0x80 ; where the command tail lives + +.var op_bit 1 ; output modem chars flag +.var mat_bit 2 ; print match messages flag +.var lf_bit 4 ; print looking for messages flag \ No newline at end of file diff --git a/source/VERSION.I b/source/VERSION.I new file mode 100644 index 0000000..c1fa3cb --- /dev/null +++ b/source/VERSION.I @@ -0,0 +1,49 @@ +; version.i - keep the version in one place + +.var no 0 +.var yes ! no + +.var major 4 +.var minor 3 +.var rev 'e' +.var subver 0 + +;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +.var dg yes ; set this no for release versions +;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.macro year + db '1991' +.endm + +.macro version +.if major >= 100 + db major / 100 + '0' +.endif +.if major >= 10 + db [major / 10] % 10 + '0' +.endif + db major % 10 + '0' + db '.' +.if minor >= 100 + db minor / 100 + '0' +.endif +.if minor >= 10 + db [minor / 10] % 10 + '0' +.endif + db minor % 10 + '0', rev +.if subver + db ' (' +.if subver >= 1000 + db subver / 1000 + '0' +.endif +.if subver >= 100 + db [subver / 100] % 10 + '0' +.endif +.if subver >= 10 + db [subver / 10] % 10 + '0' +.endif + db subver % 10 + '0', ')' +.endif +.endm + \ No newline at end of file diff --git a/source/VT100.Z b/source/VT100.Z new file mode 100644 index 0000000..c4230c1 --- /dev/null +++ b/source/VT100.Z @@ -0,0 +1,508 @@ +; vt100.z - handle vt100 emulation in QTERM + +.incl "c:vars" + +.var decom 0x20 +.var ascrl 0x40 + +.extern initv +initv: + ld hl,inits + ld de,inits + 1 + ld bc,{inite - inits} + ld (hl),b + ldir +jp23: ld hl,23 << 8 + jr dochk +chkxy: ld hl,(x) +dochk: ld a,l + ld e,79 + call chk1 + ld l,a + ld a,h + ld e,23 + call chk1 + ld h,a + ld a,(flags) + and decom + jr z,savmov + ld de,(hi) + ld a,h + cp e + jr nc,aok1 + ld h,e +aok1: ld a,d + cp h + jr nc,savmov + ld h,d +savmov: ld (x),hl +lodmov: ld hl,(x) + jp moveto + +.extern opchar +opchar: ld c,a ; save char in c + ld a,(mode) + and op_bit + ret nz ; exit if not doing output + ld a,(wflg) ; split window mode? + or a + jp nz,winrec ; yes - hand to receive portion + ld a,(vflg) ; vt100 emulation on? + or a + jp z,scrout ; no- send to screen + ; otherwise give char to vt100 processor + +.extern vt100 +vt100: + ld a,c + cp '\b' + ld hl,x + jr nz,nobs + ld a,(hl) + dec a + ret m +savho: ld (hl),a +jpsco: jp scrout +nobs: xor '\r' + jr z,savho + cp '\t' ^ '\r' + jr nz,notab + ld a,(hl) + and ~7 + add a,8 + cp 80 + ret z + ld (hl),a + jr lodmov +notab: cp '\n' ^ '\r' + jr nz,nonl + inc hl + ld a,(lo) + or a + jr nz,islo + ld a,23 +islo: cp (hl) + jp z,scrlvt + ld a,(hl) + cp 23 + jr z,lodmov + inc (hl) +lodmv1: jr lodmov +nonl: cp '\7' ^ '\r' + jr z,jpsco + ld hl,nk + ld a,(hl) + or a + jr z,nokil + dec (hl) + ret +nokil: ld a,(state) + or a + jr nz,stt1 + ld a,c + cp '\e' + jr nz,noesc + xor a + ld (fbits),a + ld (setv),a + inc a +savstt: ld (state),a + ret +noesc: cp ' ' + ret c ; all other control chars are tossed + cp 0x7f ; delete? + ret z ; yes - we throw it away + jr c,okasc ; less than delete => not a grafix char + ld c,'+' ; assume all are '+'s + ld hl,vtxtbl ; except special ones in the table +.dseg +vtxtbl: db 205,'=' + db 186,'|' + db 196,'-' + db 179,'|' + db 0 +.cseg +xtblp: ld e,(hl) ; get char to match + dec e + inc e ; is it zero? + jr z,okasc ; yes, all done so exit + inc hl + ld d,(hl) ; get ascii char to use + inc hl + cp e ; did we hit it? + jr nz,xtblp ; nope, back to try again + ld c,d ; else save character from d +okasc: ld hl,x + ld a,(hl) + cp 79 + jr nc,xge79 + inc (hl) + jr jpsco +xge79: inc hl + ld a,(hl) + cp 23 + jr nz,yne23 + ld a,(lo) + or a + jr z,cka + cp 23 + ret nz +cka: ld a,(flags) + and ascrl + ret z + push bc + call scrlvt + ld hl,[22 << 8] + 79 + call moveto + pop bc + call scrout + jp jp23 +yne23: call scrout + ld a,(flags) + and ascrl + jr z,lodmv1 + ld hl,y + ld a,(lo) + or a + jr z,nolo + cp (hl) + jr nz,nolo + push hl + call scrlvt + pop hl + dec (hl) +nolo: inc (hl) + dec hl + ld (hl),0 + jr lodmv2 +stt1: dec a + jr nz,stt2 + ld a,c + cp '[' + ld a,2 + jr z,savstt + xor a + ld (state),a + ld a,c + cp '#' + jr z,k1 + cp '(' + jr z,k1 + cp ')' + jr nz,tryM +k1: ld hl,nk + inc (hl) + ret +tryM: ld hl,y + cp 'M' + jr nz,tryE + ld a,(hi) + cp (hl) + jr nz,nohi + call revsvt + jr lodmv2 +nohi: dec (hl) + jp p,lodmov + inc (hl) + jr lodmv2 +tryE: xor 'E' + jr nz,tryD + dec hl + ld (hl),a + inc hl + jr doD +tryD: dec a + jr nz,try7 +doD: ld a,(lo) + or a + jr z,look + ld a,23 +look: cp (hl) + jr nz,nolo1 + call scrlvt + jr lodmv2 +nolo1: ld a,(hl) + cp 23 + jr nc,lodmv2 + inc (hl) +lodmv2: jp lodmov +try7: cp ['7' ^ 'E'] - 1 + jr nz,try8 + ld hl,(x) + ld (sx),hl + ld a,(mm) + ld (sm),a + ret +try8: cp ['8' ^ 'E'] - 1 + ret nz + ld hl,(sx) + ld (x),hl + ld a,(sm) + call setdb + jp chkxy +stt2: dec a + jr nz,stt3 + ld hl,p1 + ld (pp),hl + ld hl,0 + ld (p1),hl + ld a,3 + ld (state),a + ld a,c + cp '?' + jr nz,stt3 + ld (setv),a + ret +cs2: call clear +lodmv3: jr lodmv2 +vclrs: ld a,(de) + or a + jp z,cleos + dec a + jr nz,cs2 + push hl + ld hl,0 + call moveto + pop bc + push bc + ld a,b + or a + jr z,nolin +cslp: push bc + call cleol + ld c,'\n' + call scrout + pop bc + djnz cslp +nolin: ld b,c + inc b + ld c,' ' +cllp: push bc + call scrout + pop bc + djnz cllp + ld c,'\b' + jp scrout +cl2: ld c,'\r' + call scrout + call cleol + jr lodmv3 +vclrl: ld a,(de) + or a + jp z,cleol + dec a + jr nz,cl2 + ld c,'\r' + push hl + call scrout + pop bc + jr nolin +stt3: ld a,c + cp ';' + jr nz,nosc + ld a,(setv) + or a + jr z,nosv +dofbit: ld hl,p1 + ld a,(hl) + or a + ret z + ld b,a + xor a + ld (hl),a + scf +fblp: adc a,a + djnz fblp + dec hl + or (hl) + ld (hl),a + ret +nosv: ld hl,(pp) + ld de,p1 + sbc hl,de + jr nz,setp3 + add hl,de + inc hl +savpp: ld (pp),hl + ret +setp3: ld hl,p3 + ld (hl),0 + jr savpp +nosc: sub '0' + cp 10 + jr nc,notdig + ld hl,(pp) + ld e,a + ld a,(hl) + add a,a + add a,a + add a,(hl) + add a,a + add a,e + ld (hl),a + ret +notdig: ld hl,state + ld (hl),0 + ld hl,swtab - 1 +.dseg +.macro table byte,word + dw word + db byte - '0' +.endm +swtab: + table 'A',vup + table 'B',vdown + table 'C',vright + table 'D',vleft + table 'H',vmove + table 'f',vmove + table 'J',vclrs + table 'K',vclrl + table 'r',vmarg + table 'm',vmode + table 'h',vsm + table 'l',vrm +endtab: +.cseg + ld b,{endtab - swtab} / 3 +switch: inc hl + ld e,(hl) + inc hl + ld d,(hl) + inc hl + cp (hl) + jr z,hitit + djnz switch + ret +hitit: push de + ld hl,(x) + ld de,p1 + ret +vup: call defp1 + neg + jr addy +vdown: call defp1 +addy: add a,h + ld h,a +dochk1: jp dochk +vleft: call defp1 + neg + jr addx +vright: call defp1 +addx: add a,l + ld l,a + jr dochk1 +vmove: call defp1 + ld h,a + ld a,(flags) + and decom + jr z,hok + ld a,(hi) + add a,h + ld h,a +hok: inc de + call defp1 + ld l,a + dec h + dec l + jr dochk1 +vmarg: call defp1 + ld c,a + inc de + ld a,(de) + or a + jr nz,look1 + ld a,24 +look1: ld b,a + ld hl,[24 << 8] + 1 + sbc hl,bc + jr nz,chkbc + ld b,c + jr setbc +chkbc: ld a,c + cp b + ret nc +setbc: dec b + dec c + ld (hi),bc + ld hl,0 + jr dochk1 +vmode: ld hl,(pp) + ld a,(hl) +setdb: ld (mm),a + or a + jp nz,dim + jp bright +vsm: call dofbit + ld a,(fbits) + ld hl,flags + or (hl) + ld (hl),a + ret +vrm: call dofbit + ld a,(fbits) + cpl + ld hl,flags + and (hl) + ld (hl),a + ret + +chk1: or a + jp p,chkp + xor a + ret +chkp: cp e + ret c + ld a,e + ret + +defp1: ld a,(de) + or a + ret nz + inc a + ret + +scrlvt: ld hl,(hi) + ld a,l + cp h + ld l,23 + jr nz,jproll + ld h,l +jproll: jp rollit + +revsvt: ld a,(lo) + or a + jr z,norevd + cp 23 + jr z,norevd + ld h,a + ld l,0 + call moveto + call dellin +norevd: ld hl,(hi - 1) + ld l,0 + call moveto + jp inslin + +.useg +inits: +state: ds 1 +hi: ds 1 +lo: ds 1 +flags: ds 1 +sx: ds 1 +sy: ds 1 +mm: ds 1 +sm: ds 1 +nk: ds 1 +inite: +x: ds 1 +y: ds 1 + +setv: ds 1 +fbits: ds 1 +p1: ds 1 +p2: ds 1 +p3: ds 1 +pp: ds 2 + \ No newline at end of file diff --git a/source/XMODEM.I b/source/XMODEM.I new file mode 100644 index 0000000..b5a4b49 --- /dev/null +++ b/source/XMODEM.I @@ -0,0 +1,34 @@ +; xmodem.i - header file for xmodem file transfer code + +.var FALSE 0 +.var TRUE 1 + +; ASCII Constants + +.var SOH 001 +.var STX 002 +.var ETX 003 +.var EOT 004 +.var ENQ 005 +.var ACK 006 +.var LF '\n' +.var CR '\r' +.var NAK 025 +.var SYN 026 +.var CAN 030 +.var ESC '\e' + +; XMODEM Constants + +.var TIMEOUT -1 +.var ERRMAX 20 +.var NAKMAX 2 +.var RETRMX 8 +.var CRCSW 3 +.var KSWMAX 5 +.var BBUFSIZ 1024 +.var NAMSIZ 11 +.var CTRLZ 032 +.var CRCCHR 'C' +.var BADNAM 'u' + \ No newline at end of file diff --git a/source/XUTIL.Z b/source/XUTIL.Z new file mode 100644 index 0000000..8d3a1ad --- /dev/null +++ b/source/XUTIL.Z @@ -0,0 +1,178 @@ +; xutil.z - xmodem utilities + +.incl "c:xmodem" + +.extern initx ; set up an xmodem file transfer +initx: + ld hl,xabort + ld (abortf),hl ; set the abort function + ld hl,xmstr + jp initsc ; just set up screen with 'Xmodem' string +.dseg +xmstr: db 'Xmodem\0' +.cseg + +.extern xmflgs ; parse flags for an xmodem send +xmflgs: + call clrflg + ld a,1 + ld (crcmod),a ; but set crc flag +xmflp: ld a,(hl) + or 0x20 + cp 0x20 + ret z ; return on end of string or space + inc hl + cp 'b' ; 'b' - modem7 batch + jr nz,noxmb + ld (mdm7b),a ; set modem7 batch flag + xor a + ld (ymdmb),a ; clear ymodem batch (can't have both) + jr xmflp +noxmb: cp 'y' ; 'y' - ymodem batch + jr nz,noxmy + ld (ymdmb),a ; set ymodem batch flag + xor a + ld (mdm7b),a ; clear modem7 batch (can't have both) + jr xmflp +noxmy: cp 'k' ; 'k' - 1K packets + jr nz,noxmk + ld (longpk),a ; set the flag + jr xmflp +noxmk: cp 'q' ; 'q' - quiet value + jr nz,noxmq + ex de,hl + ld hl,qcount + inc (hl) ; bump the count + ex de,hl + jr xmflp +noxmq: cp 'a' ; 'a' - alarm + jr nz,noxma + ld (beep),a ; set flag to beep on completion + jr xmflp +noxma: xor 'c' ; 'c' - checksum mode + jr nz,xmflp + ld (crcmod),a ; set to checksum + jr xmflp + +.extern updcrc ; update crc with value in a +updcrc: + ld c,a ; save byte in c + call dou1 +dou1: ld hl,chksum + ld a,c + rrca + rrca + rrca + rrca + ld c,a + xor a + rld + inc hl + rld + xor c + and 0x0f + add a,a + push hl + ld l,a + ld h,0 + ld de,crctab + 1 + add hl,de + ex de,hl + pop hl + ld a,(de) + dec de + xor (hl) + ld (hl),a + dec hl + ld a,(de) + xor (hl) + ld (hl),a + ret + +.extern can3 ; send 5 CAN's to far end to force it to stop +can3: ; used to be three, but with Zmodem systems + ld b,5 ; sometimes more are needed +can3lp: push bc + ld a,CAN + call modopc + pop bc + djnz can3lp + ret + +.extern chkcan ; called when we get a CAN - checks for a +chkcan: ; second right behind it + call gettc3 ; wait 3 seconds + cp CAN + ret nz ; no - didn't get one + call diag + db 'Cancelled by remote\0' + xor a + ret + +.extern rnresp ; beef about failure of far end to respond +rnresp: + call diag + db 'Remote not responding\0' + ret + +.extern cvtnib ; convert a nibble at the bottom of a to a +cvtnib: ; character + and 0x0f ; get just the bits we want + add a,'0' + cp '9' + 1 ; check if it's a digit ok + ret c ; yes - return as is + add a,0x27 ; else conver to a letter + ret + +xabort: call gettc3 + jr nc,xabort ; let the line cool down + call can3 + jp dabort + +.useg +.extern olp +olp: ds 1 +.extern attmps +attmps: ds 1 +.extern closeo +closeo: ds 1 +.extern fatal +fatal: ds 1 +.extern errorf +errorf: ds 1 +.extern batch +batch: ds 1 +.extern secip +secip: ds 1 +.extern eotc +eotc: ds 1 +.extern openfl ; \ +openfl: ds 1 ; \ Bound +.extern sectnm ; / +sectnm: ds 2 ; / +.extern bufsiz +bufsiz: ds 2 +.extern chksum +chksum: ds 2 + +.extern expsec +expsec: ds 2 +.extern sntsec +sntsec: ds 2 +.extern xbuff +.extern packet +xbuff: +packet: + ds 1024 + +.dseg +crctab: + dw 0x0000, 0x1021 + dw 0x2042, 0x3063 + dw 0x4084, 0x50a5 + dw 0x60c6, 0x70e7 + dw 0x8108, 0x9129 + dw 0xa14a, 0xb16b + dw 0xc18c, 0xd1ad + dw 0xe1ce, 0xf1ef + \ No newline at end of file