-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmicron.asm
5751 lines (5299 loc) · 139 KB
/
micron.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
; ═════════════════════════════════════════════════════════════════════════════════
; БЕЙСИК для МИКРО-80/БЕЙСИК для РАДИО-86РК/БЕЙСИК для ЮТ-88
; БЕЙСИК-СЕРВИС для МИКРО-80/БЕЙСИК-СЕРВИС для РАДИО-86РК
; БЕЙСИК-СЕРВИС для ЮТ-88/БЕЙСИК-МИКРОН для РАДИО-86РК
; ═════════════════════════════════════════════════════════════════════════════════
;
; Это дизассемблер Бейсика для "МИКРО-80" и Бейсика для "Радио-86РК" и других...
; Гипотетически Бейсик для МИКРО-80=Бейсик для ЮТ-88, но это пока не проверялось.
; Имена меток взяты с дизассемблера Altair BASIC 3.2 (4K)
; По ходу разбора встречаются мысли и хотелки.
;
; Общие хотелки:
; !!Добавить поддержку каналов и потоков, как в Sinclair Basic, не забывая совместимость с ECMA стандартом
; !!Пересобрать с адреса 100h. Вначале добавить CP/M адаптер. При наличии поддержки дисковых фунок - адаптер не цепляем.
; !!Добавить OPTION BASE для управления индеском массива (Совместимость ANSI)
; !!Автонастройка памяти (сейчас жестко задано в коде), кроме версии для РК-86
; !!GO TO=GOTO, GO SUB=GOSUB (учесть в токенизаторе)
; ??ГОСТ расширение основных средств уровень 1 и 2 не могут быть реализованы из-за усеченного знакогенератора. Нет строчных букв.
; !!Отвязать по максимуму от работы в ОЗУ (версия для ROM-диска? Мысль интересная, т.к можно высвободить гору ОЗУ.
; Больше актуально не для М-80/ЮТ-88, а для РК-86. При этом для самого интерпретатора можно заполучить 32Кб
; для стандартного ROM-диска).
; !!Добавить IF x GOSUB
;
; Для микроши f82d есть? Или как-то по другому?
;
; БЕЙСИК для МИКРО-80/РАДИО-86РК - Общее устройство
;
; Как распределяется память
;
; +-------------------------------+ FFFFH
; ! ПЗУ МОНИТОРа (2К) !
; +-------------------------------+ F800H
; ! Рабочие ячейки МОНИТОРа !
; +-------------------------------+ F750H
; ! Не использована !
; +-------------------------------+ F000H (МИКРО-80/ЮТ-88)
; ! ОЗУ экрана !
; +-------------------------------+ E800H (МИКРО-807/ЮТ-88)
; ! ОЗУ курсора !
; +-------------------------------+ E000H (МИКРО-80)
; ! Не использована !
; +-------------------------------+ (MEM_TOP)
; ! Строковые переменные !
; +-------------------------------+ (STR_TOP)
; ! Стек Бейсика !
; +-------------------------------+ (STACK_TOP)
; ! Массивы !
; +-------------------------------+ (VAR_ARRAY_BASE)
; ! Числовые переменные !
; +-------------------------------+ (VAR_BASE)
; ! Текст программы на Бейсике !
; +-------------------------------+ (PROGRAM_BASE)
; ! Буфер экрана !
; +-------------------------------+ (SCRBUF) (МИКРО-80/ЮТ-88)
; ! Область подпрограмм !
; ! пользователя !
; +-------------------------------+ 1960H
; ! Интерпретатор Бейсика !
; +-------------------------------+ 0000H
;
; Рассмотрим блоки памяти, которые идут непосредственно после интерпретатора Бейсика:
;
; The minimum amount of stack space is 18 bytes - at initialisation, after the
; user has stated the options they want, the amount of space is reported as
; "X BYTES FREE", where X is 4096 minus (amount needed for Basic, plus 18 bytes
; for the stack). With all optional inline functions selected - SIN, RND, and SQR
; - X works out to 727 bytes. With no optional inline functions selected, the
; amount increases to 973 bytes.
;
; Текст программы на Бейсике
;
; На рис. показан формат строки программы на Бейсике в том виде, в каком она хранится в памяти микро-ЭВМ.
; В начале каждой строки два байта отведены для хранения указателя адреса начала следующей строки программы,
; следующие два байта хранят номер строки, а заканчивается она байтом, заполненным одними нулями. Таким образом,
; текст программы хранится в памяти в виде специальной структуры данных, называемой в литературе "односвязным списком".
;
; Для большей эффективности по скорости и использованию памяти, программа храниться в токенизированном виде.
; Токенизация заключается в простой замене ключевых слов их идентификаторами. Идентификатора занимают один байта
; (более поздние версии используют 1- и 2-байтовые идентификаторы). Идентификатор определяется установленным
; старшим битом, т.е. коды индентификаторов находятся в диапазоне 0x80 - 0xFF.
;
; Например, строка:
;
; FOR I=1 TO 10
;
; В токенизированном виде выглядит как :
;
; 81 " I=1" 95 " 10"
;
; Здесь 081H - идентификатор для FOR, далее следует строка " I=1", после которой - идентификатор 095H для TO, и оканчивается
; строкой " 10". Всего 9 байт вместо 13 байт для нетокенизированной строки.
;
; This particular example line of input is meaningless unless it is part of a larger program. As you should know already,
; each line of a program is prefixed with a line number. These line numbers get stored as 16-bit integers preceding the
; tokenised line content. Additionally, each line is stored with a pointer to the following line. Let's consider the
; example input again, this time as a part of a larger program :
;
; 10 FOR I=1 TO 10
; 20 PRINT "HELLO WORLD"
; 30 NEXT I
;
; Приняв начало памяти программы за 0D18, данная программа будет сохранена в памяти следующим образом:
;
;
;
; Таким образом, каждая строка программы состоит из трех компонент:
;
; Указатель на следующую строку
; Номер строки
; Токенизированная строка
;
; Последняя строка программы всегда присутствует и
; содержит нулевой указатель на несуществующую следующую строку.
; Эта нулевая строка, длиной два байта, маркер конца программы.
; Заканчивая обработку очередной строки программы, интерпретатор
; последовательно просматривает указатели списка до тех пор, пока
; не будет найдена строка с требуемым номером. Конец списка
; помечается двумя "нулевыми" байтами. Вы таким же образом
; можете вручную (с помощью директив Монитора) определить,
; где заканчивается программа, просматривая указатель списка
; до тех пор, пока не обнаружите три смежных байта, заполненных
; нулями. Во многих практических случаях, воспользовавшись
; рассмотренными рекомендациями, можно восстановить программу,
; в которой в результате сбоя была нарушена целостность списка.
; После восстановления структуры списка необходимо изменить значения,
; хранящиеся в ячейках памяти 0245Н и 0246Н. В этих ячейках хранятся
; значения соответственно младшего и старшего байта конечного адреса
; программы. Этот адрес на двойку превосходит адрес первого байта
; маркера конца списка.
;
; Переменные
; ----------
;
; Поддержка переменных в этой версии Бейсика достаточно ограниченная.
; Разрешенный тип переменных только числовой - нет символьных, структур, и, конечно,
; не различаются целые числи и числа с плавающей точкой. Все переменные хранятся и
; обрабатываются как числа с плавающей точкой.
;
; Длина имени переменных ограничена максимум двумя символами: первый (обязательный)
; символ должен быть буквой (латинской), а второй (опциональный) символ - число.
; Таким образом следующие определения некорректны:
;
; LET FOO=1
; LET A="HELLO"
; LET AB=A
;
; В то время как следующие - корректны:
;
; LET A=1
; LET B=2.5
; LET B2=5.6
;
; Фиксированная длина переменных существенно упрощает их хранение в памяти.
; Каждая переменная занимает 6 байт в памяти: два байта для имени и четыре
; байта для значения переменной.
;
; Массивы
; -------
;
; Массивы хранятся в отдельной области памяти, сразу после области переменных. Начала области массивов определяется переменной
; VAR_ARRAY_BASE. Массивы определяются ключевым словом DIM. Размерность массива n приводит к выделению n+1 элементов,
; адресуемых значениями индекса от 0 до n включительно.
;
; Соответственно, код ниже корректен:
;
; DIM A(2)
; A(0) = 1
; A(1) = 2
; A(2) = 3
;
; но:
;
; A(3) = 4
;
; приведет к ошибке Ошибка 09. Индекс не соответствует размерности массива.
;
; Массив хранится в памяти аналогично обычным переменным. Вначале следует двух-байтовое имя
; переменной. Далее следует 16-битное целое, содержащее размер в байтах элементов массива.
; Далее следуют непосредственно элементы массива (по 4-е байта на каждый элемент).
; Например, указанный выше массив A(2), если сохранен по адресу 0D20, будет храниться так:
;
; Address Bytes Value Description
; 0D20 0x4100 'A\0' Variable name
; 0D22 0x000C Total size, in bytes, of the array elements.
; 0D24 0x81000000 1 Element 0 value
; 0D28 0x82000000 2 Element 1 value
; 0D2C 0x82400000 3 Element 2 value
;
;
; Порядок исполнения
; ------------------
;
; При выполнении команды RUN исполнение начинается с первой строки программы.
; Когда строка заканчивается, исполнение продолжается со следующей строки и так далее до тех
; пор, пока не будет достигнут конец программы, команда END или STOP.
;
; This is too simple for all but the simplest programs - there are two mechanisms in Basic
; for altering program flow so that code can run in loops and subroutines be called.
; These mechanisms are FOR/NEXT for looping, and GOSUB/RETURN for subroutines.
;
; In both FOR and GOSUB cases, the stack is used to store specific information about
; the program line to return to.
;
CPU 8080
Z80SYNTAX EXCLUSIVE
; Конфигурация
ifndef MICRON
MICRON EQU 0 ; Модификации для "Бейсик-Микрон"
endif
ifndef MIKROSHA
MIKROSHA EQU 0 ; Модификации для "Бейсик для Микроша"
endif
; Т.к. Микроша близок в РК86, то используем его код в качестве основы
if MIKROSHA
RK86 EQU 1 ; Модификации для "Бейсик для Радио-86РК"
RAM EQU 32 ; Микроша шла только с 32кб
endif
IF MICRON
RAM EQU 32 ; Микрон с 32кб
ENDIF
ifndef RK86
RK86 EQU 0 ; Модификации для "Бейсик для Радио-86РК"
endif
ifndef UT88
UT88 EQU 0 ; Модификации для "Бейсик для ЮТ-88"
endif
ifndef SERVICE
SERVICE EQU 0 ; Модификации для Бейсик-Сервис
endif
ifndef BASICNEW
BASICNEW EQU 0 ; Включить мои изменения в коде
endif
ANSI EQU 0 ; Включить поддержку совместимости с ANSI Minimal Basic
GOST EQU 0 ; Включить поддержку совместимости с ГОСТ 27787-88
IFNDEF RAM
RAM EQU 16
ENDIF
; Верхний адрес доступной памяти. В МИКРО-80/ЮТ-88 задано жестко,
; а в РК-86 настраивается при инициализации. В Микроше тоже жестко.
; В Миероне тоже жестко
if MIKROSHA
MEM_TOP EQU 075FFH
else; MIKROSHA
if MICRON
MEM_TOP EQU 075FFH
else; MICRON
IF RAM=12
MEM_TOP EQU 02FFFH
ELSEIF RAM=16
MEM_TOP EQU 03FFFH
ELSEIF RAM=32
MEM_TOP EQU 07FFFH
ELSEIF RAM=48
MEM_TOP EQU 0BFFFH
ENDIF
endif; MICRON
endif; MIKROSHA
IF BASICNEW
IF ANSI
OPTION EQU 1 ; Поддержка команды OPTION
LET EQU 1 ; Поддержка команды LET
RANDOMIZE EQU 1 ; Поддержка команды RANDOMIZE
END EQU 1 ; Поддержка команды END
ELSE ; ANSI
IF GOST
OPTION EQU 1 ; Поддержка команды OPTION
LET EQU 1 ; Поддержка команды LET
RANDOMIZE EQU 1 ; Поддержка команды RANDOMIZE
END EQU 1 ; Поддержка команды END
ELSE ; GOST
OPTION EQU 0 ; Поддержка команды OPTION
LET EQU 1 ; Поддержка команды LET
RANDOMIZE EQU 0 ; Поддержка команды RANDOMIZE
END EQU 1 ; Поддержка команды END
ENDIF ; GOST
ENDIF ; ANSI
ELSE ; BASICNEW
OPTION EQU 0 ; Поддержка команды OPTION
LET EQU 0 ; Поддержка команды LET
RANDOMIZE EQU 0 ; Поддержка команды RANDOMIZE
END EQU 0 ; Поддержка команды END
ENDIF ; BASICNEW
IF BASICNEW
CHK MACRO adr, msg
ENDM
ELSE
IF MICRON
CHK MACRO adr, msg
ENDM
ELSE
CHK MACRO adr, msg
IF adr-$
ERROR msg
ENDIF
ENDM
ENDIF
ENDIF
IF RK86
IF SERVICE
PROGRAM_BASE_INIT EQU 1D00H
ELSE
PROGRAM_BASE_INIT EQU 1B00H
ENDIF
IF RAM=16
SCRADDR EQU 037C2H
ELSE ; 32kb
SCRADDR EQU 077C2H
ENDIF
ELSE
IF SERVICE
SCRBUF EQU 1D00H
PROGRAM_BASE_INIT EQU 2500H
ELSE
IF BASICNEW
SCRBUF EQU 1D00H
PROGRAM_BASE_INIT EQU 2500H
ELSE
SCRBUF EQU 1A00H
PROGRAM_BASE_INIT EQU 2200H
ENDIF
ENDIF
ENDIF
IF MICRON
VarSize EQU 2049h
IOCode EQU 205Ch ; три байта in a, (n) или out (n), a плюс ret
TERMINAL_X EQU 2063h
TMP_HL EQU 2064h
LINE_BUFFER EQU 2090h
ControlChar EQU 2117h
DIM_OR_EVAL EQU 2118h
VALTYP EQU 2119h
DATA_STM EQU 211Ah
MEMSIZ EQU 211Bh
TEMPPT EQU 211Dh
TMPST EQU 211Fh
TMPSTR EQU 212Bh
STR_TOP EQU 212FH
CUR_TOKEN_ADR EQU 2131h
DATA_LINE EQU 2133h
NO_ARRAY EQU 2135h
INPUT_OR_READ EQU 2136h
PROG_PTR_TEMP EQU 2137h
PROG_PTR_TEMP2 EQU 2139h
CURRENT_LINE EQU 213Bh
OLD_LINE EQU 213Dh
OLD_TEXT EQU 213Fh
STACK_TOP EQU 2141H
PROGRAM_BASE EQU 2143h
VAR_BASE EQU 2145h
VAR_ARRAY_BASE EQU 2147h
VAR_TOP EQU 2149h
DATA_PROG_PTR EQU 214Bh
FACCUM EQU 214Dh
ERR_NF EQU 00H
ERR_SN EQU 02H
ERR_RG EQU 04H
ERR_OD EQU 06H
ERR_FC EQU 08H
ERR_OM EQU 0CH
ERR_US EQU 0EH
ERR_DD EQU 12H
ERR_DZ EQU 14H
ERR_TM EQU 18H
ERR_ST EQU 1EH
ERR_CN EQU 20H
ERR_UF EQU 22H
PrintString EQU 0F818H
ENDIF
;
;********************
;* 1. Интерпретатор *
;********************
;================
;= 1.1 Рестарты =
;================
; Полезной возможностью 8080 является возможность вызова ряда адресов в нижних адресах
; памяти однобайтовой инструкцией вместо стандартных 3-х байтовых вызовов
; CALL и подобными командами. Данные адреса называют адресами "Рестартов"
; и обычно используются для часто вызываемых функций, что эконоит по 2 байта на каждом вызове.
; Всего Бейсик использует 7 рестартов. 8-й рестарт используется отладчиками.
; Начало (RST 00h)
; Запуск интерпретатора осуществляется с адреса 0. Проводится инициализация стека и
; переход на код инициализации.
IF BASICNEW
ORG 0
; ORG 100H
RST MACRO adr
CALL adr
ENDM
ELSE
ORG 0
ENDIF
Start:
IF RK86
LD SP, TMPSTACK
ELSE
LD SP, MEM_TOP
ENDIF
JP Init
; Данные байты не используются? В оригинале здесь указатели на какие-то данные, а не код.
IF MICRON
NOP
NOP
ELSE
INC HL
EX (SP),HL
ENDIF
;RST 08h
INCLUDE "spSyntaxCheck.inc"
IF BASICNEW
ELSE
;RST 10h
INCLUDE "spNextChar.inc"
ENDIF
; OutChar (RST 3)
; Печать символа на терминал.
IF BASICNEW
ELSE
OutChar:
ENDIF
IF MICRON
PUSH BC
PUSH HL
PUSH AF
LD C,A
JP OutChar_tail
NOP
; RST 20h
include "spCompareHLDE.inc"
INIT_PROGAM_BASE:
DW 2201H
ELSE
IF BASICNEW
ELSE
PUSH AF
LD A,(ControlChar)
OR A
JP OutChar_tail
ENDIF
; RST 20h
include "spCompareHLDE.inc"
;
IF BASICNEW
ELSE
NULLS: DB 01 ; Число нолей-1, которое надо вывести после перевода строки (это было нужно для терминалов)
ENDIF
TERMINAL_X: DB 00 ; Variable controlling the current X positions of terminal output
ENDIF
; RST 28h
include "spFTestSign.inc"
;
;PushNextWord (RST 6)
;Effectively PUSH (HL). First we write the return address to the JMP instruction at the end of the function; then we read the word at (HL) into BC and push it onto the stack; lastly jumping to the return address.
;
PushNextWord:
IF MICRON
LD C, (HL)
INC HL
LD B,(HL)
INC HL
JP RST6_CONT
NOP
RST7: RET
NOP
NOP
RST6_CONT:
LD (TMP_HL),HL
POP HL
PUSH BC
PUSH HL
LD HL,(TMP_HL)
RET
ELSE
EX (SP),HL
LD (RST6RET),HL
POP HL
IF BASICNEW
ELSE
JP RST6_CONT ; Отличие от Altair - место для обработчика RST 7
;
;
;
; ORG 38H
RST7:
RET
ENDIF
L0039: DW 0
IF BASICNEW
ELSE
RST6_CONT:
ENDIF
LD C,(HL)
INC HL
LD B,(HL)
INC HL
PUSH BC
RST6RET: EQU $+1
JP 04F9H ; Это самомодифицирующийся код, см. PushNextWord.
ENDIF
; Блок данных
IF BASICNEW
ELSE
IF MICRON
ELSE
include "data.inc"
ENDIF
ENDIF
;=========================
;= 1.4 Utility Functions =
;=========================
; Some useful functions.
CHK 027ah, "Сдвижка кода"
include "spGetFlowPtr.inc"
include "spCopyMemoryUp.inc"
include "spCheckEnoughVarSpace.inc"
; CheckEnoughMem
; Checks that HL is more than 32 bytes away from the stack pointer. If HL is within 32 bytes
; of the stack pointer then this function falls into OutOfMemory.
CheckEnoughMem:
PUSH DE
EX DE,HL
IF MICRON
LD HL,0FFDBh ; Отличается от более старых версий на 1 байт. Должно быть -34. 0FFDFh. Причину не разбирал
ELSE
LD HL,0FFDAH ; HL=-34 (extra 2 bytes for return address)
ENDIF
ADD HL,SP
RST CompareHLDE
EX DE,HL
POP DE
RET NC
; 5 обработчиков ошибки
; Используется трюк с LD BC,...
; (Здесь можно еще пооптимизировать, используя трюки с INC/DEC/LD B,...)
OutOfMemory:
LD E, ERR_OM
JP Error
DATASyntaxError:
LD HL,(DATA_LINE)
LD (CURRENT_LINE),HL
SyntaxError:
LD E, ERR_SN
IF MICRON
XOR A
LD (2078h),A
ENDIF
DB 01 ; LD BC,...
DivideByZero:
LD E, ERR_DZ
DB 01 ; LD BC,...
WithoutFOR:
LD E, ERR_NF
; Error
;
; Сбрасывает стек, выводит сообщение об ошибке (смещение
; сообщение об ошибке передается в E) и прекращает исполнение
; программы.
Error:
IF BASICNEW
LD A, E ; Делим смещение на 2
SCF
CCF
RRA
LD (ErrorCode), A ; И сохраняем для получения по ERR
LD HL, (CURRENT_LINE) ; Получаем текущую строку
LD (ErrorLine), HL ; И сохраняем ее для получения по ERL
ENDIF
CALL ResetStack
XOR A
LD (ControlChar),A
CALL NewLine
IF MICRON
LD A,E
RRCA
LD E,A
INC E
LD HL,ErrorMessages
L00B8: DEC E
JP Z,MessageFound
L00BC: LD A,(HL)
INC HL
OR A
JP Z,L00B8
JP L00BC
MessageFound:
CALL PrintString
ELSE
LD HL,ERROR_CODES
LD D,A
LD A,'?'
RST OutChar
ADD HL,DE
IF BASICNEW
LD E, (HL)
INC HL
LD D, (HL)
EX DE, HL
CALL PrintString
ELSE
LD A,(HL)
RST OutChar
RST NextChar
RST OutChar
ENDIF
ENDIF
LD HL, szError
PrintInLine:
CALL PrintString
LD HL, (CURRENT_LINE)
LD A,H
AND L
INC A
JP Z,Main
PUSH HL
CALL PrintIN
LD A,':'
RST OutChar
LD A,(208Dh)
INC A
LD E,A
LD D,00h
CALL L131C
POP HL
EX DE,HL
LD A,(2078h)
OR A
JP Z,L171B
;
; Main
; Here's where a BASIC programmer in 1975 spent most of their time : typing at an "OK" prompt, one line at a time. A line of input would either be exec'd immediately (eg "PRINT 2+2"), or it would be a line of a program to be RUN later. Program lines would be prefixed with a line number. The code below looks for that line number, and jumps ahead to Exec if it's not there.
;
Main:
IF MICRON
LD HL, szOK ; szOK
CALL PrintString
ELSE
XOR A
LD (ControlChar),A ; Включаем вывод на экран (не управляющий символ)
ENDIF
LD HL,0FFFFH ; Сбрасываем текущую выполняемую строку
LD (CURRENT_LINE),HL
IF MICRON
LD (2078h),HL
ELSE
LD HL,szOK ; Выводим приглашение
CALL PrintString
ENDIF
GetNonBlankLine:
XOR A
LD (ControlChar),A
LD (LINE_BUFFER),A
LD (TERMINAL_X),A
CALL L0279
; --- START PROC L010D ---
L010D: CALL TerminateInput
; --- START PROC L0110 ---
L0110:
RST NextChar ; Считываем первый символ из буфера. Флаг переноса =1, если это цифра
INC A ; Проверяем на пустую строку. Инкремент/декремент не сбрасывает флаг переноса.
DEC A
JP Z, GetNonBlankLine ; Снова вводим строку, если пустая
PUSH AF ; Сохраняем флаг переноса
CALL LineNumberFromStr ; Получаем номер строки в DE
PUSH DE ; Запоминаем номер строки
CALL Tokenize ; Запускаем токенизатор. В C возвращается длина токенизированной строки, а в А = 0
LD B,A ; Теперь BC=длина строки
POP DE ; Восстанавливаем номер строки
POP AF ; Восстанавлливаем флаг переноса
JP NC, Exec ; Если у нас строка без номера, то сразу исполняем
; StoreProgramLine
; Here's where a program line has been typed, which we now need to store in program memory.
StoreProgramLine:
PUSH DE
PUSH BC
RST NextChar
PUSH AF
CALL FindProgramLine ; Ищем строку в программе
PUSH BC
JP NC,InsertProgramLine ; Если не нашли, то вставляем строку
; Carry was set by the call to FindProgramLine, meaning that the line already exists.
; So we have to remove the old program line before inserting the new one in it's place.
; To remove the program line we simply move the remainder of the program
;(ie every line that comes after it) down in memory.
RemoveProgramLine:
EX DE,HL
LD HL,(VAR_BASE)
RemoveProgramLineLoop:
LD A,(DE)
LD (BC),A
INC BC
INC DE
RST CompareHLDE
JP NC, RemoveProgramLineLoop
LD H,B
LD L,C
INC HL
LD (VAR_BASE),HL
;To insert the program line, firstly the program remainder (every line that comes
; after the one to be inserted) must be moved up in memory to make room.
InsertProgramLine:
POP DE
POP AF
JP Z,UpdateLinkedList
LD HL,(VAR_BASE)
EX (SP),HL
POP BC
ADD HL,BC
PUSH HL
CALL CopyMemoryUp
POP HL
LD (VAR_BASE),HL
EX DE,HL
LD (HL),H
INC HL
INC HL
POP DE
LD (HL),E
INC HL
LD (HL),D
INC HL
CopyFromBuffer:
LD DE,LINE_BUFFER ;Copy the line into the program
CopyFromBufferLoop:
LD A,(DE)
LD (HL),A
INC HL
INC DE
OR A
JP NZ,CopyFromBufferLoop
;Now the program line has been inserted/removed, all the pointers from each line to the next need to be updated.
UpdateLinkedList:
CALL ResetAll
INC HL
UpdateLinkedListLoop:
LD D,H
LD E,L
LD A,(HL)
INC HL
OR (HL)
JP Z,GetNonBlankLine2
INC HL
INC HL
INC HL
XOR A
FindEndOfLine:
CP (HL)
INC HL
JP NZ,FindEndOfLine
EX DE,HL
LD (HL),E
INC HL
LD (HL),D
EX DE,HL
JP UpdateLinkedListLoop
GetNonBlankLine2:
LD A,(ControlChar)
OR A
JP Z,GetNonBlankLine
DEC A
JP Z,Main
XOR A
LD (ControlChar),A
JP L18E7
INCLUDE "spFindProgramLine.inc"
; --- START PROC New ---
New:
RET NZ
LD HL,(PROGRAM_BASE)
XOR A
LD (HL),A
INC HL
LD (HL),A
INC HL
LD (VAR_BASE),HL
; --- START PROC ResetAll ---
ResetAll:
LD HL,(PROGRAM_BASE)
DEC HL
LD (HL),00h
; --- START PROC ClearAll ---
ClearAll:
LD (PROG_PTR_TEMP),HL
LD HL,(MEMSIZ)
LD (STR_TOP),HL
CALL Restore0
; --- START PROC L01CD ---
L01CD: LD HL,(VAR_BASE)
LD (VAR_ARRAY_BASE),HL
LD (VAR_TOP),HL
; --- START PROC ResetStack ---
ResetStack:
POP BC
LD HL,(STACK_TOP)
LD SP,HL
LD HL,TMPST
LD (TEMPPT),HL
LD HL,0000h
PUSH HL
LD (OLD_TEXT),HL
LD HL,(PROG_PTR_TEMP)
XOR A
LD (NO_ARRAY),A
PUSH BC
RET
; Tokenize
;
; Токенизирует строку в буфере LINE_BUFFER, заменяя ключевые слова кодом токена.
; На выходе C содержит длину токенизированной строки плюс несколько байт
; для завершения строки программы.
Tokenize:
XOR A
L01F2:
LD (DATA_STM),A
LD C,05H ; Initialise line length to 5
LD DE,LINE_BUFFER ; ie, output ptr is same as input ptr at start.
TokenizeNext:
LD A,(HL) ; Получение введенного символа
;If char is a space, jump ahead to write it out.
CP ' '
JP Z,WriteChar
;If char is a " (indicating a string literal) then freely copy up to the closing ". Obviously we don't want to tokenize string literals.
LD B,A
CP '"'
JP Z,FreeCopy
;If char is null then we've reached the end of input, and can exit this function.
OR A
JP Z,Exit
; Обработка DATA
LD A,(DATA_STM)
OR A
LD B,A
LD A,(HL) ; Восстанавливаем введенный символа
JP NZ,WriteChar
IF MICRON
ELSE
; Обработка ?
CP '?'
LD A, TK_PRINT ; Замена ? на PRINT
JP Z, WriteChar
IF BASICNEW
; Обработка '
LD A,(HL) ; Восстанавливаем введенный символ
CP "'"
LD A, TK_REM ; Замена ' на REM
JP Z, WriteChar
ENDIF
;
LD A,(HL) ; Восстанавливаем введенный символ
ENDIF
CP '0' ; Меньше '0'?
JP C,KwSearch ; Ищем ключевое слово
CP ';'+1 ; 0123456789:;
JP C,WriteChar
; Here's where we start to see if we've got a keyword. B здесь содержит 0 (см. код выше где OR A; LD B,A)
KwSearch:
PUSH DE ; Preserve output ptr.
LD DE,KEYWORDS-1 ;
PUSH HL ; Preserve input ptr.
DB 3Eh ; LD A, ...
KwCompare:
INC HL ; !! Отличается от Микро-80 !!
INC DE
KwCompareDE:
LD A,(DE) ; Get keyword char to compare with.
AND 7FH ; Ignore bit 7 of keyword char.
JP Z, NotAKeyword ; If keyword char==0, then end of keywords reached.
CP (HL) ; Keyword char matches input char?
JP NZ, NextKeyword ; If not, jump to get next keyword.
; OK, so input char == keyword char. Now we test bit 7 of the keyword char : if it's 0 then we haven't yet reached the end of the keyword and so have to loop back to continue comparing.
LD A,(DE)
OR A
JP P, KwCompare
; Matched a keyword! First thing we do is remove input ptr from the stack, as since we're matched to a keyword we don't need to go back and try to match another keyword - HL is already the correct input ptr. Then we set A to the keyword ID which gets written out in the next block but one (notice we LXI over the next block).
POP AF ; Remove input ptr from stack. We don't need it.
LD A,B ; A=Keyword ID
OR 80H ; Set bit 7 (indicates a keyword)
DB 0F2H ; JP P,...
; Here we have found that the input does not lead with a keyword, so we restore the input ptr and write out the literal character.
NotAKeyword:
POP HL ; Restore input ptr
LD A, (HL) ; and get input char
; Write character, and advance buffer pointers.
POP DE ; Restore output ptr
WriteChar:
INC HL ; Advance input ptr
LD (DE),A ; Store output char
INC DE ; Advance output ptr
INC C ; C++ (arf!).
; If we've just written the ID of keyword REM then we need to freecopy the rest of the line.
; Here we test for REM (8E) and jump back to the outer loop if it isn't. Note that if it is