-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathbasic5.mac
4051 lines (3763 loc) · 83 KB
/
basic5.mac
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
TITLE BASIC-11 interpreter
SUBTTL Immediate mode commands
.Z80
; Z80 port by Hector Peraza, 2016-2020
include BASDEF.INC
include BASTKN.INC
CR equ 0Dh
LF equ 0Ah
public EDIT,CLVARS,GFIL,SCRATCH,OLD1,HEAD1,HEAD2,HEAD3
public UPPACK
extrn VERSION,CLRFAC,MSG,SAVREG,INITPG,CLOSYS,READY,LITEVAL
extrn SKPEOL,SKPOBJ,CPHLDE,CPIXBC,RDBLK,WRBLK,DELETE,VARSIZ
extrn FRESET,LINGET,OPNSYS,DATIM,CATALOG,DNPACK,ALLOC,VAL
extrn NAMSET,PURGALL,DISPAT,ODEVTT,EXECUTE,BYE,NUMOUT,FILSPE
extrn CHKFIL,SKPLIN,PUTCHR,ISDIG,CKCTLC,NUMB,SKPSYM,ISLETR
extrn FPSTS,FPST,FILEA,CHAIN1,WRVFBL,RDNXBL,NEGHL,CHKBFP
extrn CPDEHL,CPBCHL
;-----------------------------------------------------------------------
cseg
; The program jumps here after displaying the READY prompt
EDIT: call LINGET ; get input line from terminal
jr nc,EDITL0 ; not EOF, jump
call CLOSYS ; else close system channel 13
EDIT1: ld hl,0
ld (IDEV),hl ; reset input to terminal
ld hl,(CODE)
ld de,(EDITLN)
ld a,d
or e
jr nz,edln ; jump to find line
ld a,(CHNFLG)
or a ; CHAIN or OVERLAY?
jr nz,edit2 ; start execution if yes
jp READY ; else go to immediate mode
edln: call SKPLIN ; go to next program line
jr c,ulnerr ; jump if end of program reached
ex de,hl
ld a,(hl) ; else fetch line number into DE
inc hl
ld h,(hl)
ld l,a
ex de,hl
push hl
ld hl,(EDITLN)
call CPHLDE
pop hl
jr c,edln ; loop until line >= EDITLN found
jr z,edit2 ; jump if line found
ld a,(CHNFLG)
or a ; CHAIN or OVERLAY?
jr z,edit3 ; jump if not
ulnerr: rst 10h
db 05h ; undefined line number
edit2: ld a,(CHNFLG)
cp 2 ; CHAIN?
jr c,edit3 ; jump if not
push hl
call DNPACK ; down-pack strings
call INIRUN ; prepare to run program
pop hl
edit3: xor a
ld (CHNFLG),a ; clear CHAIN/OVERLAY flag
ld bc,0
ld (EDITLN),bc
jp EXECUTE
EDITL0: ld hl,EDIT
push hl ; push return address on stack (main loop)
EDITL1: call TRAN ; translate (tokenize) line
push hl ; push HL (points one char past end of line)
ld de,LINE ; DE = begin of input buffer
or a
sbc hl,de
ex (sp),hl ; tokenized line length now on stack
ex de,hl
ld a,(hl) ; get first char
inc hl
or a
jp m,eimmed ; jump to exec line if a token
ld b,a ; otherwise fetch word into BC, HI byte first
ld a,(hl)
inc hl
ld c,a ; then LO-byte
and 01h ; odd number? (means this is a program line)
jr z,eimmed ; jump to execute line if not (must be a var)
dec bc ; BC = offset into line number table
ld hl,(LINTAB)
add hl,bc ; index into table, HL points to block
pop de ; get length back into DE
push de
ld a,e ; tokenized line length is 3 (empty line)?
cp 3 ; (empty line = lineno,CR)
jr z,editl2 ; jump if yes to delete any existing line
ld e,(hl) ; get line number from LINTAB into DE
inc hl
ld d,(hl)
dec hl
push hl
ld hl,(HILINO) ; compare with highest line number
call CPDEHL
pop hl
jr nc,DELLN ; if DE <= HILINO
ld (HILINO),de ; set new highest line number
pop hl ; get length
push hl
call NEGHL ; -length
ld bc,(DEFTAB) ; DEFTAB follows CODE
dec bc ; point to last program byte
ld a,(bc) ; get it
or a ; zero?
jp nz,APNDLN ; append new line if not
dec bc ; else backup one byte
jp APNDLN ; append new line
editl2: pop de ; drop tokenized line length
ld de,0
push de ; and replace it with zero length
inc hl ; skip over line number
inc hl
xor a
ld (hl),a ; clear second word in LINTAB entry
inc hl ; (invalidate line)
ld (hl),a
dec hl
dec hl
dec hl ; back to start of LINTAB entry
jr DELLN ; jump to delete line
eimmed: pop bc ; pop length
pop af ; remove word from stack (return address?)
push bc ; restore length
ld bc,(IDEV) ; current read chan descr
ld a,b
or c ; reading from file (OLD command)?
jp z,EXECLN ; execute line in not
snerr: rst 10h
db 06h ; else -> syntax error
; Compare line numbers pointed to by IX and DE.
; Returns CY if (IX) > DE.
CMPLN: inc de
ld a,(de)
cp (ix+1)
dec de
ret nz
ld a,(de)
cp (ix+0)
ret
; Replace, delete or insert program line
DELLN: push hl ; get LINTAB address for this line
pop ix ; into IX
ld hl,(CODE) ; HL = begin of program
fndl1: call SKPLIN ; go to next line
ld c,l ; remember address of line in BC
ld b,h
jp c,INSLIN ; jump if end of program
call CMPLN ; else compare line numbers
jr c,fndl1 ; loop while (DE) < (IX)
jp nz,INSLIN ; if different line number -> insert line
; fall thru ; else replace line
; Replace line. Here with BC = addr of line to replace.
push bc
call SKPLIN ; go to next line, HL = addr of next line.
pop bc
; first, delete any references to this line in PDL and DEFTAB
; if a line is changed which is referenced by the read pointer,
; an FN pointer or a GOSUB pointer, then that pointer must be
; cleared
DELLIN: ld ix,(PDL)
ld de,(GSBCTR)
ld a,(ix)
and (ix+1)
inc a ; (ix) = 0FFFFh? (first entry in PDL)
jr z,delln2 ; skip if yes
delln1: inc bc
call CPIXBC
dec bc
jr c,delln2 ; jump if (IX) <= BC [(IX) < BC+1]
push de
ld e,(ix)
ld d,(ix+1)
call CPDEHL
pop de
jr c,delln2 ; or if (IX) > HL
xor a
ld (ix),a ; clear this entry
ld (ix+1),a
delln2: inc ix
inc ix
ld a,d
or e
jr z,delln3
dec de
jr delln1 ; !!! was bgt ; loop while DE > 0
delln3: ld ix,(DEFTAB)
inc ix
inc ix
delln4: push hl
push de
push ix
pop de
ld hl,(LINTAB)
call CPHLDE
pop de
pop hl
jr nc,delln6 ; exit loop if IX >= LINTAB
inc bc
call CPIXBC
dec bc
jr c,delln5 ; jump if (IX) <= BC [(IX) < BC+1]
push de
ld e,(ix)
ld d,(ix+1)
call CPDEHL
pop de
jr c,delln5 ; or if (IX) > HL
xor a
ld (ix),a ; clear this entry
ld (ix+1),a
delln5: inc ix
inc ix
inc ix
inc ix
jr delln4 ; loop
delln6: or a
sbc hl,bc ; obtain length [of block to delete?]
jr insln1 ; continue below
; Insert line
INSLIN: ld hl,0
insln1: pop de
push de ; get length into DE
or a
sbc hl,de ; HL -= length
; fix any references in PDL and DEFTAB for current line
ld ix,(PDL)
ld de,(GSBCTR)
ld a,(ix)
and (ix+1)
inc a ; (IX) = 0FFFFh?
jr z,insln3 ; skip if yes
insln2: call CPIXBC
jr c,insln3 ; skip if (IX) < BC
ex de,hl
push hl
ld l,(ix)
ld h,(ix+1)
or a
sbc hl,de
ld (ix),l
ld (ix+1),h ; (IX) -= HL (fixup address in PDL)
pop hl
ex de,hl
insln3: inc ix
inc ix
ld a,d
or e
jr z,insln4
dec de
jr insln2 ; if > 0 (check!)
insln4: ld ix,(DEFTAB)
inc ix
inc ix
insln5: push hl
push de
push ix
pop de
ld hl,(LINTAB)
call CPHLDE
pop de
pop hl
jr nc,insln7 ; exit loop if IX >= LINTAB
call CPIXBC
jr c,insln6 ; skip if (IX) < BC
ex de,hl
push hl
ld l,(ix)
ld h,(ix+1)
or a
sbc hl,de
ld (ix),l
ld (ix+1),h ; (IX) -= HL (fixup DEFTAB address)
pop hl
ex de,hl
insln6: inc ix
inc ix
inc ix
inc ix
jr insln5 ; loop
insln7: ld a,h ; check length difference
or a
jp m,APNDLN ; jump if < 0 (expand)
or l
jp z,apndl6 ; jump if = 0 (same length as deleted)
; contract
pop de ; get length into DE
push de
ex de,hl
add hl,bc
ex de,hl ; dst in DE = BC + length
add hl,de ; src in HL = BC + length + length2
push bc
push de
ex de,hl
ld hl,(DEFTAB)
or a
sbc hl,de
ld c,l ; BC = block size
ld b,h
ex de,hl ; src in HL
pop de ; dst in DE
ldir ; move program code down
pop bc
dec de
ld a,(de)
or a
jr z,insln8
inc de
insln8: bit 0,e ; even?
jr z,insln9 ; jump if yes
xor a
ld (de),a
inc de ; word align
insln9: push bc
push hl
ld hl,(SYMBOL)
ld bc,(DEFTAB)
or a
sbc hl,bc
add hl,de
ld (SYMBOL),hl ; set new SYMBOL start address
ld hl,(LINTAB)
or a
sbc hl,bc ; BC = DEFTAB
add hl,de
ld (LINTAB),hl ; set new LINTAB start address
ld (DEFTAB),de
pop hl
push de
ex de,hl
ld hl,(LOFREE)
or a
sbc hl,de
ld c,l ; BC = size of block to move
ld b,h
ex de,hl ; src in HL
pop de ; dst in DE
ldir ; move data areas (DEFTAB..LOFREE) down
pop bc
ld (LOFREE),de ; set new LOFREE
jr insl11
insl10: xor a
ld (de),a ; clear vacated string storage to zeroes
inc de
insl11: call CPHLDE
jr c,insl10 ; loop while DE < HL
jp apndl6
; Append new program line. Here with HL = -length.
; expand
APNDLN: ld de,(DEFTAB)
dec de
ld a,(de) ; get last program byte
or a ; zero?
jr z,apndl1 ; jump if yes
inc de
apndl1: push hl ; HL = -length
ex de,hl
or a
sbc hl,de
push hl
pop ix ; IX = DEFTAB - HL = DEFTAB + length
inc hl
res 0,l ; make it even
ld de,(DEFTAB)
or a
sbc hl,de ; HL = IX - DEFTAB (corrected length)
ld de,(LOFREE)
add hl,de ; add LOFREE
ld de,(HIFREE)
call CPHLDE
jr nc,apndl2 ; jump if HL <= HIFREE
ptberr: rst 10h ; else error
db 10h ; program too big
apndl2: ld de,(LOSTR) ; check to see that the space that will
call CPHLDE ; be used is not occupied by strings
jr nc,apndl3 ; if HL <= LOSTR
call UPPACK ; no, move them up
ld de,(LOSTR) ; try again
call CPHLDE
jr c,ptberr ; if HL > LOSTR -> program too big
apndl3: ld de,(LOFREE) ; get old LOFREE into DE
ld (LOFREE),hl ; set new LOFREE
push bc ; push BC = old DEFTAB-1
push de
push hl
ld hl,(DEFTAB)
or a
ex de,hl
sbc hl,de
ld c,l ; BC = size of block to move
ld b,h
pop de ; DE = dst
pop hl ; HL = src
jr z,apndl4 ; jump if nothing to move
dec hl
dec de
lddr ; move data area (DEFTAB..LOFREE) up
inc de
apndl4: ld hl,(SYMBOL)
ld bc,(DEFTAB) ; get DEFTAB into BC
or a
sbc hl,bc ; HL = SYMBOL - DEFTAB (size)
add hl,de
ld (SYMBOL),hl ; set new symbol table start
ld hl,(LINTAB)
or a
sbc hl,bc ; BC still has DEFTAB
add hl,de
ld (LINTAB),hl ; set new LINTAB
ld (DEFTAB),de ; and DEFTAB
ex de,hl ; restore HL
dec hl
ld (hl),0 ; clear the possibly used filler
pop bc ; pop old DEFTAB-1
pop hl ; pop -length
push bc
push ix
pop de ; DE <- IX
add hl,de ; HL = IX - length
push hl
or a
sbc hl,bc
ld c,l ; BC = block size
ld b,h
pop hl ; HL = src, dst already in DE
jr z,apndl5 ; jump if nothing to move
dec hl
dec de
lddr ; move code up
apndl5: pop bc
apndl6: ld e,c ; DE = dst
ld d,b
pop bc ; pop line length
push de ; remember starting dest addr
ld a,b
or c
jr z,apndl7
ld hl,LINE ; HL = src (begin of input buffer)
ldir ; copy the new line into the code
apndl7: pop hl ; pop address of inserted line into HL
dec hl
apndl8: call SKPLIN ; find end of program line
ret c ; return if end of program
push hl
ld bc,(CODE)
or a
sbc hl,bc ; obtain offset from start of CODE
inc de ; skip over line number
inc de
ex de,hl
ld (hl),e ; (DE+2) <- HL - CODE
inc hl
ld (hl),d ; store program line offset
pop hl
jr apndl8 ; loop to fix all backpointers
; Execute line
EXECLN: pop bc ; pop length into BC
ld hl,LINE ; HL = begin of input buffer
call ODEVTT
ld a,(hl)
cp T.CMD ; direct statement follows?
jp z,DIRECT ; execute direct statement if yes
push hl
add hl,bc
ld de,(CODE)
call CPDEHL
jp nc,errlng ; error if HL >= CODE
ld (hl),T.EOF ; add "end of program" token
ld hl,0
ld (CLMNTT),hl ; clear terminal column
pop hl
jp EXECUTE ; start execution
;-----------------------------------------------------------------------
; Translate input line
TRAN: ld hl,LINE
ld e,l ; HL,DE = begin of input buffer
ld d,h
tr1: ld a,(hl)
inc hl
cp CR ; CR?
jr nz,tr1 ; no, loop (find the end of string)
push hl
or a
sbc hl,de ; compute length (note we have at least 1 char)
ld c,l
ld b,h
IF 0
ld de,(CODE) ; DE = ptr to tokenized prog text @end of inp buf
ELSE
ld de,LINE+134 ;!!!
ENDIF
dec de ; dest in DE
pop hl
dec hl ; src in HL
lddr ; move string to end of buffer
ex de,hl
inc hl ; src now in HL (string in hi-end)
inc de ; dst now in DE (start of input buffer)
tr2: ld (T2),de ; T2 = begin of input buffer
ld bc,0
ld (T3),bc
trnxt: xor a
ld (T1),a
trnx1: call GETNB ; get next non-blank or CR character
cp CR ; CR? (i.e. empty line?)
jr nz,tr3 ; no, process
ex de,hl
ld (hl),T.EOL ; otherwise set end of line
inc hl
ret ; and return with HL = past end of tokenized line
tr3: cp '.' ; '.' (???)
jp z,xnum
cp '?' ; shortcut for PRINT
jp z,tr13
call ISDIG ; digit?
jp nc,xnum ; jump if yes
ld ix,OPTBL ; operator table address
ld a,(hl)
call ISLETR ; is alphabetic?
jr c,tr6 ; jump if not
ld bc,0
ld (T3),bc
ld ix,KWTBL ; if yes, use keyword table
ld c,l ; BC = begin of line
ld b,h
tr4: inc bc ; find next ascii non-space character
ld a,(bc)
cp CR ; CR?
jr z,tr5
cp ' '+1 ; space or below?
jr c,tr4 ; yes, ignore
tr5: call ISLETR ; is character alphabetic?
jp c,xvar ; jump if not (no keyword matches)
tr6: ld c,l
ld b,h
tr7: ld a,(bc)
inc bc
call ISLETR ; is alphabetic?
jr c,tr8 ; jump if not
and 5Fh ; convert to uppercase (!!!TODO: write this better)
tr8: cp (ix) ; compare character to keyword
inc ix
jr z,tr7 ; if matches, loop
dec bc ; back to last unmatched character from input
dec ix
ld a,(ix)
or a ; A = first unmatched keyword char
jp z,xvar ; if zero, it was the end of the table:
; jump if no matches found
jp m,tr11 ; maybe token value? jump if yes
ld a,(bc) ; get last unmatched char again
inc bc
cp CR ; CR? end of line?
jr z,tr9
dec bc
ld a,(bc)
inc bc
cp ' '+1 ; was the preceding char blank?
jr c,tr7 ; if yes, ignore it (note that this allows
; having spaces embedded inside keywords!)
tr9: dec bc ; back one input word character
ld a,(ix)
inc ix
cp ' ' ; space?
jr z,tr7 ; loop if yes
tr10: ld a,(ix)
inc ix
or a ; token value?
jp p,tr10 ; no, skip remaining chars
jr tr6 ; and try next keyword
tr11: cp T.STOP ; STOP/DEL
jr nz,tr12
dec de
ld a,(de)
inc de
cp T.EQ ; previous token was a '=' ?
jp z,xvar
tr12: push de ; TODO: write this better!
push ix
pop hl
ld de,CMDTBL ; direct statement table address
call CPHLDE ; CY if IX > CMDTBL
pop de
ld l,c
ld h,b
jr nc,tr14 ; jump if it was not a direct statement
ld a,T.CMD
ld (de),a ; insert "command" token
inc de
ld a,(ix)
ld (de),a ; and follow with the token
inc de
jr tr15
tr13: ld a,T.PRNT
ld (de),a
inc de
inc hl
jp trnxt
tr14: ld a,(ix)
ld (de),a ; store the token
inc de
push hl
push de
push ix ; !!!TODO: write this better
pop de
ld hl,KWTBL ; keyword table address
call CPHLDE
pop de
pop hl
jr c,trystr ; jump if it was not a keyword
ld a,(ix)
cp T.REM ; REM
ld c,0FFh ; do not compress spaces
jp z,xlit
cp T.DATA ; DATA
jr z,xdata
cp T.FN ; FN
jp z,xfn
cp T.NEXT ; NEXT
jp z,xnext
cp T.GSUB ; GOSUB
jr z,xgoto
cp T.GOTO ; GOTO
jr z,xgoto
cp T.THEN ; THEN
jr z,xthen
cp T.CALL ; CALL
jp z,xcall
jp trnxt
tr15: cp T.SUB ; SUB
jr nz,tr18 ; jump if not
xdata: ld c,a
ld a,T.TEXT
ld (de),a ; SUB or DATA: add "text" token
inc de
call CPHLDE
ld a,c
jp nc,errlng ; error -> line too long to translate
tr16: ld a,(hl)
cp CR ; CR?
jr z,tr17
ld (de),a ; no, copy the rest of the line unchanged
inc hl
inc de
jr tr16 ; loop
tr17: xor a
ld (de),a ; end it with a null char
inc de
jp trnxt
tr18: cp T.REN ; RENAME
jp c,trnxt ; jump if no file-related operation
ld c,0 ; compress spaces
jp xlit ; store the rest of the line unchanged
; process THEN token
xthen: ld (T2),hl
; fall thru
; process GOTO/GOSUB tokens
xgoto: ld (T3),ix ; ??? to make it non-zero?
jp trnxt
; not a keyword, try string
trystr: ld a,(ix)
cp T.EOL ; '\' token
jp z,tr2
cp T.SNGQ ; ' token
ld c,"'"
jr z,xstr1
cp T.DBLQ ; " token
jp nz,trnxt
ld c,'"'
xstr1: ld a,T.TEXT
ld (de),a ; add "text" token
inc de
call CPHLDE
jp nc,errlng ; error -> line too long to translate
xstr2: ld a,(hl)
cp c ; delimiting quote char found?
jr z,xstr3 ; yes, end loop
cp CR ; end of line?
jr z,xstr5
ld (de),a ; otherwise keep copying
inc hl
inc de
jr xstr2 ; loop
xstr3: xor a
ld (de),a ; append a zero char
inc de
inc hl
ld a,c
cp "'" ; ' char?
ld a,T.SNGQ ; use ' token as the delimiting quote
jr z,xstr4
ld a,T.DBLQ ; else use " token
xstr4: ld (de),a ; add the delimiting quote
inc de
jp trnxt
; Unterminated string: replace the quote token + "text" token by a
; "text" token followed by the quote.
xstr5: ld l,e
ld h,d
ld (hl),0 ; append a zero char
inc hl
ld (hl),T.EOL ; and a '\' token
inc hl
xstr6: dec de
ld a,(de)
cp T.TEXT ; search for the "text" token
jr nz,xstr6
ld a,c ; get quote char (still in C)
ld (de),a ; replace the "text" token with the quote
dec de
ld a,T.TEXT
ld (de),a ; and replace the quote token with a "text"
ret ; return with HL pointing after end of line
; Process FN token
xfn: call GETNB ; get next non-blank or CR character
call ISLETR ; must be alphabetic (function name)
jr c,badfn ; error if not
ld c,a ; BC = function name
ld b,0
inc hl
res 5,c ; convert to uppercase
call GETNB ; get next non-blank or CR character
cp '$' ; string function?
jr z,xfn1 ; jump if yes, use '$' as second char of name
cp '%' ; integer function?
jr nz,xfn2 ; jump if not
xfn1: ld b,(hl) ; set second char of name (FN type)
inc hl
xfn2: ld a,(hl) ; get next char
cp '(' ; must be left parenthesis
jr z,xfn3
dec hl
jr badfn ; else error
xfn3: push hl
push de
ld de,(DEFTAB) ; scan DEFTAB
xfn4: ld hl,(LINTAB) ; up to LINTAB
call CPHLDE
jr nc,xfn6 ; exit loop if DE >= LINTAB
ld a,(de) ; compare name
cp c
jr nz,xfn5
inc de
ld a,(de)
dec de
cp b
jr z,xfn7 ; found
xfn5: inc de ; else skip this entry
inc de
inc de
inc de
jr xfn4 ; loop
xfn6: push bc ; function not found, push function name
ld c,e ; BC = end of DEFTAB
ld b,d
ld de,4 ; DE = size
call MOVRGN ; alloc 4 bytes for FN table entry
ld l,e
ld h,d
pop bc ; pop function name
ld (hl),c ; store function name
inc hl
ld (hl),b
inc hl
ld a,0FFh
ld (hl),a ; followed by 0FFFFh
inc hl
ld (hl),a
ld bc,4
ld hl,(LINTAB)
add hl,bc
ld (LINTAB),hl ; update LINTAB
ld hl,(SYMBOL)
add hl,bc
ld (SYMBOL),hl ; and SYMBOL start addresses
xfn7: ld hl,(DEFTAB)
ex de,hl
or a
sbc hl,de ; HL = DE - DEFTAB (FN offset into DEFTAB)
pop de
ld a,h
ld (de),a ; store offset in translated line (note order)
inc de
ld a,l
ld (de),a
inc de
pop hl ; restore input pointer
jp trnxt ; and resume scanning
badfn: dec hl
ld (hl),'N' ; FN error
dec hl
ld (hl),'F'
dec de
ld c,0FFh ; do not compress spaces
jr xlit ; store the rest of the line as text
; Process CALL token
xcall: xor a
ld (de),a
inc de
ld (de),a
inc de
ld (de),a
inc de
call GETNB ; get next non-blank or CR character
cp "'"
jr z,xnext1
cp '"'
jr z,xnext1
ld c,0 ; compress spaces
jr xlit ; store the rest of the line as text
; Process NEXT token
xnext: xor a
ld (de),a
inc de
ld (de),a
push hl
ld hl,9
add hl,de
ex de,hl
pop hl
xnext1: call CPHLDE
jr nc,errlng ; -> line too long to translate
jp trnxt
errlng: rst 10h
db 0Fh ; line too long to translate
; Check for variable name
xvar: push hl ;!!!TODO!!! write this better
push de
push ix
pop hl
ld de,OPTEND ; end of operator table?
call CPHLDE
pop de
pop hl
ld c,0FFh ; (do not compress spaces)
jr z,xlit ; if yes, add the rest of the line unmodified
ld (T4),hl ; otherwise it was alphabetic (var name?)
ld a,(hl) ; get first character
inc hl
and 5Fh ; convert to uppercase
ld c,a
ld b,0
call GETNB ; get next non-blank or CR character
call ISDIG ; number?
jr nc,xvar2 ; jump if yes
call ISLETR ; is alphabetic?
jr c,xv21 ; jump if not
push hl
ld hl,(T2)
call CPHLDE
pop hl
jr z,xv1
ld a,(T1)
or a
jr z,xv21
xv1: ld hl,(T4)
ld c,0 ; compress spaces
xlit: ld a,T.TEXT
ld (de),a ; add "text" token
inc de
call CPHLDE
jr nc,errlng ; -> line too long to translate
xlit1: ld a,(hl)
inc hl
cp CR ; CR
jr z,xlit3
cp '\'
jr z,xlit3
cp '('
jr z,xlit3
cp ' ' ; space
jr nz,xlit2
inc c ; check flag in C
dec c ; zero?
jr z,xlit1 ; ignore (compress) spaces if yes
xlit2: ld (de),a ; store char
inc de
jr xlit1 ; continue until delimiter found
xlit3: xor a
ld (de),a ; end with a zero byte
inc de
dec hl
jp trnxt
; variable name of type letter + number (first char in C)
xvar2: ld b,(hl) ; get second char
inc hl
xv21: push de
ld de,(SYMBOL) ; DE = SYMBOL (ptr to variable area?)
push hl ; allocate 2 words on the stack
call GETNB ; get next non-blank or CR character
xv22: push hl
ld hl,(LOFREE)
call CPHLDE ; end of variable area reached?
pop hl ; (NC if DE >= LOFREE+1)
jp nc,addvar ; (if DE > LOFREE) jump if yes (variable not found)
push de
pop ix
ex de,hl
call SKPSYM ; skip to next variable
ex de,hl
dec de
ld a,(de)
cp b ; same name?
inc de
jr nz,xv22 ; no, keep searching
dec de ;!!!TODO!!! write this better
dec de
ld a,(de)
cp c
inc de
inc de
jr nz,xv22 ; no, keep searching
pop af
push hl ; remember this line position
ld a,(ix+0)
and 03h
jr z,xv24
ld a,(hl)
cp '$' ; '$'
jr z,xv22 ; loop if yes
cp '%' ; '%'