-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCTS256A.ASM
1644 lines (1445 loc) · 54.6 KB
/
CTS256A.ASM
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
;** TMS-7000(tm) DISASSEMBLER V1.10beta3 - (c) 2015-20 GmEsoft, All rights reserved. **
;
; Tue Dec 27 08:39:46 2022
;
; Disassembly of : CTS256A.BIN
; Equates file : CTS256A.EQU
; Screening file : CTS256A.SCR
;==============================================================================
; registers used by the text-to-speech code in the masked ROM...
REGA EQU R0 ; A register
XREGA EQU >0000 ; A register long address
REGB EQU R1 ; B register
XREGB EQU >0001 ; B register long address
INRDH EQU R2 ; input buffer read pointer MSB
INRDL EQU R3 ; input buffer read pointer LSB
INRD EQU R3 ; input buffer read pointer
INWRH EQU R4 ; input buffer write pointer MSB
INWRL EQU R5 ; input buffer write pointer LSB
INWR EQU R5 ; input buffer write pointer
OUTRDH EQU R6 ; output buffer read pointer MSB
OUTRDL EQU R7 ; output buffer read pointer LSB
OUTRD EQU R7 ; output buffer read pointer
OUTWRH EQU R8 ; output buffer write pointer MSB
OUTWRL EQU R9 ; output buffer write pointer LSB
OUTWR EQU R9 ; output buffer write pointer
FLAGS1 EQU R10 ; flags 1
FLAGS2 EQU R11 ; flags 2
BUFPTRH EQU R12 ; buffer pointer MSB
BUFPTRL EQU R13 ; buffer pointer MSB
BUFPTR EQU R13 ; buffer pointer pair
XBUFPTR EQU >000D ; temp register pair long address
INRDPH EQU R14 ; next input buffer read pointer MSB
INRDPL EQU R15 ; next input buffer read pointer LSB
INRDP EQU R15 ; next input buffer read pointer
INRDBH EQU R16 ; backward input buffer read pointer MSB
INRDBL EQU R17 ; backward input buffer read pointer LSB
INRDB EQU R17 ; backward input buffer read pointer
PEPROMH EQU R18 ; EPROM scan pointer MSB
PEPROML EQU R19 ; EPROM scan pointer LSB
PEPROM EQU R19 ; EPROM scan pointer
INRDLH EQU R18 ; last input buffer read pointer MSB
INRDLL EQU R19 ; last input buffer read pointer LSB
INRDLX EQU R19 ; last input buffer read pointer
RULPTRH EQU R20 ; Rules pointer MSB
RULPTRL EQU R21 ; Rules pointer MSB
RULPTR EQU R21 ; Rules pointer
NXTCHR EQU R22 ; Next character (-$20 & $3F)
COUNTER EQU R23 ; Counter (RAM pages or chars)
LSTDLMH EQU R24 ; last delimiter position MSB
LSTDLML EQU R25 ; last delimiter position LSB
LSTDELM EQU R25 ; last delimiter position
ISP0256 EQU R27 ; SP0256 write address
INRDLBH EQU R28 ; last backward input buffer read pointer MSB
INRDLBL EQU R29 ; last backward input buffer read pointer LSB
INRDLB EQU R29 ; last backward input buffer read pointer
INLWMH EQU R30 ; input buffer low water mark MSB
INLWML EQU R31 ; input buffer low water mark LSB
INLWMX EQU R31 ; input buffer low water mark
INHWMH EQU R32 ; input buffer low water mark MSB
INHWML EQU R33 ; input buffer low water mark LSB
INHWMX EQU R33 ; input buffer low water mark
INSTA1H EQU R34 ; input buffer start - 1 MSB
INSTA1L EQU R35 ; input buffer start - 1 LSB
INSTA1 EQU R35 ; input buffer start - 1
INENDH EQU R36 ; input buffer end MSB
INENDL EQU R37 ; input buffer end LSB
INEND EQU R37 ; input buffer end
OUTEN1H EQU R38 ; output buffer end + 1 MSB
OUTEN1L EQU R39 ; output buffer end + 1 LSB
OUTEND1 EQU R39 ; output buffer end + 1
INSTAH EQU R40 ; input buffer start MSB
INSTAL EQU R41 ; input buffer start LSB
INSTA EQU R41 ; input buffer start
OUTSTAH EQU R42 ; output buffer start MSB
OUTSTAL EQU R43 ; output buffer start LSB
OUTSTA EQU R43 ; output buffer start
PSP0256 EQU R45 ; SP0256 base pointer
PPARINP EQU R47 ; Parallel input base pointer
JEPROMH EQU R48 ; EPROM jump vector MSB
JEPROM EQU R49 ; EPROM jump vector
OUTHWM EQU R50 ; output buffer high water mark
INFREEH EQU R51 ; number of free bytes in input buffer MSB
INFREEL EQU R52 ; number of free bytes in input buffer LSB
INFREE EQU R52 ; number of free bytes in input buffer
OUTFREH EQU R53 ; number of free bytes in output buffer MSB
OUTFREL EQU R54 ; number of free bytes in output buffer LSB
OUTFREE EQU R54 ; number of free bytes in output buffer
MATCHED EQU R55 ; matched chars counter
INLENH EQU R56 ; number of bytes in input buffer MSB
INLENL EQU R57 ; number of bytes in input buffer LSB
INLEN EQU R57 ; number of bytes in input buffer
; I/O ports...
IOCNT0 EQU P0 ; I/O Control register 0
APORT EQU P4 ; Port A
BPORT EQU P6 ; Port B
IOCNT1 EQU P16 ; I/O Control register 1
SSTAT EQU P17 ; read - Serial port Status
SMODE EQU P17 ; 1st write - Serial port Mode
SCTL0 EQU P17 ; 2nd and subsequent writes - Serial port Control register 0
T3DATA EQU P20 ; Timer 3 Data
SCTL1 EQU P21 ; Serial Control register 1
RXBUF EQU P22 ; Serial Receive Buffer
TXBUF EQU P23 ; Serial Transmit Buffer
;==============================================================================
AORG >F000
;==============================================================================
; Main entry point
CTS256 MOV %>3A,B
LDSP ; Init stack pointer 3B-XX
MOVD %SP0256,PSP0256 ; PSP0256 := $2000 - Memory-mapped parallel output to SP0256A-AL2
MOVP %>AA,IOCNT0 ; IOCNT0 = IOCNT0 := 1010 1010
; Full Expansion;
; Clear INT1, INT2 and INT3 flags
MOVP %>0A,IOCNT1 ; IOCNT1 = IOCNT1 := 0000 1010
; Clear INT4 and INT5 flags
MOVP APORT,B ; Read APORT = APORT
AND %>07,B ; Get Serial mode
CMP %>00,B ; Is it Parallel mode?
; useful?
; Jump if yes
JZ PARALL ; Start in parallel mode
AND %>7F,FLAGS1 ; Clear FLAGS1.7, indicating serial mode
MOVP APORT,A ; Read APORT = APORT
AND %>08,A ; Get Selectable Serial Config flag
CMP %>00,A ; Is it set?
; useful?
; Jump if not
JZ SER7N2 ; Serial fixed 7N2 config
LDA @>1000 ; Read serial config from $1000
MOVP A,SMODE ; Init P17 = SMODE with serial config
JMP SERSEL ; Serial selectable config
SER7N2 MOVP %>CB,SMODE ; P17 = SMODE := Fixed Serial 7N2 config
SERSEL MOVP %>15,SCTL0 ; P17 = SCTL0 := OOO1 O1O1
; Reset error flags; enable RX & TX
; A := value for SCTL1 (prescaler)
LDA @SCT1TB(B) ; Table of values for SCTL1
PUSH A
; A := value for T3DATA (timer3 reload)
LDA @T3DATB(B) ; Table of timer3 reload values T3DATA
POP B
MOVP B,SCTL1 ; set SCTL1
MOVP A,T3DATA ; set T3DATA
ORP %>01,IOCNT1 ; enable T4
; proceed with RAM setup
JMP INIRAM ; Init RAM config
; Table of values for SCTL1
SCT1TB BYTE >FF,>40,>43,>40,>43,>40,>40,>40
; Table of timer3 reload values T3DATA
T3DATB BYTE >FF,>20,>57,>07,>C2,>0F,>81,>03
; Start in parallel mode
PARALL OR %>80,FLAGS1 ; Set FLAGS1.7, indicating parallel mode
MOVD %PARINP,PPARINP ; PPARINP := $0200 parallel input mapped address
ORP %>30,IOCNT0 ; IOCNT0 = IOCNT0, set .4=enable INT3, .5=clear INT3
; Init RAM config
INIRAM MOVP APORT,B ; B := APORT = APORT
AND %>10,B ; test APORT.4 = RAM buffer - 0=internal - 1=external
CMP %>00,B ; useful?
; jump if internal
JZ INTRAM ; init internal RAM pointers
MOVD %CTSXRAM,INRD ; INRD := base RAM address = $3000
MOVD INRD,INSTA ; INSTA := INRD
PUSH INRDH
DEC INRDH
MOV INRDH,INSTA1H ; INSTA1 := INRD - 1 ($2FFF)
MOV %>FF,INSTA1L
ADD %>02,INRDH ; INRDH += 2 ($31)
CLR COUNTER ; COUNTER := 0 (page counter)
; Init external RAM sizing loop
IRAML0 INC INRDH ; ++INRDH
CMP %>F0,INRDH ; INRDH == $F0 ($F000 reached)?
; Exit loop if yes
JZ IRAMX0
INC COUNTER ; ++COUNTER
CMP %>10,COUNTER ; COUNTER == $10 (16 pages)?
; exit loop if yes
JZ IRAMX0
MOV %>5A,A ; A := $5A (RAM test pattern)
STA *INRD ; *INRD := A
CLR A ; A := 0 (useful?)
LDA *INRD ; A := *INRD
CMP %>5A,A ; A == $5A (test pattern)?
; exit loop if not
JNZ IRAMX0
SWAP A ; A := $A5 (reverse bits)
STA *INRD ; *INRD := A
CLR A ; A := 0 (useful?)
LDA *INRD ; A := *INRD
CMP %>A5,A ; A == $A5 (test pattern)?
; exit loop if not
JNZ IRAMX0
; Test next RAM page
JMP IRAML0
IRAMX0 MOV INRDH,OUTEN1H ; OUTEND1 := INRDH:$00 (end RAM addr + 1)
CLR OUTEN1L ; ATTENTION: page boundary !!
SUB %>01,INRDH ; --INRDH (why not DEC INRDH?)
MOVD INRD,OUTRD ; OUTRD := INRD = last RAM page addr (output buffer read ptr)
MOVD OUTRD,OUTSTA ; OUTSTA := OUTRD (output buffer start)
SUB %>01,INRDH ; --INRDH (why not DEC INRDH?)
MOV INRDH,INENDH ; INEND := INRDH:$FF (input buffer end)
MOV %>FF,INENDL
POP INRDH ; INRDH := $30
MOV %>DF,OUTHWM ; OUTHWM := $DF
JMP INIROM ; Init EPROM
; init internal RAM pointers
INTRAM MOVD %>0051,INSTA ; INSTA := $0051 (input buffer start)
MOVD %>0065,INEND ; INEND := $0065 (input buffer end)
MOVD %>0050,INSTA1 ; INSTA1 := $0050 (input buffer start - 1)
MOVD %>0066,OUTSTA ; OUTSTA := $0066 (output buffer start)
MOVD %>0080,OUTEND1 ; OUTEND1 := $0080 (end RAM addr + 1)
MOVD INSTA,INRD ; INRD := $0051 (input buffer read ptr)
MOVD OUTSTA,OUTRD ; OUTRD := $0066 (output buffer read ptr)
MOV %>01,OUTHWM ; OUTHWM := $01 (output high water mark)
; Init EPROM
INIROM MOVD %>0000,PEPROM ; PEPROM := 0
LF0DA CLR B ; B := 0 (5 signature bytes counter/index)
ADD %>10,PEPROMH ; ++PEPROMH (next page, starting at $1000)
CMP %>F0,PEPROMH ; $F0 reached? (ending at $F000)
; Jump if yes
JZ INITOK ; Done peripherals init
LF0E3 LDA *PEPROM ; Read signature byte
; and compare it to the 5 first letters flags $80,$48,$28,$58,$85
CMPA @LTFLGS(B) ; Letter flags
; next page if mismatch
JNZ LF0DA
INC B ; next test byte
CMP %>05,B ; done 5 bytes
; Exit if yes => EPROM found
JZ STAROM ; Boot in EPROM
INC PEPROML ; next EPROM byte
JMP LF0E3 ; loop
; Boot in EPROM
STAROM INC PEPROML ; point to 1st byte following sig bytes
MOVD PEPROM,JEPROM ; JEPROM := PEPROM = EPROM entry point
BR *JEPROM ; Boot in EPROM
; Done peripherals init
INITOK MOV %>00,JEPROMH ; JEPROMH := 0 (EPROM not present)
CALL @INIPTR ; Init buffer pointers and regs
CALL @SAYOK ; Say O.K.
JMP LF110
;==============================================================================
; Polling loop
POLL BTJO %>01,FLAGS2,LF110 ; test FLAGS2.0 = any delimiter; jump if yes
AND %>EF,FLAGS2 ; reset FLAGS2.4 (found delimiter?)
; Idle loop (wait interrupt?)
WTIDLE BTJZ %>10,FLAGS2,WTIDLE ; Idle loop (wait FLAGS2.4)
LF110 CMP INRDL,INWRL ; Input buffer read ptr LSB != write ptr LSB?
JNZ ENDPOL ; Exit polling loop if yes
CMP INRDH,INWRH ; Idem for MSB?
JNZ ENDPOL ; Exit polling loop if yes
JMP POLL ; Polling loop
; Exit polling loop
ENDPOL CMP %>00,INLENH ; Chars in buffer MSB
JNZ LF126 ; exit ENDPOL loop if != 0
CMP %>00,INLENL ; Chars in buffer LSB
JZ ENDPOL ; loop if == 0
; Exit ENDPOL loop
LF126 BTJZ %>08,FLAGS2,LF133 ; FLAGS2.3 == 1 (output buffer full or too high)? Jump if not
CMP %>01,OUTHWM ; Internal RAM?
; jump if yes
JZ CLBUF1 ; Clear buffers if yes
; wait reset of bit 3 of FLAGS2
W11B3 BTJO %>08,FLAGS2,W11B3 ; wait reset of bit 3 of FLAGS2 (output buffer low enough)
LF133 CALL @ENCODE ; Encode text to allophones
CMP OUTRDL,OUTWRL ; Output buffer empty?
JZ LF110 ; jump to input buffer polling loop if yes
ORP %>01,IOCNT0 ; enable INT1* (SP0256) to send output
JMP LF110 ; jump to input buffer polling loop
; Clear buffers
CLBUF1 BR @CLBUF ; clear buffers - reinit
;==============================================================================
; Init buffer pointers and regs
INIPTR AND %>00,FLAGS2 ; FLAGS2 := 0 (why not CLR FLAGS2?)
CLR MATCHED ; MATCHED := 0 (matched chars to discard)
CLR INLENH ; INLENH := 0
CLR INLENL ; INLENL := 0
ORP %>01,BPORT ; set BPORT.0 (DSR/BUSY)
MOVD INSTA,INRD ; INRD := INSTA (input buffer start)
MOVD OUTSTA,OUTRD ; OUTRD := OUTSTA (output buffer start)
MOV %>20,A ; A := $20
STA *INRD ; *INRD := $20 (input buffer read ptr)
CALL @INCINRD ; inc INRD and roll to INSTA if == OUTSTA
MOVD INRD,INWR ; INWR := INRD (input buffer write ptr)
PUSH INRDL ; save pointers
PUSH OUTRDL
PUSH OUTRDH
DEC INRDL ; OUTRD := ( OUTRD - INRD + 1 ) = input buffer size
SUB INRDL,OUTRDL
SBB INRDH,OUTRDH
MOVD OUTRD,INFREE ; INFREE := input buffer size - 1
DECD INFREE
TSTA ; = CLR C
RRC OUTRDH ; OUTRD /= 2
RRC OUTRDL
MOVD OUTRD,INLWMX ; INLWMX := buffer size / 2 (and clears C)
RRC OUTRDH ; OUTRD /= 2
RRC OUTRDL
TSTA ; = CLR C
RRC OUTRDH ; OUTRD /= 2
RRC OUTRDL
MOVD OUTRD,INHWMX ; INHWMX := buffer size / 8
POP OUTRDH ; restore pointers
POP OUTRDL
POP INRDL
MOVD OUTRD,OUTWR ; OUTWR := OUTRD = output buffer write ptr
MOVD INRD,LSTDELM ; LSTDELM last delimiter ptr := INRD input buffer read ptr
MOVD OUTEND1,OUTFREE ; OUTFREE := OUTEND1 = end RAM address + 1
SUB OUTSTAL,OUTFREE ; OUTFREE := ( OUTFREE - OUTSTA ) = output buffer size
SBB OUTSTAH,OUTFREH
MOVP APORT,B ; B := APORT
AND %>80,B ; APORT.7 set? (Delimiter=any)
CMP %>00,B ; useful?
; Jump if not
JZ LF1A6
OR %>01,FLAGS2 ; FLAGS2.0 := 1 if any delimiter
LF1A6 EINT ; enable interrupts
RETS
;==============================================================================
; "O-K\n"
STROK TEXT 'O-K'
BYTE >0D
; Say O.K.
AUDIBLE ; from datasheet
SAYOK AND %>F9,FLAGS1 ; Clear FLAGS1.1 and FLAGS1.2: Write to input buffer
CLR B
LF1B0 LDA @STROK(B) ; Get char
CALL @STINPB ; Store char in input buffer
INC B ; next char
CMP %>04,B ; loop until 4 chars processed
JNZ LF1B0
RETS ; return
;==============================================================================
; INT4 handler (serial interrupt)
INT4 BTJOP %>02,SSTAT,INT3 ; Jump if P17.1 (RXRDY) == 1
RETI ; else return from interrupt
; INT3 handler (parallel interrupt)
INT3 PUSH A ; save A from interrupt
BTJO %>80,FLAGS1,LF1D1 ; FLAGS1.7 (parallel mode) == 1? Jump if yes
ANDP %>FE,IOCNT1 ; Reset IOCNT1.0 (disable serial INT4)
; wait RXBUF ready with new character
WRXRDY BTJZP %>02,SSTAT,WRXRDY ; wait RXBUF ready with new character
MOVP RXBUF,A ; get A := RXBUF incoming character
JMP LF1D6 ; handle incoming char in A
; Parallel mode
LF1D1 ANDP %>EF,IOCNT0 ; Disable IOCNT0.0 = INT3*
LDA *PPARINP ; Read char from parallel input ($0200)
; Handle incoming char
LF1D6 CALL @STINPB ; Store char in input buffer
POP A ; restore A from interrupt
BTJO %>20,FLAGS2,LF1E1 ; FLAGS2.5 == 1? (input buffer full) skip next instr if yes
CALL @ENINT ; re-enable parallel or serial interrupt
LF1E1 RETI ; return from interrupt
;==============================================================================
; Store char in input buffer
STINPB PUSH B ; Save registers
PUSH FLAGS1
PUSH BUFPTRH
PUSH BUFPTRL
AND %>F9,FLAGS1 ; reset FLAGS1.1 and FLAGS1.2: write to input buffer
CMP %>1B,A ; <ESC>? => clear buffers
JNZ NOTESC ; Skip if not
; clear buffers - reinit
CLBUF ANDP %>FE,IOCNT0 ; Disable INT1* (SP0256)
CALL @INIPTR ; Init buffer pointers and regs
MOV %>3A,B ; Init SP
LDSP ;
MOVD PSP0256,ISP0256 ; R26:27 := R44:45 (SP0256 device)
STA *ISP0256 ; *R26:27 := A (SP0256 device)
CALL @ENINT ; Enable input interrupt (parallel or serial)
BR @POLL ; Polling loop
; Not <ESC>
NOTESC CMP %>12,A ; <Ctrl-R>? => backspace until last delimiter
JNZ NOTCTR ; Skip if not
; Handle <Ctrl-R>
BTJO %>01,FLAGS2,LF21E ; FLAGS2.0 == 1 (any delimiter)? Exit if yes
SUB LSTDLML,INRDL ; INRD (input buf read ptr) -= LSTDELM (last delimiter)
SBB LSTDLMH,INRDH ; (may not work correctly if rolled...)
SUB INRDL,INFREEL ; INFREE (input buffer free size) -= INRD
SBB INRDH,INFREEH
MOVD LSTDELM,INRD ; INRD := LSTDELM (last delimiter position)
MOV %>01,INLENL ; input buffer counter LSB(why?) := 1
LF21E BR @XSTINP ; exit handler
; Not <Ctrl-R>
NOTCTR CMP %>08,A ; <BkSp>?
JNZ NOTBKS ; Skip if not
; Handle <BkSp>
CMP INRDH,INWRH ; Input buffer pointers equal?
JNZ LF22F
CMP INRDL,INWRL
JZ XSTINP ; If yes, exit handler
LF22F PUSH INRDL ; Save INRD
PUSH INRDH
MOVD INWR,INRD ; INRD := INWR input buffer write ptr
CALL @DECINRD ; dec INRD and roll if needed
MOVD INRD,INWR ; INWR := INRD
POP INRDH ; restore INRD
POP INRDL
INC INFREEL ; Inc INFREE input buffer free size
JNC XSTINP
INC INFREEH
JMP XSTINP ; exit handler
; Not <BkSp>: Handle other chars
NOTBKS CMP %>27,A ; '''?
JZ STOCHR ; Jump if yes
CMP %>7B,A ; higher than 'z'?
JP DELIMT ; Jump if yes
CMP %>30,A ; '0'..'9'?
JN DELIMT
CMP %>3A,A
JN STOCHR
CMP %>41,A ; >= 'A'?
JPZ STOCHR
; Delimiter (not letter/digit/')
DELIMT BTJO %>01,FLAGS2,LF267 ; FLAGS2.0 set (any delimiter)? Jump if yes
CMP %>0D,A ; <CR>?
JNZ LF26A ; skip if not
OR %>10,FLAGS2 ; FLAGS2.4 := 1 (found CR => exit idle loop)
LF267 MOVD INRD,LSTDELM ; LSTDELM := INRD (last delimiter position)
LF26A OR %>80,A ; A.7 := 1 (set high bit) if delimiter
INC INLENL ; Increment INFREE (# of bytes in input buffer)
JNC STOCHR ; put char in buffer
INC INLENH
; put char in buffer
STOCHR CALL @RWBUFR ; write char in buffer
BTJZ %>20,FLAGS2,XSTINP ; FLAGS2.5 set (inp buffer full)? if not exit handler
MOV %>8D,A ; A := <CR> + $80
INC INLENL ; Increment INFREE (# of bytes in input buffer)
JNC DINC1
INC INLENH
DINC1 CALL @RWBUFR ; write char in buffer
; exit handler
XSTINP POP BUFPTRL ; Restore registers
POP BUFPTRH
POP FLAGS1
POP B
RETS ; end of input char handling
;==============================================================================
; FLAGS1:7 ? (enable INT3) : (enable INT4)
ENINT BTJZ %>80,FLAGS1,ENINT4 ; enable INT4
ORP %>10,IOCNT0
RETS
; enable INT4
ENINT4 ORP %>01,IOCNT1
RETS
;==============================================================================
; select input or output buffer and read/store byte in it
RWBUFR BTJO %>02,FLAGS1,RDBUF ; FLAGS1.1 set? Jump if yes (read from either buffer)
; write to input or output buffer
BTJO %>04,FLAGS1,WROBUF ; FLAGS1.2 set? Jump if yes (output buffer)
; write to input buffer
MOVD INWR,BUFPTR ; BUFPTR := INWR input buffer write ptr
DECD INFREE ; Decrement # of free input buffer bytes
JMP WRBUF ; Store A in buffer
; write to output buffer
WROBUF MOVD OUTWR,BUFPTR ; BUFPTR := OUTWR output buffer write ptr
DECD OUTFREE ; Decr output buffer free size
; Write byte A to buffer
WRBUF STA *BUFPTR ; *BUFPTR := A (store byte in buffer)
LDA @XBUFPTR ; A := BUFPTR (why using LDA?)
ADD %>01,BUFPTRL ; Increment BUFPTR
ADC %>00,BUFPTRH
CALL @ROLBUFP ; Roll BUFPTR if needed
BTJO %>04,FLAGS1,WOBUFX ; FLAGS1.2 set? Jump if yes (output buffer)
MOVD BUFPTR,INWR ; INWR := BUFPTR (inp buffer write ptr)
WRBUFX CALL @CHKBUF ; Check if inp or out buffer full or above HWM
RETS ; return
WOBUFX MOVD BUFPTR,OUTWR ; OUTWR := BUFPTR (out buffer write ptr)
JMP WRBUFX ; Check buffer and return
; read from input or output buffer
RDBUF BTJO %>04,FLAGS1,RDOBUF ; FLAGS1.2 set? jump if yes (output buffer)
; read from input buffer
MOVD INRD,BUFPTR ; BUFPTR := INRD input buffer read ptr
BTJO %>02,FLAGS2,RDBUF1 ; FLAGS2.1 set? Jump if yes
INC MATCHED ; inc MATCHED if not... (matched chars to discard)
JMP RDBUF1 ; Read byte
; read from output buffer
RDOBUF MOVD OUTRD,BUFPTR ; BUFPTR := OUTRD out buffer read ptr
INC OUTFREL ; Inc OUTFREE = Out free bytes? (TODO: clarify)
JNC RDBUF1
INC OUTFREH
; Read A from buffer
RDBUF1 LDA *BUFPTR ; Read byte
BTJO %>04,FLAGS1,LF2F3 ; FLAGS1.2 set? jump if yes (out buffer)
BTJZ %>80,A,LF2F0 ; Is high bit set? Jump if not
OR %>01,FLAGS1 ; FLAGS1.0 := A.7 = char high bit = delimiter flag
JMP LF2F3
LF2F0 AND %>FE,FLAGS1 ; clear FLAGS1.0 = delimiter flag
; Incr buffer read pointer
LF2F3 PUSH A
LDA @XBUFPTR ; A := BUFPTR (why?)
ADD %>01,BUFPTRL ; increment BUFPTR
ADC %>00,BUFPTRH
CALL @ROLBUFP ; Roll if needed
BTJO %>04,FLAGS1,ROBUFX ; jump if output buffer
; update input buffer read ptr
MOVD BUFPTR,INRD ; INRD := BUFPTR
POP A ; restore character
RETS ; return
; update output buffer read ptr
ROBUFX MOVD BUFPTR,OUTRD ; OUTRD := BUFPTR
CALL @CKOBUF ; Check if output buffer full or above HWM
POP A ; restore allophone
RETS
;==============================================================================
; Roll BUFPTR according to active buffer (FLAGS1.2)
; - if FLAGS1.2 set (output buffer active):
; to OUTSTA (out buffer start) if reached OUTEND1 (out buffer end)
; - if FLAGS1.2 not set (input buffer active):
; to INSTA (in buffer start) if reached OUTSTA (in buffer end)
ROLBUFP BTJO %>04,FLAGS1,ROLOUT ; Roll output buffer
; Roll input buffer
ROLINP CMP OUTSTAL,BUFPTRL
JNZ LF322
CMP OUTSTAH,BUFPTRH
JNZ LF322
MOVD INSTA,BUFPTR
LF322 RETS
; Roll output buffer
ROLOUT CMP OUTEN1L,BUFPTRL
JNZ LF330
CMP OUTEN1H,BUFPTRH
JNZ LF330
MOVD OUTSTA,BUFPTR
LF330 RETS
;==============================================================================
; Check if inp or out buffer full or above HWM
CHKBUF BTJZ %>04,FLAGS1,CKIBUF ; Send XON/XOFF if needed
; Check if output buffer full or above high water mark
CKOBUF CMP %>01,OUTFREH ; Only 1 free byte in output buffer?
JZ LF343 ; jump if yes
CMP OUTHWM,OUTFREL ; OUTFREE > OUTHWM? (output counter >= HWM?)
JP LF343 ; jump if yes
OR %>08,FLAGS2 ; set FLAGS2.3 (output buffer full)
RETS
LF343 AND %>F7,FLAGS2 ; reset FLAGS2.3 (output buffer not full)
RETS
; Check if input buffer full or above high water mark
CKIBUF CMP %>00,INFREEH ; Free bytes in input buffer?
JP LF356 ; jump if yes
CMP %>01,INFREEL
JP LF356
OR %>20,FLAGS2 ; set FLAGS2.5 (input buffer full)
JMP TXOFF ; Send XOFF, clear DTR/BUSY*
; Input buffer not full
LF356 CMP INLWMH,INFREEH ; INFREE inp buf free bytes < 1/2 size?
JN LF362 ; Jump if yes
JP TXON ; Else send XON, set DTR/BUSY*
CMP INLWML,INFREEL
JPZ TXON
LF362 CMP INHWMH,INFREEH ; INFREE inp buf free bytes < 1/8 size?
JP LF384 ; Jump if not
JN LF36E ; Else Send XOFF, clear DTR/BUSY*
CMP INHWML,INFREEL
JP LF384
LF36E OR %>04,FLAGS2 ; set FLAGS2.2: input buffer above HWM
; Send XOFF, clear DTR/BUSY*
TXOFF ANDP %>FE,BPORT ; clear BPORT.0 (DSR/BUSY=true)
MOVP %>13,TXBUF ; TXBUF := XOFF
RETS
; Send XON, set DTR/BUSY*
TXON AND %>DB,FLAGS2
CALL @ENINT ; FLAGS1:7 = parallel mode ? (enable INT3) : (enable INT4)
ORP %>01,BPORT ; set BPORT.0 (DSR/BUSY=false)
MOVP %>11,TXBUF ; TXBUF := XON
LF384 RETS ; Return
;==============================================================================
; INT1 handler (SP0256)
INT1 ANDP %>FE,IOCNT0 ; Disable IOCNT0.0 = INT1*
PUSH A ; save regs
PUSH B
PUSH FLAGS1
PUSH BUFPTRH
PUSH BUFPTRL
OR %>06,FLAGS1 ; select "read from output buffer"
CALL @RWBUFR ; do the read
MOVD PSP0256,ISP0256 ; ISP0256 := PSP0256 SP0256 base address
ADD REGA,ISP0256 ; Add allophone code to the address
STA *ISP0256 ; Write any byte to that address (value doesn't care)
POP BUFPTRL ; restore regs
POP BUFPTRH
POP FLAGS1
POP B
POP A
CMP OUTRDL,OUTWRL ; Buffer empty?
JZ LF3AE ; skip if yes
ORP %>01,IOCNT0 ; else enable IOCNT0.0 = INT1*
LF3AE RETI ; return from interrupt
;==============================================================================
; select rules set
SELRUL CMP %>30,A ; A >='0'?
JPZ SELRUL1 ; jump if yes
JMP SELPCT ; < '0' => rules for punctuation
SELRUL1 CMP %>3A,A ; A >='9'+1?
JPZ SELRUL2 ; jump if yes
MOVD %RULNUM,RULPTR ; select rules for digits
AND %>DF,FLAGS1 ; Non-letters rules
RETS ; done
SELRUL2 CMP %>41,A ; A >='A'?
JPZ SELRUL3 ; jump if yes
JMP SELPCT ; < 'A' => rules for punctuation
SELRUL3 CMP %>5B,A ; A >='Z'+1?
JPZ SELRUL4 ; jump if yes
OR %>20,FLAGS1 ; Letters rules
RETS
SELRUL4 CMP %>61,A ; A >='a'?
JPZ SELRUL5 ; jump if yes
JMP SELPCT ; < 'a' => rules for punctuation
SELRUL5 CMP %>7B,A ; A >='z'+1?
JPZ SELPCT ; jump if yes
SUB %>20,A ; A -= $20 (convert to upper case)
OR %>20,FLAGS1 ; Letters rules
RETS ; done
SELPCT AND %>DF,FLAGS1 ; Non-letters rules
MOVD %RLPNCT,RULPTR ; Rules for punctuation
RETS
;==============================================================================
; Encode text to allophones
ENCODE CMP %>00,JEPROMH ; EPROM active?
; Skip if not
JZ ENCODE1
BR *JEPROM ; Jump in EPROM
ENCODE1 MOVD INRD,INRDB ; INRDB := INRD (backward input buffer read ptr)
CALL @DECINRB ; dec INRDB and roll if needed
; Main encoder loop
ENCODL1 CALL @FETCH ; Fetch char and mask high bit
BTJZ %>01,FLAGS1,ENCODE2 ; FLAGS2.7 := FLAGS1.0 (delimiter flag)
OR %>80,FLAGS2
JMP ENCODE3
ENCODE2 AND %>7F,FLAGS2
ENCODE3 CALL @SELRUL ; select rules set
BTJZ %>20,FLAGS1,ENCODE4 ; FLAGS1.5 set (rules for letters)? jump if no
CLR B ; useful?
SUB %>41,A ; A -= 'A'
MPY %>02,A ; AB := A * 2
ADD %>02,B ; B += 2
LDA @TABRUL(B) ; Index of rules tables
MOV A,RULPTRH ; RULPTRH := MSB of letter rules table
LDA @TABRU1(B) ; Index of rules tables LSB
MOV A,RULPTRL ; RULPTRL := LSB of letter rules table
ENCODE4 MOV %>01,B ; B := 1 (number of '[') to seek
CALL @RSEEKB ; right seek 1st '['
; Loop 2: Compare chars between [ ]
ENCODL2 CALL @CMPBKT ; compare in-brackets
BTJO %>10,FLAGS1,ENCODF1 ; FLAGS1.4 set (match failed)? Jump if yes
; Check patterns after ]
MOVD INRDB,INRDLB ; INRDLB := INRDB (save backward input buffer read ptr)
AND %>BF,FLAGS1 ; clear FLAGS1.6 (read input using INRD)
CALL @CHKPAT ; Check rule pattern
BTJO %>10,FLAGS1,ENCODF1 ; FLAGS1.4 set (match failed)? Jump if yes
; Check patterns before [
CALL @LSEEKB ; left seek '['
OR %>40,FLAGS1 ; set FLAGS1.6 (read input using INRDB)
CALL @CHKPAT ; Check rule pattern
BTJO %>10,FLAGS1,ENCODF2 ; FLAGS1.4 set (match failed)? Jump if yes
; Matching Rule found
ADD MATCHED,INFREEL ; INFREE += MATCHED (discard preceding chars)
ADC %>00,INFREEH
CLR MATCHED ; no more chars to discard
; Seek allophones to output
AND %>FD,FLAGS2 ; clear FLAGS2.1 (allow to count chars to discard in MATCHED)
MOV %>02,B ; B := 2 (number of '[') to seek
CALL @RSEEKB ; right seek 2nd '[' => 1st allophone
; Write allophones
CALL @WRALLO ; Write allophones
; Next input character
MOVD INRDP,INRD ; INRD := INRDP - restore input buffer read ptr
MOVD INRD,INRDB ; INRDB := INRD - copy to backward input buffer read ptr
CALL @DECINRB ; dec INRDB and roll to INEND if == INSTA1
BTJZ %>80,FLAGS2,ENCODL1 ; High bit of char was set (delimiter)? Jump if not
; Finalize and return
DECD INLEN ; dec INFREE (number of bytes in input buffer?)
CALL @CKIBUF ; Check if input buffer full or above high water mark
RETS ; return
; Match failed after opening [ => seek next rule
ENCODF1 INC RULPTRL ; inc RULPTR rule pointer
JNC DINC2
INC RULPTRH
DINC2 MOV %>02,B ; B := 2 (number of '[') to seek
; Loop: Seek next rule opening [
ENCODL3 CALL @RSEEKB ; right seek 2nd or 3rd '[' => next pattern '['
MOV %>01,MATCHED ; MATCHED := 1 (one char to discard)
AND %>FD,FLAGS2 ; clear FLAGS2.1 (allow to count chars to free in MATCHED)
JMP ENCODL2 ; check next rule
; Match failed before opening [ => seek next rule
ENCODF2 MOV %>03,B ; B := 3 (number of '[') to seek
JMP ENCODL3 ; right seek 3rd '[' and check next rule
;==============================================================================
; left seek '['
LSEEKB LDA *RULPTR ; A := *RULPTR
BTJZ %>40,A,LSEEBB1 ; A.6 set ('[' found)? jump if not
RETS ; else return
LSEEBB1 DECD RULPTR ; dec RULPTR
JMP LSEEKB ; and loop
;==============================================================================
; right seek Bth '['
RSEEKB CLR COUNTER ; COUNTER := 0
RSEEKB1 LDA *RULPTR ; A := *RULPTR
BTJZ %>40,A,RSEEKB2 ; A.6 set ('[' found)? jump if not
INC COUNTER ; inc COUNTER
CMP COUNTER,B ; jump if COUNTER != B
JNZ RSEEKB2
RETS ; else return: found
RSEEKB2 INC RULPTRL ; inc RULPTR
JNC DINC3
INC RULPTRH
DINC3 JMP RSEEKB1 ; and loop
;==============================================================================
; Write allophones
WRALLO CLR COUNTER ; COUNTER := 0 (flag for exit)
LDA *RULPTR ; A := *RULPTR
CMP %>FF,A ; A == $FF?
JZ WRALLOX ; exit if yes: no allophone to send
BTJZ %>80,A,WRALLO1 ; A.7 set (']' found)? jump if not
INC COUNTER ; else inc COUNTER
WRALLO1 AND %>3F,A ; mask A.6 and A.7
AND %>FD,FLAGS1 ; clear FLAGS1.1 (write in buffer)
OR %>04,FLAGS1 ; set FLAGS1.2 (output buffer)
CALL @RWBUFR ; write allophone in buffer
INC RULPTRL ; inc RULPTR
JNC DINC4 ;
INC RULPTRH ;
DINC4 CMP %>01,COUNTER ; ']' found?
JNZ WRALLO ; loop if not
WRALLOX RETS ; else return
;==============================================================================
; compare in-brackets
CMPBKT MOVD INRD,INRDLX ; INRDLX := INRD (input read ptr)
AND %>F7,FLAGS1 ; clear FLAGS1.3
BTJZ %>20,FLAGS1,CMPNLTR ; FLAGS1.5 letter rules? jump if not
LDA *RULPTR ; A := *RULPTR
CMP %>FF,A ; A == $FF? (single letter/end of rules)
JNZ CMPLTR ; jump if not to letter rules
JMP CMPEND ; else exit (success)
; not letter rules: check 1st char, otherwise implicit
CMPNLTR CMP %>FF,A ; A == $FF?
JZ CMPEND ; exit if yes (success)
CALL @DECINRD ; dec INRD and roll if needed
DEC MATCHED ; dec MATCHED (matched chars to discard)
; letter rules
CMPLTR CALL @FETCH ; Fetch char and mask high bit
CMP %>61,A ; lower case?
JN CMPLTR1 ; skip if not
SUB %>20,A ; else adjust to upper case
CMPLTR1 SUB %>20,A ; adjust to range 00-3F
MOV A,B ; B := A
LDA *RULPTR ; A := *RULPTR
BTJZ %>80,A,CMPLTR2 ; A.7 set (']' found)? Skip if not
OR %>08,FLAGS1 ; else set FLAGS1.3
CMPLTR2 AND %>3F,A ; mask A.6 and A.7
CMP REGA,B ; A == B?
JZ CPMATCH ; jump if yes
OR %>10,FLAGS1 ; set FLAGS1.4: pattern match failed
MOVD INRDLX,INRD ; INRD := INRDLX
RETS ; return (failed)
CPMATCH BTJZ %>08,FLAGS1,CMPNEXT ; FLAGS1.3 set (']' found)? Jump if not
; pattern match successful
CMPEND MOVD INRD,INRDP ; INRDP := INRD - save input buffer read ptr
AND %>EF,FLAGS1 ; clear FLAGS1.4: pattern match successful
OR %>02,FLAGS2 ; set FLAGS2.1 - forbid to count matched chars to discard
RETS ; return
; next letter
CMPNEXT INC RULPTRL ; inc RULPTR
JNC CMPLTR ;
INC RULPTRH ;
JMP CMPLTR ; and loop: check next char
;==============================================================================
; Get letter flags
GFLAGS MOV NXTCHR,B ; B := Next char
CMP %>3A,B ; B > 'Z'-$20?
JP GFLAGX ; exit if yes
CMP %>21,B ; B < 'A'-$20?
JN GFLAGX ; exit if yes
SUB %>21,B ; B -= 'A' (could replace the CMP above!)
LDA @LTFLGS(B) ; Letter flags
RETS ; done
GFLAGX CLR A ; non-letter => clear all flags
RETS ; done
; Letter flags
; ------------
; 7(80): Vowel
; 6(40): Voiced consonant
; 5(20): Sibilant
; 4(10): Preceding long U
; 3(08): Consonant
; 2(04): Front vowel
; 1(02): Back vowel
; 0(01): Suffix ('E')
LTFLGS BYTE >80 ; A 1000 0000 - EPROM sig check begin
BYTE >48 ; B 0100 1000
BYTE >28 ; C 0010 1000
BYTE >58 ; D 0101 1000
BYTE >85 ; E 1000 0101 - EPROM sig check end
BYTE >08 ; F 0000 1000
BYTE >68 ; G 0110 1000
BYTE >08 ; H 0000 1000
BYTE >84 ; I 1000 0100
BYTE >78 ; J 0111 1000
BYTE >08 ; K 0000 1000
BYTE >58 ; L 0101 1000
BYTE >48 ; M 0100 1000
BYTE >58 ; N 0101 1000
BYTE >82 ; O 1000 0010
BYTE >08 ; P 0000 1000
BYTE >08 ; Q 0000 1000
BYTE >58 ; R 0101 1000
BYTE >38 ; S 0011 1000
BYTE >18 ; T 0001 1000
BYTE >82 ; U 1000 0010
BYTE >48 ; V 0100 1000
BYTE >48 ; W 0100 1000
BYTE >28 ; X 0010 1000
BYTE >84 ; Y 1000 0100
BYTE >78 ; Z 0111 1000
;==============================================================================
; Pattern jump table
PATBRT BR @PATVOW ; # 09 1+ vowels
BR @PATVOC ; . 0A voiced consonant: B D G J L M N R V W X
BR @PATSUF ; % 0B suffix: -ER(S) -E -ES -ED -ELY -ING -OR -MENT
BR @PATSIB ; & 0C sibilant: S C G Z X J CH SH
BR @PATPLU ; @ 0D T S R D L Z N J TH CH SH preceding long U
BR @PAT1CO ; ^ 0E 1 consonant only
BR @PATFVO ; + 0F front vowel: E I Y
BR @PAT0MC ; : 10 0+ consonants
BR @PAT1MC ; * 11 1+ consonants
BR @PATBVO ; > 12 back vowel: O U
BR @PATNLT ; < 13 Anything other than a letter
BR @PAT2MV ; ? 14 2+ vowels
;==============================================================================
; Check rule pattern
CHKPAT BTJO %>40,FLAGS1,CKPATL1 ; FLAGS1.6 set (scan direction left)? jump if yes
MOV %>40,B ; '[' mask
INC RULPTRL ; inc RULPTR rule ptr
JNC DINC5
INC RULPTRH
DINC5 JMP CHKPAT1 ; proceed
CKPATL1 MOV %>80,B ; ']' mask
DECD RULPTR ; dec RULPTR rule ptr
; check if boundary has been reached
CHKPAT1 LDA *RULPTR ; get rule char
BTJZ B,A,CKPTCHR ; is '[' or ']'? jump if not
BTJO %>40,FLAGS1,CKPATL2 ; FLAGS1.6 set (scan direction left)? jump if yes
DECD RULPTR ; dec RULPTR rule ptr
JMP DINC6
CKPATL2 INC RULPTRL ; inc RULPTR rule ptr
JNC DINC6
INC RULPTRH
DINC6 AND %>EF,FLAGS1 ; clear FLAGS1.4 rule pattern match failed
RETS ; return
; check rule char
CKPTCHR CALL @GNEXT ; get next input char and move code to range 00-3F
LDA *RULPTR ; A := *RULPTR - load rule char
CMP %>15,A ; A < $15? (pattern symbol)
JN CHKSYMB ; jump if yes
CKNXCHR CMP REGA,NXTCHR ; otherwise compare input char with rule char
JZ CHKPAT ; continue with next char if both chars match
; pattern match failed
PATFLD OR %>10,FLAGS1 ; set FLAGS1.4 - match failed
MOVD INRDLX,INRD ; INRD := INRDLX - restore input ptr
MOVD INRDLB,INRDB ; INRDB := INRDLB - restore backward input ptr
RETS ; return (failed)
; check rule pattern symbol
CHKSYMB CMP %>07,A ; is a "'"?