Repository navigation
Expand file tree
/
Copy pathedit.asm
More file actions
1031 lines (994 loc) · 22 KB
/
Copy pathedit.asm
File metadata and controls
1031 lines (994 loc) · 22 KB
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
;
; BASIC-DOS Program Editing Commands
;
; @author Jeff Parsons <Jeff@pcjs.org>
; @copyright (c) 2020-2026 Jeff Parsons
; @license MIT <https://basicdos.com/LICENSE.txt>
;
; This file is part of PCjs, a computer emulation software project at pcjs.org
;
; Every line of the loaded program is stored in the program's text blocks as
; a label # (a word, zero if the line has none), a length (a byte), and the
; line's text (without the label # or the space following it). cmdLoad fills
; the text blocks from a file; the other functions here edit them in place:
; numbered lines typed at the prompt (see enterLine), and AUTO, DELETE, and
; EDIT (see editcmd.asm). LIST and SAVE are here, too.
;
; Lines are kept in the order they were loaded (or entered); a new line is
; inserted before the first line with a larger label #, after any unlabeled
; lines that precede it. An unlabeled line belongs to the nearest labeled
; line preceding it, so LIST and DELETE include it with that line.
;
DOSFUNC equ 1
include cmd.inc
CODE SEGMENT
EXTNEAR <allocText,freeCache,memError,writeStrCRLF>
EXTNEAR <getToken,getFileName,chkExt,addString,openOutput,findProgram>
EXTNEAR <writeOutput,writeError,openError,noFile,listVars>
EXTNEAR <freeAllText,openInput,readInput,closeInput>
IF DETOK
EXTNEAR <loadTokens>
ENDIF
EXTWORD <OPTION_TOKENS>
EXTABS <TOK_VARS>
EXTSTR <BAS_EXT,BAT_EXT>
ASSUME CS:CODE, DS:DATA, ES:DATA, SS:DATA
MAX_LINE equ 65529 ; largest line # (as in MSBASIC)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; cmdLoad
;
; Opens the specified file and loads it into one or more text blocks.
;
; TODO: Shrink the final text block to the amount of text actually loaded.
;
; Inputs:
; DS:SI -> filespec (with length CX)
; DX -> one of: BAT_EXT, BAS_EXT, or cmdLoad
;
; Outputs:
; Carry clear if successful, set if error (the main function doesn't
; care whether this succeeds, but other callers do)
;
; Modifies:
; Any except DX
;
DEFPROC cmdLoad
LOCVAR lineLabel,word ; current line label
LOCVAR lineOffset,word ; current line offset
LOCVAR pTextLimit,word ; current text block limit
LOCVAR pLineBuf,word
LOCVAR lineTerm,byte ; previous line terminator
LOCVAR pFileExt,word
ENTER
ASSUME DS:DATA
mov [pFileExt],dx
mov [lineTerm],0
cmp dx,offset cmdLoad ; called with an ambiguous name?
jne lf1a ; no
;
; TODO: LOAD inside a running BAS or BAT file would replace the running file's
; own text (and code), so for now, it's an error.
;
call chkProgram ; is a program running?
jnc lf0 ; no
jmp lf13
lf0: mov dx,offset BAT_EXT ; try .BAT and then .BAS, if necessary,
call findProgram ; as running does (see PATH$)
jc lf1b
lf1a: call openInput ; open the specified file
jnc lf1c
lf1b: call openError ; report error (AX) opening file (SI)
jmp lf13
lf1c: call freeAllText ; free any pre-existing blocks
call allocText ; ES:DI -> new text block
jc lf4y
mov ax,es:[BLK_SIZE]
mov [pTextLimit],ax
;
; For every complete line at DS:SI, determine the line label (if any), and
; then add the label # (2 bytes), line length (1 byte), and line contents
; (not including any leading space or terminating CR/LF) to the text block.
;
; Lines may be terminated by either CR/LF or LF alone. Either CR or LF ends
; a line, and whenever a line ends with CR, a LINEFEED immediately following
; it is skipped; lineTerm remembers the terminator, in case the LINEFEED isn't
; read until the next readInput.
;
lea ax,[bx].LINEBUF
mov [pLineBuf],ax
sub cx,cx ; DS:SI contains zero bytes now
lf3: jcxz lf4
cmp [lineTerm],CHR_RETURN ; did the previous line end with CR?
jne lf3c ; no
mov [lineTerm],0
cmp byte ptr [si],CHR_LINEFEED
jne lf3c
inc si ; skip LINEFEED from the previous line
dec cx
jmp lf3
lf3c: push cx
mov dx,si ; save SI
lf3a: lodsb
cmp al,CHR_RETURN
je lf3b
cmp al,CHR_LINEFEED
je lf3b
loop lf3a
lf3b: xchg si,dx ; restore SI; DX is how far we got
pop cx
je lf5 ; we found the end of a line
;
; The end of the current line is not contained in our buffer, so "slide"
; everything at DS:SI down to LINEBUF, fill in the rest of LINEBUF, and try
; again.
;
cmp si,[pLineBuf] ; is current line already at LINEBUF?
je lf4y ; yes, we're done
push cx
push di
push es
push ds
pop es
mov di,[pLineBuf]
rep movsb
pop es
pop di
pop cx
lf4: mov si,[pLineBuf] ; DS:SI has been adjusted
;
; At DS:SI+CX, read (size LINEBUF - CX) more bytes.
;
push cx
push si
add si,cx
mov ax,size LINEBUF
sub ax,cx
xchg cx,ax
call readInput
pop si
pop cx
jc lf4x
add cx,ax
jcxz lf4y ; if file is exhausted, we're done
jmp lf3
lf4x: jmp lf10
lf4y: jmp lf12
;
; We found the end of another line starting at DS:SI and ending at DX.
;
lf5: cmp byte ptr [si],0FEh ; MSBASIC protected (FEh) files aren't
IF DETOK ; supported, but tokenized (FFh) files
jb lf5a ; are (see loadTokens)
je lf10
mov ax,offset loadTokens
cmp ax,ds:[PSP_CODESIZE] ; is the tokenized file loader loaded?
jae lf10 ; no (see genUpper): invalid file
call loadTokens
jc lf10
jmp lf12
ELSE
jae lf10 ; (unless DETOK is zero)
ENDIF
lf5a: mov [lineOffset],si
push dx
DOSUTIL ATOI32D ; DS:SI -> decimal string
ASSERT Z,<test dx,dx> ; DX:AX is the result but keep only AX
mov [lineLabel],ax
pop dx
jnc lf6 ; we found a label #
;
; There's no label #, so keep any leading whitespace (which ATOI32D skipped),
; so that SAVE and LIST show the line as it was.
;
mov si,[lineOffset]
jmp short lf7
;
; We've extracted the label #; skip over one intervening space.
;
lf6: lodsb
cmp al,CHR_SPACE
je lf7
dec si
lf7: dec dx ; back up to the line terminator
sub dx,si ; DX = # of chars on line (may be zero)
;
; Is there room for DX more bytes at ES:DI?
;
mov ax,di
add ax,dx
add ax,3
cmp ax,[pTextLimit] ; overflows the current text block?
jbe lf8 ; no
;
; No, there's not enough room, so allocate another text block.
;
push cx
push si
call allocText ; ES:DI -> new text block
pop si
pop cx
jc lf11 ; unable to allocate enough memory
mov ax,es:[BLK_SIZE]
mov [pTextLimit],ax
lf8: mov ax,[lineLabel]
stosw
mov al,dl
stosb
push cx
mov cx,dx
rep movsb
mov es:[BLK_FREE],di
pop cx
mov ax,si
sub ax,[lineOffset]
sub cx,ax
;
; Consume the line terminator and go back for more.
;
lodsb
dec cx
mov [lineTerm],al ; remember terminator (CR or LF)
jmp lf3
lf10: PRINTF <"Invalid file format",13,10>
lf11: call freeAllText
stc
lf12: pushf
call closeInput
popf
lf13: mov dx,[pFileExt] ; restore DX for cmdFile calls
LEAVE
ret
ENDPROC cmdLoad
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; cmdList
;
; Process "LIST [line][-[line]]", which lists the specified range of lines
; (or all lines). LIST VARS lists variable values and array dimensions.
;
; Inputs:
; BX -> CMDHEAP
; DI -> TOKENBUF
;
; Outputs:
; None
;
; Modifies:
; Any
;
DEFPROC cmdList
mov dl,1
call getToken ; option or line range?
jc ls1 ; no argument
push si
lea dx,[OPTION_TOKENS]
DOSUTIL TOKID ; AX = option ID, if any
pop si
jc ls1 ; not an option, so maybe a range
push ax
mov dl,2
call getToken ; the option must be the last argument
pop ax
jc ls0
jmp syntaxError
ls0: cmp ax,TOK_VARS
jne ls0b
jmp listVars
ls0b: jmp syntaxError ; (eg, LIST SYSTEM)
ls1: mov al,'-'
call getRange ; AX = 1st line #, DX = last line #
jnc ls2
ret
ls2: mov si,offset listLine
jmp short walkLines
ENDPROC cmdList
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; cmdSave
;
; Process "SAVE file", which saves the loaded program as a text file (with
; a BAS extension if none is specified).
;
; The file is closed here, rather than by cleanUp, so that we can report a
; failure to update the file's directory entry (ie, its size).
;
; Inputs:
; BX -> CMDHEAP
; DI -> TOKENBUF
;
; Outputs:
; None
;
; Modifies:
; Any
;
DEFPROC cmdSave
mov dl,[bx].CMD_ARG
sub cx,cx ; (no default filename)
call getFileName ; DS:SI -> filename, CX = length
jnc sv1
jmp noFile
sv1: call chkExt ; does the filename have an extension?
jnc sv2 ; yes
mov dx,offset BAS_EXT ; no, so add one
call addString
sv2: call openOutput
jnc sv3
jmp openError ; report error (AX) opening file (SI)
sv3: sub ax,ax
mov dx,-1 ; save every line
mov si,offset saveLine
call walkLines
jc sv9 ; (the error was reported)
push bx
mov bx,[bx].HDL_OUTPUT
mov ah,DOS_HDL_CLOSE
int 21h
pop bx
jnc sv9
jmp writeError
sv9: ret
ENDPROC cmdSave
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; walkLines
;
; Calls a function for every line whose label # (or if it has none, the
; label # of the nearest labeled line preceding it) is within a range.
;
; Inputs:
; BX -> CMDHEAP
; AX = 1st line #
; DX = last line #
; SI = function (called with ES:DI -> line and CX = its length; it
; must return DI -> next line (or carry set to stop), and preserve BX)
;
; Outputs:
; Carry set if the function stopped the walk
;
; Modifies:
; Any but BX
;
DEFPROC walkLines
push bp
push si ; [bp+6]: function
push dx ; [bp+4]: last line #
push ax ; [bp+2]: 1st line #
sub ax,ax
push ax ; [bp]: label # of the current line
mov bp,sp
call firstLine
wl1: jc wl8 ; no more lines
test ax,ax ; does the line have a label #?
jz wl2 ; no
mov [bp],ax ; yes, so it's the current label #
wl2: mov ax,[bp]
cmp ax,[bp+2]
jb wl3 ; before the range
cmp ax,[bp+4]
ja wl3 ; after the range
call word ptr [bp+6] ; call the function
jc wl9
call getLine ; ES:DI -> next line
jmp wl1
wl3: call nextLine
jmp wl1
wl8: clc
wl9: lea sp,[bp+8] ; (LEA preserves the carry flag)
pop bp
ret
ENDPROC walkLines
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; listLine
;
; Lists a line (see walkLines), as saveLine would write it, so that any tabs
; in the line line up as they would with TYPE.
;
; Inputs:
; ES:DI -> line
;
; Outputs:
; DI -> next line (carry clear)
;
; Modifies:
; AX, CX, DX, SI, DI
;
DEFPROC listLine
mov ax,es:[di]
test ax,ax ; does the line have a label #?
jz li1 ; no
PRINTF <"%u ">,ax
li1: push ds
push es
pop ds
lea si,[di+2] ; DS:SI -> length-prefixed text
call writeStrCRLF
mov di,si
pop ds
clc
ret
ENDPROC listLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; saveLine
;
; Writes a line to the output file (see walkLines).
;
; Inputs:
; BX -> CMDHEAP
; ES:DI -> line
;
; Outputs:
; DI -> next line (carry set if error)
;
; Modifies:
; AX, CX, DX, SI, DI
;
DEFPROC saveLine
mov ax,es:[di]
test ax,ax ; does the line have a label #?
jz sa1 ; no
push es
push di
push ds
pop es
lea di,[bx].LINEBUF
mov si,di
call putNum ; store the label # and a space
mov cx,di
sub cx,si ; CX = # of characters
pop di
pop es
call writeOutput ; write them
jc sa9
sa1: push ds
push es
pop ds
lea si,[di+2]
lodsb
mov ah,0
xchg cx,ax ; DS:SI -> text, CX = length
jcxz sa2
call writeOutput ; write the text
sa2: pop ds
jc sa9
add si,cx
mov di,si ; DI -> next line
mov ax,(CHR_LINEFEED SHL 8) OR CHR_RETURN
push ax
mov si,sp
mov cl,2
call writeOutput ; write CR/LF from the stack
pop ax
sa9: ret
ENDPROC saveLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; syntaxError
;
; Outputs:
; Carry set
;
; Modifies:
; AX
;
DEFPROC syntaxError
PRINTF <"Syntax error",13,10>
stc
ret
ENDPROC syntaxError
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; getRange
;
; Parses the optional range argument of AUTO, DELETE, EDIT, and LIST (eg,
; "10", "10-", "-20", or "10-20"; AUTO separates its values with a comma).
;
; Inputs:
; AL = separator
; BX -> CMDHEAP
; DI -> TOKENBUF
;
; Outputs:
; If carry clear, AX = 1st value (0 if none), DX = 2nd value (0FFFFh if
; none, or the 1st value if there was no separator), and CX = 0 if there
; was a value but no separator; otherwise, carry set (and a message was
; printed)
;
; Modifies:
; AX, CX, DX, SI
;
DEFPROC getRange
push ax
mov dl,1 ; DL = 1st arg (not CMD_ARG, since a
call getToken ; range like "-20" looks like a switch)
pop dx ; DL = separator
jnc gr1
sub ax,ax ; no token, so the range is everything
mov dx,-1
mov cx,dx
ret
gr1: add cx,si ; CX -> end of token
call getNum ; AX = 1st value
cmp [si],dl ; followed by the separator?
mov dx,ax ; (if not, the 2nd value is the 1st)
jne gr3 ; no
inc si
push ax
mov dx,si
call getNum ; AX = 2nd value
cmp si,dx ; any digits?
xchg dx,ax ; DX = 2nd value
jne gr2 ; yes
mov dx,-1 ; no, so there's no limit
gr2: pop ax
cmp si,cx ; was the entire token used?
jne syntaxError ; no
ret ; (carry clear, CX non-zero)
gr3: cmp si,cx ; was the entire token used?
jne syntaxError ; no
sub cx,cx ; CX = 0 (no separator, carry clear)
ret
ENDPROC getRange
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; getNum
;
; Converts the decimal digits (if any) at DS:SI to a value; values larger
; than MAX_LINE are limited to 65530-65539, so they're never valid line #s.
;
; Inputs:
; DS:SI -> digits
;
; Outputs:
; AX = value (0 if no digits), DS:SI -> first non-digit
;
; Modifies:
; AX, SI
;
DEFPROC getNum
push dx
sub dx,dx
gn1: lodsb
sub al,'0'
cmp al,10 ; decimal digit?
jae gn9 ; no
cbw
xchg ax,dx ; AX = value, DX = digit
cmp ax,MAX_LINE / 10 + 1
jb gn2
mov ax,MAX_LINE / 10 + 1 ; (the result stays invalid)
gn2: add ax,ax
add dx,ax ; DX = value * 2 + digit
add ax,ax
add ax,ax
add dx,ax ; DX = value * 10 + digit
jmp gn1
gn9: dec si
xchg ax,dx
pop dx
ret
ENDPROC getNum
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; putNum
;
; Stores a value in decimal, followed by a space.
;
; Inputs:
; AX = value
; ES:DI -> buffer
;
; Outputs:
; ES:DI -> next byte in buffer
;
; Modifies:
; AX, DI
;
DEFPROC putNum
push cx
push dx
mov cx,10
push cx ; push a non-digit (10) as a marker
pn1: sub dx,dx
div cx
push dx ; push the next digit
test ax,ax
jnz pn1
pn2: pop ax
add al,'0'
cmp al,'0'+10 ; was it the marker?
je pn3 ; yes
stosb
jmp pn2
pn3: mov al,CHR_SPACE
stosb
pop dx
pop cx
ret
ENDPROC putNum
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; chkProgram
;
; Used by commands that can't be used while a program is running.
;
; Inputs:
; BX -> CMDHEAP
;
; Outputs:
; Carry set if a program is running (and a message was printed)
;
; Modifies:
; AX
;
DEFPROC chkProgram
cmp [bx].CBLKDEF.BDEF_NEXT,0; is a program running?
je cp9 ; no (carry clear)
PRINTF <"Not allowed in a program",13,10>
stc
cp9: ret
ENDPROC chkProgram
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; enterLine
;
; Called by the main loop with every line of input: if the line begins with
; a line # (followed by a space or nothing), it's added to the program,
; replacing any line with the same #; if nothing follows the line #, the line
; with that # is deleted.
;
; Inputs:
; BX -> CMDHEAP
; DS:SI -> input (with length CL)
;
; Outputs:
; Carry set if not a numbered line (SI and CL unchanged); otherwise,
; carry clear (the line was processed)
;
; Modifies:
; AX, DX (and anything but BX, DS, and ES if carry clear)
;
DEFPROC enterLine
call parseLine
jc el9
push es
call storeLine
pop es
clc
el9: ret
ENDPROC enterLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; parseLine
;
; Inputs:
; DS:SI -> input (with length CL)
;
; Outputs:
; If carry clear, AX = line #, DS:SI -> text, CX = length of text;
; otherwise, the input doesn't begin with a line # (SI and CL unchanged)
;
; Modifies:
; AX, CX, DX, SI
;
DEFPROC parseLine
mov dx,si
call getNum ; AX = line #, SI -> next character
cmp si,dx ; any digits?
je pl8 ; no
cmp byte ptr [si],CHR_SPACE ; followed by a space or nothing?
ja pl8 ; no
jb pl1
inc si ; skip one space (as cmdLoad does)
pl1: mov ch,0
add cx,dx ; CX -> end of input
sub cx,si ; CX = length of text (carry clear)
ret
pl8: mov si,dx
stc
ret
ENDPROC parseLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; storeLine
;
; Adds a line to the program, replacing any line with the same label #; if
; the line has no text, the line with that label # (if any) is deleted.
;
; Inputs:
; AX = line #
; BX -> CMDHEAP
; DS:SI -> text (with length CX)
;
; Outputs:
; Carry set if error (and a message was printed)
;
; Modifies:
; Any but BX and DS
;
DEFPROC storeLine
dec ax
cmp ax,MAX_LINE ; is the line # valid (1-MAX_LINE)?
inc ax
jae sl8 ; no
xchg dx,ax ; DX = line #
mov [bx].ERR_CODE,0 ; (so that memError reports errors)
push cx
push si
call freeCache ; (the program must be recompiled)
cmp [bx].TBLKDEF.BLK_NEXT,0 ; does the program have a text block?
jne sl1 ; yes (carry clear)
call allocText ; no, so allocate one
sl1: pop si
pop cx
jc sl9
push cx
push si
call findLine ; ES:DI -> line, if any
pop si
pop cx
jc sl2 ; not found
jne sl2 ; not found
call delLine ; delete the existing line
sl2: jcxz sl7 ; nothing to insert
jmp short insLine
sl7: clc
sl9: ret
sl8: jmp syntaxError
ENDPROC storeLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; insLine
;
; Inserts a line at ES:DI. If the text block doesn't have enough room, it's
; split in two (see splitBlock), and the line is added to the end of the first
; block or, if it still doesn't fit, to the start of the second.
;
; Inputs:
; BX -> CMDHEAP
; DX = label #
; DS:SI -> text (with length CX)
; ES:DI -> insertion point
;
; Outputs:
; Carry set if error (and a message was printed)
;
; Modifies:
; AX, CX, SI, DI, ES
;
DEFPROC insLine
call tryLine
jnc il9
call splitBlock
jc il9
call tryLine ; try the end of the current block
jnc il9
mov es,es:[BLK_NEXT]
mov di,size TBLK
call tryLine ; and then the start of the next one
jnc il9
jmp memError
il9: ret
ENDPROC insLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; tryLine
;
; Inputs:
; DX = label #
; DS:SI -> text (with length CX)
; ES:DI -> insertion point
;
; Outputs:
; Carry set if there's not enough room in the text block (no changes);
; otherwise, carry clear and the line was inserted
;
; Modifies:
; AX (and CX, SI, DI if successful)
;
DEFPROC tryLine
push si
mov si,es:[BLK_FREE] ; SI = end of the block's lines
mov ax,si
add ax,cx
add ax,3 ; AX = new end
cmp es:[BLK_SIZE],ax ; enough room?
jb tl9 ; no
mov es:[BLK_FREE],ax
push cx
push di
push ds
mov cx,si
sub cx,di ; CX = # bytes to move (carry clear)
dec si ; SI -> last byte to move
xchg di,ax
dec di ; DI -> its new location
push es
pop ds
std
rep movsb ; make room for the line
cld
pop ds
pop di
pop cx
xchg ax,dx
stosw ; store the label #
xchg ax,dx
mov al,cl
stosb ; store the length
tl9: pop si
jc tl10
rep movsb ; and store the text
tl10: ret
ENDPROC tryLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; splitBlock
;
; Moves all the lines at ES:DI onward to a new text block, which (because
; allocText adds it to the end of the chain) we move to follow ES.
;
; Inputs:
; BX -> CMDHEAP
; ES:DI -> split point
;
; Outputs:
; Carry set if error (and a message was printed); ES:DI unchanged
;
; Modifies:
; AX
;
DEFPROC splitBlock
push cx
push dx
push si
push di
push es
mov cx,di ; CX = split point
mov dx,es ; DX = current block
call allocText ; ES:DI -> new block
jc sb9
push ds
mov si,cx ; SI = split point
mov ax,es ; AX = new block
mov ds,dx ; DS = current block
mov cx,ds:[BLK_NEXT]
cmp cx,ax ; is the new block next already?
je sb2 ; yes
mov ds:[BLK_NEXT],ax ; no, so insert it here
mov es:[BLK_NEXT],cx
sb1: mov ds,cx ; and remove it from the end
mov cx,ds:[BLK_NEXT]
cmp cx,ax
jne sb1
mov word ptr ds:[BLK_NEXT],0
mov ds,dx
sb2: mov cx,ds:[BLK_FREE]
sub cx,si ; CX = # bytes to move
mov ax,di
add ax,cx
cmp es:[BLK_SIZE],ax ; enough room in the new block?
jb sb8 ; no
mov es:[BLK_FREE],ax
mov ds:[BLK_FREE],si
rep movsb
sb8: pop ds
jnc sb9
call memError
sb9: pop es
pop di
pop si
pop dx
pop cx
ret
ENDPROC splitBlock
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; delLine
;
; Deletes a line (see walkLines, too).
;
; Inputs:
; ES:DI -> line
;
; Outputs:
; DI -> next line (carry clear)
;
; Modifies:
; AX
;
DEFPROC delLine
push cx
push si
push di
push ds
mov al,es:[di+2]
mov ah,0
add ax,3 ; AX = size of the line
mov si,di
add si,ax ; SI -> next line
mov cx,es:[BLK_FREE]
sub es:[BLK_FREE],ax
sub cx,si ; CX = # bytes to move (carry clear)
push es
pop ds
rep movsb
pop ds
pop di
pop si
pop cx
ret
ENDPROC delLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; findLine
;
; Finds the first line whose label # is greater than or equal to DX.
;
; Inputs:
; BX -> CMDHEAP
; DX = line #
;
; Outputs:
; If carry clear, ES:DI -> line, CX = its length, and ZF set if its label
; # is DX; otherwise, ES:DI -> end of the last text block (if any)
;
; Modifies:
; AX, CX, DI, ES
;
DEFPROC findLine
call firstLine
fl1: jc fl9
test ax,ax ; does the line have a label #?
jz fl2 ; no
cmp ax,dx
jae fl9 ; (ZF set if equal)
fl2: call nextLine
jmp fl1
fl9: ret
ENDPROC findLine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; firstLine (nextLine, getLine)
;
; Inputs:
; BX -> CMDHEAP (firstLine)
; ES:DI -> current line, CX = its length (nextLine)
; ES:DI -> next line, or the end of a text block (getLine)
;
; Outputs:
; If carry clear, ES:DI -> line, AX = label #, CX = length; otherwise,
; ES:DI -> end of the last text block (if any)
;