1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
|
; CPSUTL.ASM
; KERMIT - (Celtic for "FREE")
;
; This is the CP/M-80 implementation of the Columbia University
; KERMIT file transfer protocol.
;
; Version 4.0
;
; Copyright June 1981,1982,1983,1984,1985
; Columbia University
;
; Originally written by Bill Catchings of the Columbia University Center for
; Computing Activities, 612 W. 115th St., New York, NY 10025.
;
; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
; others.
;
; Utility routines, pure and impure data.
;
; revision history:
;
;edit 31, 21-Mar-1991 by MF. Implement edit 30 without checking takflg after
; "r1tch1" as we are **always** TAKEing from a file if we get to that
; point in the code. Makes for simplicity.
;edit 30, 27-Feb-1991 by MF. When TAKEing characters from a TAKE-file,
; view semicolons as normal characters (not command separators).
; This will allow such commands as REMOTE DELETE *.*;* to work
; properly from TAKE-files without having to revert to old code in
; cpsrem.asm at "remcl0" to decode Remote command arguments. TAKE-files
; ought not (in my opinion) to have multiple commands per line anyway.
;edit 29, 30-Jan-1991 by MF. Fix bug in IN1CHR which decremented "chrcnt"
; once too often after a call to INBUF (which predecrements it
; already). This, along with a fix in CPSTT.ASM, fixes a bug in the
; TRANSMIT command wherein certain characters in the file were not
; being transmitted. This bug was reported to me by Lance Tagliapietra
; of the University of Wisconsin at Platteville, WI (Email:
; <TAGLANCE@ucs.UWPLATT.EDU>).
; (he suggested not predecrementing "chrcnt" in INBUF" but this
; breaks code in routine "GTCHR" from CPSPK1.ASM so it's better
; to modify IN1CHR in this module and XMIT in CPSTT.ASM).
;edit 28, 30-Nov-1990 by MF. Modify routine "p20ln" to use "pausit" routine
; rather than explicitly checking for Console input to eliminate
; redundant code. Also fix spelling in "p20ln"'s comments.
;edit 27, 9-Sep-90 by MF. Put RET in routine PAUSIT per CPKERM.BWR.
; edit 26, September, 1987. Added pause-it routine to wait for a user keysroke.
;
; edit 25, August 19, 1987 by OBSchou. Fixed a few bugs here and there.
;
; edit 24, April 8, 1987 by OBSchou. Added routine to return one character
; from a section of several sectors worth of file. This routine needed
; for TRANSMIT.
;
; edit 23, March 11, 1986 by OBSchou for Richard Russell
; Bug in the TAKE code, such that a new sector was read in after 256
; bytes, and not the CPM value. A jnz is now jp in the test to see if
; the file buffer has bben exhausted. Many thanks for finding this
; bug. I have never used TAKE files more than 128 bytes long.
;
; edit 22, January 28, 1986 by OBSchou.
; split off the data areas from CPSUTL to CPSDAT.ASM (All in line
; with keeping individual files relatively small)
;
; edit 21 August, 1986 by OBSchou. Sorted a few more bugs in printer buffer
; etc. Have yet to try this with a real printer The code, apart from
; actually printing works OK.
;
; edit 26 20 August, 1986 by OBSchou for Godfrey Nix:
; edit 8-Aug-86 by Godfrey N. Nix [gnn] Nottingham University
; Added two extra bytes for storage of the send and receive
; start-of-packet characters. Used by CP4PKT, and altered
; by SET option in CP4MIT. Also added message strings for
; use by show routines. Added remote filename buffer and length byte.
;
; edit 11: June 20, 1986 by OBSchou. Added multi-fcbs for the DIR command
; together with some bug clearing and new routines. Had to move
; the overlay to 5000h as we ran out of space...
;
; edit 10: June 16, 1986 OBSchou. Added a pseudo clock and check for printer
; ready whenever one enters BDOS... This may slow things down a little
; but adds in (hopefully) pseudo background printing...
;
; edit 9 30-May-86 OBSchou. Added XON/XOFF routines here for the world
; at large to use. Also added two new entries in the overlay tables.
; One to give the address of the family of machines using the overlay,
; the other to the routine for giving printer status.
;
; edit 8: 27-May-86 OBSchou. Added code to check BDOS calls for info from
; the console. If so, and the take flag (takflg) is set then we
; substitute our own characters. Simple, a little tatty...
; Also added bits for SET CASE-SENSITIVE and SET FLOW=CONTROL, and
; removed the XMIT rubbish. This is a prelude to better TRANSMIT
;
; edit 7: 22 April, 1986 by OBSchou Lohghborough University
; Prlude to more changee, this time make overlay address to 4000h
; May revert back to ($+0ffh) AND 0ff00h as address for overlay.
; This gives us space to make quite a few modifications to the system
; dependent part without much fear of having to change this overlay
; address. Should also fix the Osborne problem of having to have io
; routines ii memory above 16k. I know I should not be introducing
; such system dependent rot here, but it wont be too difficult to fill
; memory to 4000h.
;
; edit 6: February 6, 1985
; Added a storage location for the port value (PORT, just below
; SPEED) which is used by the port status routine, and moved the
; printer copy flag (PRNFLG:) into the communications area so
; that the machine dependant overlay can toggle it. [Hal Hostetler]
; Added ffussy flag for filename checking. Generate the version
; string from 'verno', which is set in CP4KER, because CP4KER has the
; list of modules and their edit numbers. [Charles Carvalho]
;
; edit 5: 13-Jan-85 by Vanya J.Cooper Pima Commun. College Tel: 602-884-6809
;
;pcc002 28-Dec-84 modules:cp4tt,cp4utl
; Add connect mode <esc>P command to toggle printer on
; and off. Conflicts with "official" recommended commands
; in protocol manual, but I don't think CP/M will ever get
; a PUSH command.
;
;pcc003-pcc005 2-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
; These edits must all be installed together and change the way
; logging is handled. The log file spec is moved to a separate
; fcb, and not opened until an actual CONNECT command is given.
; This takes care of a NASTY bug that if you used any other file
; command between the LOG and CONNECT, the log file would get
; written over the last file used. This also allows logging to
; be "permanently" enabled until an CLOSE (new command) for all
; CONNECT sessions, like most other kermits do. If a log file
; already exists, it will be appended to. Also add two new
; CONNECT mode commands <esc>Q to suspend logging and <esc>R to
; resume. <esc>R means something else during TRANSMIT, but
; logging is never on then, so there shouldn't be any conflict.
; I also changed the write code, so that it can handle one more
; character after the XOFF is send to stop the host. This allows
; a little "slop" for systems that don't stop immediately (such
; as TOPS10), but it didn't help much.
;
;pcc006 2-jan-85 VJC modules:cp4cmd,cp4utl
; Problems with "?" in filespecs. On reparse, may cause action
; flag to be reset at wrong point, requiring multiple <CR>'s
; to terminate the line or other weird stuff. Also need to
; check flag and complain if wild-cards illegal.
;
;pcc008 2-Jan-85 vjc modules:cp4def,cp4tt,cp4utl
; Keyboard input during CONNECT mode can get locked out if
; there is enough input from the modem port to keep prtchr
; busy. This can happen for example, if the printer is running
; at the same speed as the modem line, leaving you helpless to
; turn it off or abort the host. Add a fairness count, so that
; at least every prfair characters we look at console input.
;
;pcc012 4-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
; Use the big buffer for the log file. Move the log file back
; into the common fcb and only save the drive, name, and
; extension between connects. Add new routines to cp4utl to
; create or append to an existing file, and to conditionally
; advance buffers only if in memory. Remove edit pcc003 that
; allows one more character after the xoff, since it didn't
; really work very well and does not fit in well with the way
; the buffer advancing routines are set up. If someone still
; thinks this would be useful, it could be put back in with a
; little more work.
;
; While testing this edit, I also noticed another bug that
; the command parsing routines do not limit or check the
; length of command lines or file specs, trashing what ever
; comes after them. Currently because of where the fcb and
; command buffer are located, this does not usually cause a
; problem, but could if an extremely long line was typed in,
; or in the future multiple fcbs defined elsewhere in memory
; were used. Maybe this should be put on the bug list
; somewhere.
;
;pcc013 8-Jan-85 vjc modules:cp4mit,cp4utl,cp4typ
; Replace CLOSE command to cancel session logging to SET
; LOGGING ON/OFF. This seems to fit in with the command
; structure better. Default the log file to KERMIT.LOG
; incase no previous LOG command. Logging is also enabled
; by LOG command, as before.
;
; edit 4: September 9, 1984
; Move command tables and associated help text to CP4MIT. Add
; makfil/clofil routines and modify outbuf to write files in big
; chunks. Update Kermit's version to 4.03.
;
; edit 3: August 21, 1984
; Make inbuf read files in big chunks to minimize disk start/stop
; delays. Buffer size and address is specified by system-dependent
; overlay.
;
; edit 2: August 3, 1984
; move "mover" to CP4SYS to allow use of Z80 block move instruction.
;
; edit 1: July 27, 1984
; extracted from CP4MIT.M80 edit 2, as part of LASM support. This is
; the last file linked for the system-independent code.
;
utlver: db 'CPSUTL.ASM (31) 21-Mar-1991$'
; Set the parity for a character in A.
; called by: spack, rexmit, logit, vt52, conchr, intchr
setpar: push h ;Save HL.
push b
lxi h,parity
mov c,m ;Get the parity routine.
mvi b,0
lxi h,parjmp ;Get the first address.
dad b
pchl
parjmp: jmp even
jmp mark
jmp none
jmp odd
jmp spacep
none: jmp parret ;Don't touch the parity bit.
even: ani 7FH ;Strip parity.
jpe parret ;Already even, leave it.
ori 80H ;Make it even parity.
jmp parret
mark: ori 80H ;Turn on the parity bit.
jmp parret
odd: ani 7FH ;Strip parity.
jpo parret ;Already odd, leave it.
ori 80H ;Make it odd parity.
jmp parret
spacep: ani 7FH ;Turn off the parity bit.
jmp parret
parret: pop b
pop h ;Restore HL.
ret
;
; Print the escape char.
; called by: stat01, telnet, intchr
escpr: lda escchr ;Get the escape char.
escpr1: cpi ' ' ;Is it a control char?
jp escpr2
push psw ; save the character
lxi d,inms10 ;Output "Control-".
call prtstr
pop psw ; restore the character
ori 100O ;De-controlify.
escpr2: mvi c,conout ;Output the char
mov e,a
call bdos
ret
; fetch keyword; if unsuccessful, return to command level.
; called by: kermit, setcom
keycmd: mvi a,cmkey
call comnd
jmp keycm2 ;no match
ret
keycm2: lxi d,ermes1 ;"Unrecognized Command"
call prtstr
jmp kermit ;Do it again.
; request confirmation; if unsuccessful, return to command level
; called by: bye, exit, help, log, setcom, show, status, send,
; finish, logout, telnet
cfmcmd: mvi a,cmcfm
call comnd
jmp kermt3 ;"Not confirmed"
ret
;
; This routine prints the number in HL on the screen in decimal.
; Uses all ACs.
; called by: cp4sys, read, send, updrtr, dir, user
nout: mvi a,'0' ; fill tempx with zeros
call filltmp
lxi b,-10 ;Get some useful constants.
nout1: lxi d,-1
nout2: dad b ;Subtract as many 10s as possible.
inx d ;Count them.
jc nout2 ;If some left keep going.
push h ;save remainder - 10
xchg ;Swap the remainder and the quotient.
mov a,h ;Get the number of 10s found.
ora l ;check for quotient non zero
cnz nout1 ;If non zero, recurse.
pop h ;Get the remainder - 10
mov a,l ;in a
adi '0'+10 ;Make the number printable and add the 10 back
call shiftmp ; cycle temp registers
sta temp1 ; and save digit
mov e,a ;Output the digit.
lda nquiet ; are we to be quiet?
ana a
rnz ; yup, so return here rather than frm bdos
mvi c,conout
jmp bdos
; prcrlf - print a CR/LF. (Saves no registers.) [Toad Hall]
; prtstr - print string pointed to by DE (now in overlay section.. see prtstx)
; called by: lots of places.
prcrlf: lxi d,crlf ;Point to the CR/LF
jmp prtstr ; Use the one in the overlay
; prtstx is funtionally the same as prtstr in the overaly but is here as
; we may need a print a string routine in case the overlay is
; either incorrect version or simply not present.
prtstx: ; PRTSTR moved to overlay, but we do need the
; same function for writing the sign-on
; message and error message if the overlay
;is not present. Thence prtstx
mvi c,prstr ; output string
jmp bdos ;a CALL followed by a RET becomes a JMP
; Jumping to this location is like retskp. It assumes the instruction
; after the call is a jmp addr.
; here from: many places.
rskp: pop h ;Get the return address.
inx h ;Increment by three.
inx h
inx h
pchl
; Jumping here is the same as a ret. 'jmp r' is used after routines
; that have skip returns, where the non-skip instruction must be 3 bytes.
; here from: many places.
r: ret
; Pause-it routine. Informs the user to press any key to continue
; and then waits for a key input. Called by the any routine
; with more than, say, 20 lines of output.
pausit: lxi d,anymes ; ask user to press any key to continue
call prtstr
pausi1: call ckcon ; see if a key typed
ana a
jz pausi1 ; loop until a key has been pressed.
ret
;
; Open a file for reading (with inbuf). The filename is already
; in fcb; upon return, the end of file flag is cleared and chrcnt
; is set to zero, so inbuf will be called to get a buffer when we
; next attempt to get a character.
; called by: sinit, seof
getfil: xra a
sta chrcnt ;Buffer is empty.
sta seccnt ;No sectors buffered.
sta eoflag ;Not the end of file.
sta endsts ;No EOF/error pending.
sta fcb+0CH ;Zero the extent.
sta fcb+0EH ;Must be zero for MAKEF or OPENF.
sta fcb+20H ;Zero the current record.
mvi c,openf ;Open the file.
lxi d,fcb
call bdos
ret
; Get next sector. If necessary, read some more from disk.
; preserves bc, de, hl
; returns nonskip if EOF or error;
; returns skip with chrcnt and bufpnt updated if success.
; called by: gtchr, get1xc (from xmt/transmit)
inbuf: lda eoflag ;Have we reached the end?
ora a
rnz ;Return if so.
push b
push d
push h
inbuf1: lda seccnt ; Do we have any sectors left?
ora a
jz inbuf3 ; If not, go get some more.
inbuf2: lhld nxtbuf ; Yes. Get address of next sector
shld bufpnt ; Update current buffer pointer
lxi b,bufsiz ; Get number of bytes in sector
dad b ; Update HL to point to next sector
shld nxtbuf ; Save for next time
dcr a ; Decrement count of buffered sectors
sta seccnt ; Put it back
mvi a,bufsiz-1 ; Number of bytes in buffer (pre-decremented)
sta chrcnt ; Store for our caller
pop h
pop d
pop b
jmp rskp ; Return success
; We don't have any sectors buffered. If we've already hit an error or
; EOF, return that status to the user.
inbuf3: lda endsts ; Check status from previous read
ora a
jz inbuf4 ; It was OK. Get some more sectors.
sta eoflag ; End of file or error. Set the flag.
xra a
sta chrcnt ; Say no characters in buffer.
pop h
pop d
pop b
ret ; Return failure
; Read sectors until we fill the buffer or get an error or EOF, then return
; the first buffer to the user. (seccnt is zero)
inbuf4: lhld bufadr ; Get address of big buffer
shld nxtbuf ; Store as next buffer address to give user
inbuf5: shld bufpnt ; Store as next buffer address to read into
xchg ; Move buffer address to DE
mvi c,setdma ; Tell CP/M where to put the data
call bdos ; ...
mvi c,readf ; Read a record.
lxi d,fcb
call bdos
sta endsts ; Save ending status
ora a ; 00H => read O.K
jnz inbuf6 ; EOF/error: stop reading.
lxi h,seccnt ; Success. Get addr of sector count
inr m ; Bump sector count by one
lda bufsec ; Get max number of sectors in buffer
cmp m ; Are we there yet?
jz inbuf7 ; Yes, can't read any more.
lhld bufpnt ; No, try for another. Get buffer address,
lxi d,bufsiz ; and size of sector,
dad d ; giving next buffer address in HL
jmp inbuf5 ; Go read another sector.
; We hit EOF or got an error. Put the DMA address back where it belongs,
; then go see if we have any sectors (before the one that got the error)
; to return to the caller. Nxtbuf points to the first good sector, if
; any; seccnt contains the count of good sectors.
inbuf6: call rstdma
jmp inbuf1 ; Go see if we have some data to return
; We've filled the big buffer. Reset the DMA address, then go return a
; sector to the caller. nxtbuf points to the beginning of the buffer;
; seccnt contains the number of sectors successfully read (except that
; if we've read 256 sectors, seccnt contains zero, so we can't just go
; to inbuf1).
inbuf7: call rstdma ;[pcc012]
lda seccnt ; Get sector count again.
jmp inbuf2 ; Return a sector.
; IN1CHR - get a single character from the file. Taken code from old
; TRANSMIT routine.
in1chr:
lda eoflag ;EOF encountered?
ora a
rnz ; Yes, finish.
lxi d,cmdbuf ; Use comnd buffer as line buffer.
lhld bufpnt ; Get current buffer pointer.
lda chrcnt ; Get current byte count
mov b,a ; in B
in1ch1: dcr b ; Assume there's a character there
jp in1ch2 ; If there was, proceed.
call inbuf ; There wasn't. Try for another buffer.
jmp in1che ; End of file.
lhld bufpnt ; Got another buffer. Get new pointer in HL
lda chrcnt ; and new byte count
mov b,a ; in B
;[MF]The modification below was made 30-Jan-1991 per report from
;[MF]Lance Tagliapietra from the University of Wisconsin at Platteville
;[MF]The following instruction should not be executed as the character counter
;[MF]has already been decremented by INBUF.
; dcr b ; we are reading in a character, so less one
in1ch2: mov a,b ; save new count
sta chrcnt
mov a,m ; Get a character from disk buffer.
inx h
shld bufpnt ; save new pointer
ani 7FH ; Mask 7 bits.
jz in1ch1 ; Skip nulls.
ret ; character in a
in1che: mvi a,0ffh ; dubious about this one...
sta eoflag
ret ; set end of file flag...?????
;;[pcc012]
; appfil - Create or append to an existing file. File name is in FCB.
; Non-skip return if could not be done. Skip return with file
; open and bufpnt pointing to end of file.
; called by logopn
appfil: xra a ;[pcc012] zero out stuff for open
sta fcb+0CH ;[pcc012] extent
sta fcb+0EH ;[pcc012] Must be zero for MAKEF or OPENF.
sta fcb+20H ;[pcc012] Zero the current record.
mvi c,openf ;[pcc012] Try to open the file
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
cpi 0FFH ;[pcc012] Did we find it?
jz makfi1 ;[pcc012] If not, go create it
mvi c,cflsz ;[pcc012] Compute the file size
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
lhld fcb+21H ;[pcc012] random record pointer
mov a,h ;[pcc012] See if zero length file
ora l ;[pcc012]
jz makfi2 ;[pcc012] set up pointers if null file
dcx h ;[pcc012] backup to last record written
shld fcb+21H ;[pcc012] store rec ptr back
lhld bufadr ;[pcc012] get buffer address
xchg ;[pcc012] to DE
mvi c,setdma ;[pcc012] set dma address
call bdos ;[pcc012] for read
mvi c,rrand ;[pcc012] read the last block
lxi d,fcb ;[pcc012]
call bdos ;[pcc012]
ora a ;[pcc012] check results
jnz rstdma ;[pcc012] reset dma and return if error
lhld bufadr ;[pcc012] get address again
lxi d,bufsiz ;[pcc012] and and size
mvi a,'Z'-40H ;[pcc012] control-Z for comparison
appcz: cmp m ;[pcc012] Is this the EOF?
jz appxit ;[pcc012] Jump if yes
inx h ;[pcc012] no, bump
dcr e ;[pcc012] and grind
jnz appcz ;[pcc012] until find or buffer empty
appxit: shld bufpnt ;[pcc012] store buffer pointer
dad d ;[pcc012] compute next buffer adr
shld nxtbuf ;[pcc012] and store
mov a,e ;[pcc012] updated chr count
sta chrcnt ;[pcc012]
xra a ;[pcc012] reset sector count
sta seccnt ;[pcc012]
call rstdma ;[pcc012] reset normal dma
jmp rskp ;[pcc012] and give good return
; Create a file, deleting any previous version. The filename is in
; fcb.
; Returns nonskip if file could not be created.
; If successful, takes skip return with bufpnt and chrcnt initialized
; for output; buffers should be output via outbuf.
; called by: gofil
makfil: mvi c,delf ; delete the file if it exists.
lxi d,fcb
call bdos
xra a
sta fcb+0CH ; zero the extent.
sta fcb+0EH ; must be zero for MAKEF or OPENF.
sta fcb+20H ; zero the current record.
;[pcc012] here from appfil above if file does not exist
makfi1: mvi c,makef ;[pcc012] now create it.
lxi d,fcb
call bdos
cpi 0FFH ; is the disk full?
rz ; take error return if so.
; success. set up pointers and counters for multisector buffering.
;[pcc012] also here from appfil if found zero length file
makfi2: lhld bufadr ;[pcc012] find beginning of buffer space.
shld bufpnt ; make it current buffer.
lxi d,bufsiz ; get sector size.
dad d ; find beginning of next buffer.
shld nxtbuf ; store for later.
mov a,e ; store buffer size
sta chrcnt ; for caller.
xra a
sta seccnt ; no sectors stored yet.
jmp rskp ; take success return.
;[pcc012]
; outadv - conditionally advance output buffer if disk write not needed.
; preserves BC
; skip return with with next output buffer set up
; non-skip return if memory buffer full and must write to disk.
; called by:logit
outadv: push b ;[pcc012] save BC as advertised
lxi h,seccnt ;[pcc012] point to sectors buffered
inr m ;[pcc012] count this one
lda bufsec ;[pcc012] how many we can hold
cmp m ;[pcc012] check if full
jnz outbf2 ;[pcc012] continue if not
dcr m ;[pcc012] full, un-advance sector count
pop b ;[pcc012] restore bc
ret ;[pcc012] and give non-skip return
; get a fresh output buffer, flushing big buffer if necessary.
; returns nonskip if disk full.
; if successful, returns skip with bufpnt and chrcnt updated. Note
; that chrcnt holds one less than the buffer size.
; preserves BC.
; called by: ptchr,logwrt
outbuf: push b
lxi h,seccnt ; count another buffered sector
inr m ; ...
lda bufsec ; get number of sectors we can hold
cmp m ; full?
jnz outbf2 ; if not, set up pointers and return
call outmbf ; flush the big buffer
jmp outbf9 ; disk error.
;[pcc012] also here from outadv to advance buffer
outbf2: lhld nxtbuf ; get pointer to fresh buffer
shld bufpnt ; store for caller
lxi d,bufsiz ; advance our pointer to next buffer
dad d
shld nxtbuf
mvi a,bufsiz-1 ; get buffer size (pre-decremented)
sta chrcnt ; store for caller
pop b
jmp rskp ; return success.
outbf9: pop b ; clean up stack
ret ; and take error return.
; flush incore output buffers.
; returns nonskip if disk full.
; if successful, returns skip with nxtbuf reset to start of buffer and
; seccnt zero.
; destroys all ac's.
; called by: outbuf, clofil.
outmbf: lhld bufadr ; get start of buffer
shld nxtbuf ; store for next fill cycle
shld bufpnt ; store for empty loop
outmb2: lhld bufpnt ; get address of current sector
xchg ; into DE
lxi h,bufsiz ; advance HL to next sector
dad d ; ...
shld bufpnt ; and store for later
mvi c,setdma
call bdos ; point CP/M at current sector
lxi d,fcb
mvi c,writef
call bdos ; output the sector
ora a ; test for error (A non-zero)
jnz rstdma ;[pcc012] reset dma and take nonskip return if so
lxi h,seccnt
dcr m ; count down buffered sectors
jnz outmb2 ; loop if more saved
call rstdma ;[pcc012] restore normal dma
jmp rskp ; return success.
; output current buffer, flush incore buffers, and close output file.
; returns nonskip if disk full; skip if successful.
; called by: rdata
clofil:
lda chrcnt ; get the number of chars left in the buffer.
cpi bufsiz ; Virgin buffer?
jz clofl3 ; yes, don't output it.
lhld bufpnt ; get the buffer pointer.
clofl1: dcr a ; lower the count.
jm clofl2 ; if full then stop.
mvi m,'Z'-100O ; put in a ^Z for EOF.
inx h ; point to the next space.
jmp clofl1
clofl2: call outbuf ; output the last buffer.
jmp r ; give up if the disk is full.
clofl3: lda seccnt ; any sectors buffered in memory?
ora a
jz clofl4 ; if not, don't try to flush.
call outmbf ; flush buffers
jmp r ; disk full.
clofl4: mvi c,closf ; close up the file.
lxi d,fcb
call bdos
jmp rskp ; return success.
; Reset DMA address to the default buffer
; called from inbuf,appfil,outmbf
rstdma: lxi d,buff ;[pcc012]
mvi c,setdma ;[pcc012]
jmp bdos ;[pcc012]
; [8] Intercept BDOS calls to check for console input
; This leads to simple trapping for input from disk rather than from the
; keyboard, alowing commands to be stored in a TAKE file.
; Printer is tested for readiness, and the second fuzzy clock is updated.
bdos: ;call print ; print a character to the printer if needed
;call clock ; update the clock
push psw ; we will need this register
lda takflg ; are we taking from a
ana a ; file or from command line
jz notake ; no, so do usual BDOS stuff
mov a,c ; get bdos function
cpi conin ; is it console in?
jz bd1in ; get a single character
cpi dconio ; direct console in or out?
jz bd1io ; test further for inpu or output
cpi rdstr ; read the console buffer?
jz bdcbuf ; then do it
cpi consta ; get the console status?
jz bdcst ; anything left in buffer?
notake: pop psw ; else we have a kosher BDOS call
jmp 0005h ; Absolute address = BDOS entry point
;
bd1in: ; get a single character from take file
pop psw ; restore stack
call r1tchr ; read a single take character
ret ; and return. We dont expand tabs,
; check for xon/off or backspaces.
;Make sure the take file is error free?
;
bd1io: ;get or put a single character from/to console
mov a,e ; get e. If 0ffh then input else output
inr a ; if 0 then input
jnz notake ; its for output, so let notake restore stack
pop psw ; otherwise we do it
call r1tchr ; read a single take character
ret ; and return from out BDOS
;
bdcbuf: ; read a line of edited (?) input from the console
pop psw ; restore stack
inx d ; point to nc
bdosc1: call r1tchr ; get a character
cpi cr ; if a cr then return
jz nomore
cpi lf ; ignore line feeds
jz bdosc1 ; so get another character
push psw ; we will want it later
mvi h,0
ldax d ; get nc, the no of characters in buffer
mov l,a ; now use as index from de+2
mvi h,0
inx d ; de is now de + 2
dad d
pop psw ; Told you we will want this
mov m,a
dcx d ; point again to nc
xchg ; make hl point to memory
inr m ; update pointer nc
xchg ; restore it
jmp bdosc1
nomore: dcx d ; restore de to point to buffer
ret
bdcst: ; get the console status. Returns a 00 if at eof
pop psw
push h ; now save de, hl for return
push d ;
lxi d,takdma ; make a point to next byte...
lhld takptr ; pointer from dma address. There will always
dad d ; be at least one byte, as the buffer is
mov a,m ; ...
pop d ; filled only if a read empties it.
pop h ; restore hl, de
cpi cntlz ; end of file?
mvi a,0ffh ; say there is
rnz ; if it is not a cntl z
jmp closet ; otherwise, close take file etc
r1tchr: ; read a single character from the take file or command line
push h
push d ; save in case of return
lda takflg ; see if character is to come from file or line
ani 1 ; if bit zero set, from take file
jz r1lchr ; get character from the command line
lxi d,takdma
lhld takptr ; get next data byte
dad d
mov a,m
lhld takptr ; cos it's destroyed with dad
inx h
shld takptr ; update pointer
call p1tchr ; print it (so the user sees it)
push psw ; save the read data for a while
mov a,l ; if l = 0 then read another sector
ana a
jp r1tch1 ;[23] was jnz. jp => 128 byte sectors
call rnsect ;read next sector
r1tch1: pop psw ; now, is this a cntl-z.. in whic case
pop d ; also these...
pop h
cpi lf ; skip if a line feed
jz r1tchr ;
;[MF][31][30]Following lines commented out so semicolons are not considered
;[MF][31][30]command separators and thus are considered part of the command
;[MF][31][30]so commands like REMOTE DELETE *.*;* work correctly.
; cpi semico ; see if its a semicolon
; jnz r1tch2 ; no, ignore it
; mvi a,cr ; else say its a cr (in case of command lines)
r1tch2: cpi cntlz ; end of file??
rnz
c1tchr: call closet ; close file etc, then
mvi a,cr ; fake a carriage return chr
; ( => clears kermit comnd line)
c1tch1: ret ; and hope that editing etc not required.
r1lchr: ; read a single character from the command line
lxi d,cbuff ; point to buffer
lda cbptr ; get pointer for next character
mov l,a
lda cbuff ; get total number of characters there
cmp l ; ... less current character
jp r1lch1 ; if positive, we have more characters
lda takflg ; no more, so reset command line bit (bit 4)
ani 0efh
sta takflg
mvi a,cntlz ; fudge an end of file
push psw ; save for common exit (r1tch1)
jmp r1tch1
;
r1lch1: mov a,l ; get count back again
mvi h,0
dad d ; get offset to character
inr a
sta cbptr
mov a,m ; get next character
cpi semico ; if a semicolon, make it a carriage return
jnz r1lch2
mvi a,cr
r1lch2: call p1tchr ; send a copy to the console
push psw ; save it for r1tch1
jmp r1tch1 ; common exit
; rnsect - read the next take sector from disk to the take dma address
; if there is no more then close the file too
rnsect: push b
push d
push h ; save in case we need these later
mvi c,setdma
lxi d,takdma ; set a next read from disk
call bdos ; recursive...
mvi c,readf
lxi d,takfcb
call bdos
ana a
cnz closet ; if returned value not zero, assume eof
lxi h,0
shld takptr ; pointer restored
call rstdma ; reset the dma address for fussy routines (this one)
pop h
pop d
pop b
ret
; closet - close the take file and set the take flag to 0 (ie no takes)
;
closet: lda takflg ; reset the take file bit (bit 0)
ani 0feh
sta takflg ; close the take file, and restore the flag
mvi c,closf
lxi d,takfcb
call bdos
call rstdma ; in case we did not do it above, reset dma
ret
;
; clock - is a 32 bit counter incremented every BDOS call. It serves as a
; timer of sorts and allows a background clock to tick away..
clock: push psw ; we need flags and hl
push h
lhld clkbit ; get the counter
inx h ;
shld clkbit
mov a,h
ora l ; do we need to update the next lot of clock bits?
jnz clockx
lhld clkbit+2 ; if carry up the top 16 bits
inx h
shld clkbit+2
clockx: pop h
pop psw
ret
; p1tchr - print a character in accumulator directly to the console
; bypassing the bdos trap above.
p1tchr: cpi lf ; if a lf ignore it
jz p1tchx
cpi cr ; ditto carriage returns
jz p1tchx
cpi cntlz ; control z
jz p1tchx ; then dont write it out
push psw ; we do not want to loose it, do we?
push b
push d
push h ; 'cos you never know what bdos does...
mov e,a
mvi c,conout ; direct console io
call 5 ; absolute address as we skip the trap
pop h
pop d
pop b ; ... and we need some of these regs.
pop psw ;
p1tchx: ret ; and return
; outprn - This routine sends charactes to the printer if the latter is ready,
; or to a buffer if the printer is not ready. If the buffer is nearly
; full, an XOFF is sent to the host, asking it to be quiet. The buffer
; is emptied by a series of calls in the connect state only.
; If the buffer is made nearly empty, then an XON is sent to the host.
;
outprn: mov a,e ; get the character to send back to a
sta prntmp ; we need all registers.
jmp outprx ; -testing-testing-testing- avoid buffer
;
outp0: call tstfree ; see how many spaces free
cpi 2 ; (free spaces in a on return)
jp outp1 ; enough free spaces, so keep going
call print ; else see if we can print summat
jmp outp0 ; and try again
;
outp1: cpi 4 ; common test - if three or less then send xoff
cm sndxoff
outpr2: mov a,b ; inc ptr and check for wrap around
call wrapt
mov b,a ; input pointer to b
sta prnbuf+1 ; save the new pointer away
lxi h,prnbuf+2 ; point to first real data entry in buffer
call inchl ; add offset in a to hl
lda prntmp ; get th character to save away
mov m,a ; save the data away
ret
; outprx - send character in a to the printer. (We have checked to see if
; the printer is ready)
; called by outprn, print
outprx: mov e,a ; character has to be in e register
call outlpt ; send it to printer
ret ; assume we print it
; TSTFREE - see how many free spacse there are in the buffer
; - returns with free space in a, ip pointer in b, op pointer in c
szecyc equ 127 ; 128 bytes in buffer (less for debugging)
;
tstfree:
lda prnbuf ; get output pointer
mov c,a ;.. to c
lda prnbuf+1 ; and input pointer ...
mov b,a ; ... to b
;
;
; Now comes the tricky bit. We must establish whether there is less than
; three characters left in the buffer. There are two conditions to test for
; 1) the input pointer is a higher value than the output pointer
; 2) the input pointer has been wrapped round and is less than the output pointer
; ie
; |-------|-------|---------------------------------------------|
; Buffer |o/p ptr|i/p ptr| Buffer proper filling ---> |
; |-------|-------|------|-------------|-----------|------------|
; i/p2 o/p i/p1
;
; If ip = ip1 then if
; (size of buffer - ip ptr + op ptr) < 3 send xoff
; If ip = ip2 then if
; (op ptr - input ptr ) < 3 send xoff
;
; First decide whice one applies
mov a,b ; get ip ptr
sub c ; see if op ptr > ip ptr (case 2)
jm outp2 ; yup, so do case two
mvi a,szecyc ; else do buffer - ip + op
sub b
add c
jmp outpx ; do common test
outp2: mov a,c ; get op pointer
sub b ; less input pointer
outpx: ret ; with free space in a
;
;
;
; print - get a character from the buffer and print it if the printer
; is ready for it. If the buffer clears more than 3 spare characters
; and an xoff has been sent, then send an xon again.
print: push h ; save for rainy days
push d
push b
push psw ; .. as we may need flags etc....
lda initflg ; First check if the system has initialised
ana a
jz printx ; If system not set up then skip
; nop
; nop
; nop ; debugging only...
call ckprtr ; check to see if printer is ready...
ana a ; not zero => ok
jz printx ; else skipit.
; nop
; nop
; nop ; skip the jump for debugging
lxi h,prnbuf
mov a,m ; get input pointer
inx h ; test against output pointer
cmp m ; if = then buffer empty
jz printx ; so quit
dcx h ;pointer to output pointer
call wrapt ; check for wrap around
sta prnbuf ; save new pointer
inx h
inx h ;
call inchl ; add output pointer to hl
mov c,m ; get byte
lda hosths ; have we told host to be quiet?
cpi xoff ; if = xoff then we have
jnz print1 ; nope, so just print it..
push b ; save the character to print
call tstfree ; see how many free bytes in buffer
pop b
cpi 4 ; 3 characters left?
jz printx
push b
call sndxon ; send an xon to host and wake it up.
pop b
print1: mov a,c ; we are gonna print a character, so ...
call outprx ; get it to a (as required by outprx) and print it
printx:
pop psw
pop b
pop d
pop h ; restore regs.
ret
;
; Utilities for the cyclical buffer. Returns a 0ffh if printer ready,
; else 0h. Called by outprn, print
ckprtr:
call lptstat ; no registers saved
; mvi a,0 ; FOR DEBUGGING PURPOSES
; nop
ret
;
inchl: push psw ; we do maths through this register
add l
mov l,a
mvi a,0
adc h
mov h,a
pop psw ; hl = a + hl
ret
; wrapt - checks the offset in a with the limits of the buffer.
; returns next address or if wrap around then 0 (start of buffer)
wrapt: push b
inr a
mov b,a ; save new a into b for now
mvi a,szecyc ; test for size of buffer
sub b
mov a,b
pop b ; restore bc regs again
rnz
xra a ; if wrap around, then reset pointer
ret ; return with next address pointer to in a
; sndxoff - send an xoff to the host and save the xoff character in hosths
; saves all regs. is called by logwrt, outprn
sndxoff:
push psw
push b
push d
push h ; some calling routines may be sensitive...
lda floctl ; are we doing flow control?
ana a
jz sndxf ; no, so dont bother.
mvi a,xoff ;^S to stop the host while we write the buffer.
sta hosths ; save it so we know we have sent it
call setpar ; set correct parity...
mov e,a
call outmdm ; output it.
lxi d,ofsnt ; say we have sent an xoff
call prtstr
sndxf: pop h
pop d
pop b
pop psw ; some routines touchy
ret
ofsnt: db cr,lf,'[XOFF sent to host]',cr,lf,'$'
; sndxon - send an xon to the host and clear the hosths flag. saves everything
; called by logwrt, print
sndxon: push psw
push b
push d
push h
lda floctl ; are we doing flow control?
ana a
jz sndxn
xra a
sta hosths ; no xoff to hos any more
mvi a,xon ;^Q to restart the host
call setpar ; set appropriate parity
mov e,a
call outmdm ; send it.
lxi d,onsnt
call prtstr ; say xon sent to host...
sndxn: pop h
pop d
pop b
pop psw
ret ; shame we dont do a pushall/popall subroutine...
onsnt: db cr,lf,'[XON sent to host]',cr,lf,'$'
;
; Routines to clear (or rather fill) TEMPnnn space with the data in A
; and to shift it all along one (filltmp an shiftmp respectively)
filltmp:
push b
push d
push h ; save all
lxi h,temp1
mvi b,10 ; ten locations to fill
fillp: mov m,a
inx h
dcr b ; loop til all done
jnz fillp
pop h
pop d
pop b ; restore all
ret
shiftmp:
push psw ; save all again
push b
push d
push h
lxi d,temp9
lxi h,temp10
mvi b,9 ; shift nine times
shiftl: ldax d ; from tempx
mov m,a ; to tempx+1
dcx d
dcx h ; mover does not work as that increments
dcr b
jnz shiftl
pop h
pop d
pop b
pop psw
ret ; else all done
; getun - get the user number to temp1 (lsd) and temp2 (msd)
;
getun: mvi a,0ffh ; tell nout to be quiet
sta nquiet
mvi c,usrcod
mvi e,0ffh ; get current user from bdos
call bdos
mov l,a ; put into hl
mvi h,0
call nout ; decimalise it (decimalise???)
xra a
sta nquiet ; let nout print again
ret
; ckcon - Do a direct console IO (read) to see if there is any input
; returns with a=0 (no input) or character (input received)
; Assume that all regs may be destroy.
ckcon: mvi e,0ffh ; direct console input
mvi c,dconio
call bdos
ret ; and return with wahtever returned in a
;
; subbc - Subtract the unsigned number in bc from the unsigned number
; in HL with the answer in HL. Flags altered, all
; other registers left intact.
subbc: sta temp1 ; hl = hl - bc.. we need the accumulator
mov a,l
sub c
mov l,a
mov a,h
sbb b
mov h,a
lda temp1 ; restore loop counter but not flags
ret
; P20LN - Routine to print a string at (DE) and count the number of
; line feeds. Pause after 20 lines printed.
p20ln: xra a ; clear the line counter
sta lincnt
p20ln1: ldax d ; get character to print
inx d
cpi '$' ; if a dollar we have done
rz
push d
push psw ; save pointer and character to print
mov e,a
call outcon ; send character
pop psw
pop d ; restore pointers etc
cpi lf ; was that last character a line feed?
jnz p20ln1 ; no, so carry on
lda lincnt ; yup, so update counter
inr a
sta lincnt
cpi 20 ; 20 lines printed?
jnz p20ln1 ; not yet
push d ; we need DE
; lxi d,anymes ; pause a while [MF]removed
; call prtstr ; write the message [MF]
;p20ln2: call ckcon ; wait for any input [MF]
; ana a ;[MF]
; jz p20ln2 ;[MF]
call pausit ;[MF] pause a while
pop d
jmp p20ln ; and continue
; Little code to allow some expansion of code without changing
; every futher address, only up to the end of this file.
; TO BE REMOVED FOR RELEASE!
; org ($+100h) AND 0FF00H
; link to the data area (was part of CPSUTL.ASM)
IF lasm
LINK CPSDAT
ENDIF ;lasm
|