]> cloudbase.mooo.com Git - kermit-80.git/blame - cpxsb.asm
Bugfix in outmdm (output buffer flush)
[kermit-80.git] / cpxsb.asm
CommitLineData
e58a7a25
L
1IF NOT lasm\r
2.printx * CPXSB.ASM *\r
3ENDIF ;NOT lasm\r
4; KERMIT - (Celtic for "FREE")\r
5;\r
6; This is the CP/M-80 implementation of the Columbia University\r
7; KERMIT file transfer protocol.\r
8;\r
9; Version 4.0\r
10;\r
11; Copyright June 1981,1982,1983,1984,1985\r
12; Columbia University\r
13;\r
14; Originally written by Bill Catchings of the Columbia University Center for\r
15; Computing Activities, 612 W. 115th St., New York, NY 10025.\r
16;\r
17; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,\r
18; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many\r
19; others.\r
20;\r
21; This file created 16 July, 1987 by OBSchou from code submitted\r
22; by William Rose for the Micromint SB180 systems (as featured in\r
23; BYTE magazine, 1986). This file has been modified to fit in with\r
24; Kermit-80 V4.08 etc. His file KERSYS.ASM is a stripped down version\r
25; of CPXSYS.ASM (formerly CP4SYS.ASM).\r
26;\r
27; revision history:\r
28; KERSYS.ASM - version 0.8 dated 13 Jul 87.\r
29;\r
30; Cutdown CP4SYS.ASM for SB-180/Ampro 230.\r
31;\r
32;\r
33; While this is a single CPU version (to ease editing) the assembler\r
34; conditionals have been kept to identify machine specific code.\r
35;\r
36; Note that the baud setting routine also sets parity, but this does not\r
37; change the parity given by Kermit's 'stat' command. I assume that the\r
38; main body of the program does its own parity check.\r
39;\r
40; Revision history (last entry first)\r
41;\r
42; edit 2, 22 July by OBSchou to massage file to fit with CPXCOM.ASM.\r
43;\r
44; edit 1, 15 July, 1987 by OBSchou for William Rose who submitted\r
45; the code for Kermit-80 V 4.05. Modified code as appropriate \r
46; for 4.08 compatability.\r
47;\r
48\r
49delfac EQU 150 ; Delay factor in SB-180 input loop - a fudge\r
50\r
51;\r
52; Keep module name, edit number, and last revision date in memory.\r
53;\r
54;sysedt: db 'KERSYS.ASM (03) 12-FEB-87 $' ; last SB-180 revision\r
55;sysedt: db 'KERSYS.ASM (04) 12-APR-87 $' ; Telecom Merlin added\r
56;sysedt: db 'KERSYS.ASM (5) 9-May-87 $' ; Minor tidying\r
57;sysedt: db 'KERSYS.ASM (6A) 17-Jun-87 $' ; BT Merlin M2215 only\r
58;sysedt: db 'KERSYS.ASM (7) 19-Jun-87 $' ; SB-180 only\r
59;sysedt: db 'KERSYS.ASM (8) 13-Jul-87 $' ; 6/9 MHz version\r
60family: db 'CPXSB.ASM (2) 22-Jul-87$' ; First entry for V4.08/9\r
61\r
62;\r
63; Assembly time message to let me know I'm building the right version.\r
64;\r
65\r
66IF sb180\r
67.printx * Assembling Kermit-80 for Micromint SB-180 *\r
68ENDIF\r
69\r
70\r
71IF sb180\r
72mnctrla EQU 000H ;Modem control port - CNTLA0\r
73mnctrlb EQU 002H ;Modem control port - CNTLB0\r
74mnstat EQU 004H ;Modem status port - STAT0\r
75mntxdat EQU 006H ;Modem output port - TDR0\r
76mnrddat EQU 008H ;Modem input port - RDR0\r
77output EQU 002H ;Transmit data register empty mask - TDRE\r
78input EQU 080H ;Receive data register full mask - RDRF\r
79z80 EQU TRUE ;This one's an HD64180, but Z80 will do \r
80ENDIF\r
81\r
82\r
83sysxin: ; continuation of system initialisation from sysinit\r
84\r
85IF sb180\r
86 lxi h, porbuf ; park the original settings\r
87 db 0EDh, 038h, mnctrla ; HD64180 code IN0 g,(m)\r
88 mov m, a\r
89 inx h\r
90 db 0EDh, 038h, mnctrlb\r
91 mov m, a\r
92 inx h\r
93 db 0EDh, 038h, mnstat\r
94 mov m, a\r
95ENDIF\r
96 ; re-initialise for KERMIT\r
97IF sb6\r
98 mvi h, 08h ; 0000$1001 - 9600 baud, (even) parity\r
99 mvi l, 08h ; 'speed' is two bytes\r
100ENDIF\r
101\r
102IF sb9\r
103 mvi h, 21h ; 0010$0001 - 9600 baud, (even) parity\r
104 mvi l, 21h ; 'speed' is two bytes\r
105ENDIF\r
106\r
107IF sb180\r
108 shld speed\r
109 lxi h, parind\r
110 mvi m, 8 ; index for 8 bits, no parity, 2 stop\r
111 call setpor\r
112ENDIF\r
113\r
114 ret\r
115\r
116porbuf: ds 3 ; original port settings\r
117 \r
118;\r
119; system-dependent KERMIT termination processing\r
120; If we've changed anything, this is our last chance to put it back.\r
121;\r
122sysexit:\r
123\r
124IF sb180\r
125 lxi h, porbuf\r
126 mov a, m ; output parity\r
127 db 0EDh, 039h, mnctrla ; HD64180 code OUT0 (m),g\r
128 inx h\r
129 mov a, m ; output baud rate\r
130 db 0EDh, 039h, mnctrlb\r
131 inx h\r
132 mov a, m ; output to clear error flags \r
133 db 0EDh, 039h, mnstat\r
134 ; read twice to reset DCD0 ?\r
135 db 0EDh, 038h, mnstat\r
136 db 0EDh, 038h, mnstat\r
137ENDIF\r
138\r
139 ret\r
140\r
141;\r
142; system-dependent processing for start of CONNECT command\r
143;\r
144syscon:\r
145 ret\r
146\r
147conmsg: ; Messages printed when entering transparent (CONNECT) mode:\r
148\r
149 db '$'\r
150\r
151;\r
152; syscls - system-dependent close routine\r
153; called when exiting transparent session.\r
154;\r
155syscls:\r
156 ret\r
157\r
158;\r
159; sysinh - help for system-dependent special functions.\r
160; called in response to <escape>?, after listing all the\r
161; system-independent escape sequences.\r
162;\r
163sysinh:\r
164\r
165IF sb180 \r
166 lxi d, inhlps\r
167 call prtstr\r
168ENDIF\r
169\r
170 ret\r
171\r
172; Additional, system-dependent help for transparent mode\r
173; (two-character escape sequences)\r
174inhlps:\r
175\r
176IF sb180\r
177 db cr, lf, 'V Cycle port parameters'\r
178ENDIF\r
179\r
180 db '$' ; string terminator\r
181\r
182;\r
183; sysint - system dependent special functions\r
184; called when transparent escape character has been typed;\r
185; the second character of the sequence is in A (and in B).\r
186; returns:\r
187; non-skip: sequence has been processed\r
188; skip: seqence was not recognized\r
189;\r
190sysint:\r
191; ani 137O ; convert lower case to upper, for testing...\r
192 ; does this work?\r
193IF sb180\r
194 cpi 'V' ; cycle port ?\r
195 jz pcycl\r
196 cpi 'v'\r
197 jz pcycl\r
198ENDIF\r
199\r
200 jmp rskp ; take skip return - command not recognised\r
201\r
202; Actual commands\r
203\r
204IF sb180\r
205pcycl:\r
206 lxi h, parind ; increment parval, modulo 12\r
207 mov a, m\r
208 adi 1\r
209 cpi 13\r
210 jnz pcy1\r
211 mvi a, 1\r
212pcy1: mov m, a ; update the storage\r
213 ; get index of name in parstr\r
214 ora a ; clear flags\r
215 dcr a\r
216 rlc\r
217 rlc\r
218 mov c, a\r
219 mvi b, 0\r
220 lxi h, parstr\r
221 inx h\r
222 dad b\r
223 push h\r
224 lxi d, cgmsg1\r
225 call prtstr\r
226 pop d\r
227 call prtstr\r
228 lxi d, cgmsg2\r
229 call prtstr\r
230 call setpor ; reset the port\r
231\r
232 ret\r
233\r
234cgmsg1: db '<$'\r
235cgmsg2: db '>$'\r
236ENDIF\r
237\r
238 ret\r
239\r
240;\r
241; Delay routine. Called with time (hundredths of seconds) in A.\r
242; The inner loop delays 1001 T-states, assuming no wait states are\r
243; inserted; this is repeated CPUSPD times, for a total delay of just\r
244; over 0.01 second. (CPUSPD should be set to the system clock rate,\r
245; in units of 100KHz: for an unmodified Kaypro II, that's 25 for\r
246; 2.5 MHz. Some enterprising soul could determine whether or not the\r
247; Kaypro actually inserts a wait state on instruction fetch (a common\r
248; practice); if so, the magic number at delay2 needs to be decreased.\r
249; (We also neglect to consider time spent at interrupt level).\r
250;\r
251; called by: sendbr\r
252; destroys BC\r
253;\r
254;delay: mvi c,cpuspd ; Number of times to wait 1000 T-states to\r
255; ; make .01 second delay\r
256;delay2: mvi b,70 ; Number of times to execute inner loop to\r
257; ; make 1000 T-state delay\r
258;delay3: dcr b ; 4 T-states (* 70 * cpuspd)\r
259; jnz delay3 ; 10 T-states (* 70 * cpuspd)\r
260; dcr c ; 4 T-states (* cpuspd)\r
261; jnz delay2 ; 10 T-states (* cpuspd)\r
262; ; total delay: ((14 * 70) + 14) * cpuspd\r
263; ; = 1001 * cpuspd\r
264; dcr a ; 4 T-states\r
265; jnz delay ; 10 T-states\r
266;\r
267; ret ; grand total: ((1001 * cpuspd) + 14) * a\r
268\r
269;\r
270; sysflt - system-dependent filter\r
271; called with character in E.\r
272; if this character should not be printed, return with A = zero.\r
273; preserves bc, de, hl.\r
274; note: <xon>,<xoff>,<del>, and <nul> are always discarded.\r
275;\r
276sysflt:\r
277 mov a,e ; get character for testing\r
278 ret\r
279\r
280;\r
281; mdmflt - modem filter\r
282; called with character to be sent to printer in E\r
283; with parity set as appropriate.\r
284; return with accumulator = 0 do do nothing,\r
285; <> 0 to send char in E.\r
286mdmflt:\r
287 mov a,e ; get character to test\r
288 ret\r
289\r
290;\r
291; prtflt - printer filter\r
292; called with character to be sent to printer in E\r
293; returns with a = 0 to do nothing\r
294; a <> 0 to print it.\r
295;\r
296; this routine for those printer that automatically insert\r
297; a lf on cr, or cr for lf. Should this be shifted to \r
298; the system indep. stuff, in say 4.06?\r
299;\r
300prtflt:\r
301 mov a,e ; get character to test\r
302\r
303IF FALSE ; strip out lf from printer stream\r
304 ani 7fh ; make sure it is parity less\r
305 cpi lf ; is it a line feed?\r
306 rnz ; no, print it\r
307; xra a ; yes, don't.\r
308 \r
309ENDIF\r
310\r
311 ret\r
312\r
313;\r
314; system-dependent processing for BYE command.\r
315;\r
316sysbye:\r
317 ret\r
318\r
319;\r
320; This is the system-dependent command to change the baud rate.\r
321; DE contains the two-byte value from the baud rate table; both\r
322; bytes of this value are also stored in 'speed'.\r
323;\r
324sysspd:\r
325\r
326IF sb180\r
327 lxi d, prtmsg ; ask for variables\r
328 call prtstr\r
329\r
330 lxi d, tbuf ; get suitable string\r
331 mvi c, 10\r
332 call bdos\r
333\r
334 lxi h, tbuf1\r
335 mov a, m\r
336 ora a\r
337 jz setpor ; leave unchanged if string zero length\r
338\r
339 cpi 3 ; check given length\r
340 jnz spd1 ; error - wrong length\r
341 inx h\r
342 inx h\r
343 mov a, m\r
344 ani 137O ; convert parity code to upper case\r
345 mov m, a\r
346\r
347 lxi d, tbuf1 ; get index of given parameter\r
348 lxi h, parstr\r
349 call sposn\r
350 ora a\r
351 jnz spd2 ; or fall through if error\r
352spd1: lxi d, invmsg ; invalid input - try again\r
353 call prtstr\r
354 jmp sysspd\r
355\r
356spd2: adi 3 ; get index to parval table\r
357 rrc ; by dividing by 4 \r
358 rrc\r
359 ani 15 ; mask out high bits\r
360 lxi h, parind\r
361 mov m, a ; and store it\r
362 call setpor ; set up port iaw index and speed bytes\r
363\r
364 ret\r
365\r
366prtmsg: db cr,lf,'Enter bit/char, parity, and stop bits required.'\r
367 db cr,lf,'(Bit 7/8 Parity N/O/E Stop 1/2 - CR same) : $'\r
368invmsg: db cr,lf,'Invalid parameters$'\r
369\r
370parind: db 8 ; default <8N2> index\r
371parstr: db 48,'7N1$7N2$7O1$7O2$7E1$7E2$'\r
372 db '8N1$8N2$8O1$8O2$8E1$8E2$'\r
373parval: db 0,1,16+2,16+3,2,3\r
374 db 4,5,16+6,16+7,6,7\r
375tbuf db 6\r
376tbuf1 db 3,'8N2','$$$$'\r
377\r
378;\r
379; Set up the port using the table index in parind and the speed byte\r
380;\r
381setpor:\r
382 lxi h, parind\r
383 mov a, m\r
384 dcr a\r
385 lxi h, parval ; table base\r
386 mvi b, 0\r
387 mov c, a\r
388 dad b ; HL points at parameter value\r
389 mov a, m\r
390 mov b, a ; park parval\r
391\r
392 ani 16 ; the parity switch bit\r
393 lxi h, speed\r
394 add m ; this is now the baud rate byte\r
395 mov c, a ; park it\r
396\r
397 mov a, b ; sort out the parameter byte\r
398 ani 7 ; b/p/s only wanted\r
399 adi 96 ; RE, TE enable\r
400 db 0EDh,039h,mnctrla ; output parity etc.\r
401 mov a, c\r
402 db 0EDh,039h,mnctrlb ; output baud rate\r
403 mvi a, 0\r
404 db 0EDh,039h,mnstat ; clear status\r
405 db 0EDh,038h,mnstat ; read twice to reset DCD0\r
406 db 0EDh,038h,mnstat\r
407 ret\r
408\r
409;\r
410; Find substring position - Leventhal page 293, modified\r
411; enter with subtring in DE and string in HL\r
412; returns index in A or 0 for failure\r
413;\r
414sposn:\r
415 mov a, m ; exit if either string length zero\r
416 ora a\r
417 jz notfnd\r
418 sta slen\r
419 mov b, a\r
420 inx h\r
421 shld string\r
422 xchg\r
423 mov a, m\r
424 ora a\r
425 jz notfnd\r
426 sta sublen\r
427 mov c, a\r
428 inx h\r
429 shld substg\r
430 mov a, b\r
431\r
432; no of searches = stringlen - substrlen + 1\r
433; if substr longer than string quit immediately\r
434\r
435 sub c\r
436 jc notfnd\r
437 inr a\r
438 mov c, a\r
439 xra a\r
440 sta index\r
441\r
442; search until remaining string shorter than substring\r
443\r
444slp1: lxi h, index\r
445 inr m\r
446 lda sublen\r
447 mov b, a\r
448 lhld substg\r
449 xchg\r
450 lhld string\r
451\r
452; try to match substring starting at index\r
453\r
454cmplp: ldax d\r
455 cmp m\r
456 jnz slp2\r
457 dcr b\r
458 jz found\r
459 inx h\r
460 inx d\r
461 jmp cmplp\r
462\r
463; arrive here if match fails\r
464\r
465slp2: dcr c\r
466 jz notfnd\r
467 lhld string\r
468 inx h\r
469 shld string\r
470 jmp slp1\r
471\r
472; found, return index\r
473\r
474found: lda index\r
475 ret\r
476\r
477; not found, return zero\r
478\r
479notfnd: sub a\r
480 ret\r
481\r
482string: ds 2\r
483substg: ds 2\r
484slen: ds 1\r
485sublen: ds 1\r
486index: ds 1\r
487ENDIF\r
488\r
489 ret\r
490\r
491;\r
492; Speed tables\r
493; (Note that speed tables MUST be in alphabetical order for later\r
494; lookup procedures, and must begin with a value showing the total\r
495; number of entries. The speed help tables are just for us poor\r
496; humans.\r
497;\r
498; db string length, string, divisor (2 bytes or 1 word, ab)\r
499; the data byte a is return in A and E, and b in D\r
500; only byte 'a' is the key for the table\r
501\r
502IF sb6\r
503spdtbl: db 9 ; 9 entries\r
504 db 04h,'1200$', 0Bh,0Bh\r
505 db 03h,'150$', 0Eh,0Eh\r
506 db 05h,'19200$', 01h,01h\r
507 db 04h,'2400$', 0Ah,0Ah\r
508 db 03h,'300$', 0Dh,0Dh\r
509 db 05h,'38400$', 00h,00h\r
510 db 04h,'4800$', 09h,09h\r
511 db 03h,'600$', 0Ch,0Ch\r
512 db 04h,'9600$', 08h,08h\r
513\r
514sphtbl: db cr,lf\r
515 db ' 150 300 600 1200 2400 4800 9600 19200 38400$'\r
516ENDIF\r
517\r
518IF sb9\r
519spdtbl: db 7 ; 7 entries\r
520 db 04h,'1200$', 24h,24h\r
521 db 05h,'19200$', 20h,20h\r
522 db 04h,'2400$', 23h,23h\r
523 db 03h,'300$', 26h,26h\r
524 db 04h,'4800$', 22h,22h\r
525 db 03h,'600$', 25h,25h\r
526 db 04h,'9600$', 21h,21h\r
527\r
528sphtbl: db cr,lf\r
529 db ' 300 600 1200 2400 4800 9600 19200$'\r
530ENDIF\r
531\r
532\r
533;\r
534; This is the system-dependent SET PORT command.\r
535; HL contains the argument from the command table.\r
536;\r
537sysprt:\r
538 ret\r
539\r
540IF sb180\r
541prttbl EQU 0 ; SET PORT is not supported\r
542prhtbl EQU 0\r
543ENDIF\r
544\r
545;\r
546; selmdm - select modem port\r
547; selcon - select console port\r
548; selmdm is called before using inpmdm or outmdm;\r
549; selcon is called before using inpcon or outcon.\r
550; For iobyt systems, diddle the I/O byte to select console or comm port;\r
551; For the rest, does nothing.\r
552; preserves bc, de, hl.\r
553;\r
554selmdm:\r
555 ret\r
556\r
557selcon:\r
558 ret\r
559\r
560;\r
561; Get character from console, or return zero.\r
562; result is returned in A. destroys bc, de, hl.\r
563;\r
564inpcon:\r
565 mvi c,dconio ;Direct console I/O BDOS call.\r
566 mvi e,0FFH ;Input.\r
567 call BDOS\r
568\r
569 ret\r
570\r
571;\r
572; Output character in E to the console.\r
573; destroys bc, de, hl\r
574;\r
575outcon:\r
576 mvi c,dconio ;Console output bdos call.\r
577 call bdos ;Output the char to the console.\r
578\r
579 ret\r
580\r
581;\r
582; outmdm - output a char from E to the modem.\r
583; the parity bit has been set as necessary.\r
584; returns nonskip; bc, de, hl preserved.\r
585;\r
586outmdm:\r
587\r
588IF sb180\r
589 db 0EDh,038h,mnstat\r
590 ani output ; check status\r
591 jz outmdm ; wait until port is available\r
592 mov a, e\r
593 db 0EDh,039h,mntxdat ; transmit\r
594ENDIF\r
595\r
596 ret\r
597\r
598;\r
599; get character from modem; return zero if none available.\r
600; for IOBYT systems, the modem port has already been selected.\r
601; destroys bc, de, hl.\r
602;\r
603inpmdm:\r
604\r
605IF sb180\r
606 lxi h, delfac ; loops to give delay\r
607inpm1: db 0EDh,038h,mnstat\r
608 ani input ; check status \r
609 jz inpm2\r
610 db 0EDh,038h,mnrddat ; get a byte\r
611 ret\r
612\r
613inpm2: dcx h ; no data\r
614 mov h, a\r
615 ora l\r
616 jnz inpm1 ; still tries left\r
617 ret ; with zero in A\r
618ENDIF\r
619\r
620 ret\r
621\r
622;\r
623; flsmdm - flush comm line.\r
624; Modem is selected.\r
625; Currently, just gets characters until none are available.\r
626;\r
627flsmdm:\r
628 call inpmdm ; Try to get a character\r
629 ora a ; Got one?\r
630 jnz flsmdm ; If so, try for another\r
631 ret ; Receiver is drained. Return.\r
632\r
633;\f\r
634\r
635;\r
636; lptstat - get the printer status. Return a=0 if ok, or 0ffh if not.\r
637lptstat:\r
638IF iobyte ;[33]\r
639 call bprtst ; get status\r
640ENDIF ;iobyte[33]\r
641IF NOT iobyte ;[33]\r
642 xra a ; assume it is ok.. this may not be necessary\r
643ENDIF ;iobyte [33]\r
644 ret\r
645;\r
646; outlpt - output character in E to printer\r
647; console is selected.\r
648; preserves de.\r
649;\r
650outlpt:\r
651 push d ; save DE in either case\r
652 call prtflt ; go through printer filter [30]\r
653 ana a ; if A = 0 do nothing,\r
654 jz outlp1 ; if a=0 do nothing\r
655\r
656outlp1: pop d ; restore saved register pair\r
657 ret\r
658\r
659; delchr - make delete look like a backspace. Unless delete is a printing\r
660; character, we just need to print a backspace. (we'll output clrspc\r
661; afterwards)\r
662delchr:\r
663\r
664 mvi e,bs ;get a backspace\r
665 jmp outcon\r
666\r
667; erase the character at the current cursor position\r
668clrspc: mvi e,' '\r
669 call outcon\r
670 mvi e,bs ;get a backspace\r
671 jmp outcon\r
672\r
673; erase the current line\r
674clrlin: lxi d,eralin\r
675 jmp prtstr\r
676\r
677; erase the whole screen, and go home. preserves b (but not c)\r
678clrtop: lxi d,erascr\r
679 jmp prtstr\r
680\r
681IF sb180\r
682sysver: db 'MicroMint SB 180 '\r
683ENDIF\r
684\r
685IF sb6\r
686 db ' (6 MHz)'\r
687ENDIF\r
688\r
689IF sb9\r
690 db ' (9 MHz)'\r
691ENDIF\r
692 db '$'\r
693\r
694IF lasm\r
695LINK CPXVDU.ASM ; get terminal defs etc\r
696ENDIF ;lasm\r