Skip to content

Commit 3d749d3

Browse files
committed
WIP FRND/FSQL for BK0010
1 parent 0c5b4a6 commit 3d749d3

4 files changed

Lines changed: 197 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ Current state of the project: **prototype**
3333
- полноценный парсинг всех конструкций языка, кроме оператора `DATA`
3434
- вычисление Integer и Single выражений, вычисление константных String выражений
3535
- `PRINT` для Integer/Single и константных строк, `INPUT` только для целых чисел
36-
- функции: `PI`, `ABS`, `FIX`, `INT`, `SGN`, `CINT`, `CSNG`, `PEEK`, `INP`, CSRLIN, POS
36+
- функции: `PI`, `ABS`, `FIX`, `INT`, `SGN`, `CINT`, `CSNG`, `PEEK`, `INP`, `SQR`, `SIN`, `COS`, `TAN`, `ATN`, `RND`
3737
- операторы: `LET` (кроме `LET MID$`), `GOTO`, `GOSUB`, `RETURN`, `IF/THEN/ELSE`, `FOR`, `NEXT`, `ON/GOTO/GOSUB`, `STOP`, `END`, `REM`, `PRINT` (включая `AT`, `TAB`, `SPC`, запятая), `INPUT` (только для Integer), `POKE`, `OUT`, `CLS`, `COLOR`, `LOCATE`, `BEEP`
3838
- без работы со строками, без работы с файлами, без графики, без `DATA`/`READ`/`RESTORE`
39+
- НЕ реализованы функции:, `CSRLIN`, `POS`, `EXP`, `LOG`, `FRE`, `CDBL`, `ASC`, `CHR$`, `LRN`, `MID$`, `STRING$`, `VAL`, `INKEY$`, `STR$`, `BIN$`, `OCT$`, `HEX$`, `LPOS`, `EOF`, `FN`, `USR`
3940
- НЕ реализованы операторы: `LET MID$`, `DIM`, `KEY`, `CLEAR`, `DATA`, `READ`, `RESTORE`, `DEF USR`, `DEF FN`, `PRINT` для строковых выражений, `INPUT` для Single и строк, `OPEN`, `CLOSE`, `SCREEN`, `PSET`, `PRESET`, `LINE`, `CIRCLE`, `PAINT`, `DRAW`, `TRON`, `TROFF`, `WIDTH`, `SYSTEM`, `MONIT`
40-
- НЕ реализованы функции: `SQR`, `SIN`, `COS`, `TAN`, `ATN`, `EXP`, `LOG`, `RND`, `FRE`, `CDBL`, `ASC`, `CHR$`, `LRN`, `MID$`, `STRING$`, `VAL`, `INKEY$`, `STR$`, `BIN$`, `OCT$`, `HEX$`, `LPOS`, `EOF`, `FN`, `USR`
4141

4242
Возможные сценарии использования компилятора:
4343

generator.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2643,6 +2643,15 @@ void Generator::GenerateFuncRnd(const ExpressionModel& expr, const ExpressionNod
26432643
const ExpressionModel& expr1 = node.args[0];
26442644
assert(expr1.GetExpressionValueType() != ValueTypeString);
26452645

2646+
// Special case for RND(0): return RNDSAV value
2647+
if (expr1.IsConstExpression() && std::floor(expr1.GetConstExpressionDValue()) == 0.0)
2648+
{
2649+
AddLine("\tMOV\tRNDSAV+2, -(SP)\t; RND(0)");
2650+
AddLine("\tMOV\tRNDSAV, -(SP)");
2651+
m_runtimeneeds.insert(RuntimeFRND);
2652+
return;
2653+
}
2654+
26462655
GenerateExpression(expr1);
26472656
if (expr1.GetExpressionValueType() == ValueTypeInteger)
26482657
AddRuntimeCall(RuntimeITOF, "to Single"); // result on stack

runtime-BK0010.tmac

Lines changed: 174 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ WRSNG:
105105
CALL FUNPK ; число печатается в буфер BUFOUT
106106
MOV #BUFOUT, R0
107107
CALL WRST ; выводим строку на экран
108-
9$: JMP @#000000
108+
9$: JMP @#000000 ; возврат, адрес подставляется в начале
109109

110110
;#####################################################################
111111
;## WRST
@@ -896,6 +896,177 @@ GRIZT: MOV (SP)+,R3 ;
896896
MOV R2,R3 ; иначе восстанавливаем R2
897897
2$: RETURN
898898

899+
;#####################################################################
900+
;## FRND
901+
;## Need FADD
902+
;## Need FSUB
903+
;## Need FMUL
904+
;## Need FDIV
905+
; Генератор псевдо-случайных чисел.
906+
; На основе кода процедуры RAN из реализации Бейсик Вильнюс для УКНЦ.
907+
; На входе: (SP) адрес возврата
908+
; (SP+2)(SP+4) = управляющий параметр:
909+
; >0: получить очередное псевдо-случайное число
910+
; <0: задаёт начальное значение
911+
; 0: вернуть предыдущее псевдо-случайное число
912+
; Результат: (SP)(SP+2) псевдо-случайное число в диапазоне [0,1)
913+
FRND:
914+
MOV (SP)+, FRND$X+2 ; сохраняем адрес для выхода
915+
TST (SP) ; проверяем старшее слово параметра
916+
BEQ 26$ ; параметр 0 => отдать текущее значение
917+
BPL 1$ ; > 0 => генерация следующего
918+
; Параметр < 0 ? инициализация
919+
CLR RNDSAV ; сбросить сохранённое значение
920+
MOV #040311, FRND$I ; начальное состояние = PI/2
921+
MOV #007733, FRND$I+2
922+
1$: MOV #FRND$I, R2 ; адрес состояния (старшее слово)
923+
MOV #FRND$I+2, R3 ; адрес состояния (младшее слово)
924+
MOV (R2), R0 ; загрузить текущее состояние
925+
MOV (R3), R1
926+
BEQ 21$ ; если ноль, особая инициализация
927+
; state = (state * 3 + 1) mod 2^31
928+
ASL R1 ; умножить на 2
929+
ROL R0
930+
ADD (R2), R0 ; state*3
931+
ADD (R3), R1
932+
ADC R0
933+
ADD (R3), R0 ; + state (младшее слово) -> state*3 + 1
934+
BPL 2$ ; если нет переполнения знакового бита
935+
ADD #100000,R0 ; инвертировать знаковый бит
936+
;RAN2:
937+
2$: MOV R0, (R2) ; сохранить новое состояние в III
938+
MOV R1, (R3)
939+
MOV #201,R2 ; начальная экспонента = 129.
940+
4$: ASL R1 ; нормализация:
941+
ROL R0 ; сдвигать мантиссу, пока старшая единица не выйдет в C
942+
BCS 23$
943+
DEC R2 ; уменьшать экспоненту
944+
BR 4$
945+
;RAN3: Упаковка в формат float: знак = 0, экспонента = R2, мантисса из старших битов R0,R1
946+
23$: CLRB R1
947+
BISB R0,R1
948+
SWAB R1
949+
CLRB R0
950+
BISB R2,R0
951+
SWAB R0
952+
ROR R0 ; знак = 0
953+
ROR R1
954+
BR 25$
955+
;RAN1: Если состояние обнулилось (например, при инициализации нулём), используем (1,3)
956+
21$: MOV #3,R1
957+
INC R0
958+
BR 2$
959+
;RAN5:
960+
25$: TST RNDSAV ; RNDSAV ещё не заполнено? первый вызов после инициализации
961+
BNE 27$ ; не ноль, значит уже было ? просто сохраняем новое
962+
; Первое после инициализации: преобразуем целое состояние в float [0,1)
963+
MOV R1,-(SP) ; поместить полученное число (R0,R1) на стек
964+
MOV R0,-(SP)
965+
CLR R5 ; R5 = 0 (счётчик масштабирования)
966+
MOV 6(SP),-(SP) ; Дублируем число для операций
967+
MOV 6(SP),-(SP)
968+
BIC #100000,(SP) ; сбросить знак (работаем с положительным числом)
969+
; Цикл приведения в диапазон [0,1)
970+
13$: CMP #040200,(SP) ; сравнить старшее слово с 1.0
971+
BGT 11$ ; если > 1.0 -> уменьшить
972+
DEC R5 ; иначе увеличиваем счётчик
973+
BGE 15$ ; если R5 >= 0, выходим (достигли нужного интервала)
974+
; Число < 1.0, но R5 < 0 -> нужно делить на 10.0
975+
CLR -(SP) ; константа 10.0 младшее слово
976+
MOV #041040,-(SP) ; константа 10.0 старшее слово
977+
CALL FDIV ; разделить число на 10.0
978+
BR 13$ ; продолжить цикл
979+
11$: MOV SP,R2
980+
MOV (R2)+,6(R2) ; копирование числа
981+
MOV (R2)+,6(R2)
982+
INC R5
983+
BLE 15$
984+
CLR -(SP) ; константа 10.0 младшее слово
985+
MOV #041040,-(SP) ; константа 10.0 старшее слово
986+
CALL FMUL ; умножить на 10.0
987+
BR 13$ ; продолжить цикл
988+
;
989+
15$: ADD #4,SP ; удаляем временные копии числа
990+
; Теперь требуется ещё отбросить целую часть
991+
CLR -(SP) ; константа 10.0 младшее слово
992+
MOV #041040,-(SP) ; константа 10.0 старшее слово
993+
CALL FDIV ; деление на 10.0
994+
CLR -(SP) ; положить 1.0 младшее слово
995+
MOV #040200,-(SP) ; положить 1.0 старшее слово
996+
CALL FADD ; сложить с 1.0
997+
CALL FSUB ; вычитание
998+
;
999+
17$: BIC #100000,(SP) ; сбросить знак результата
1000+
MOV SP,R2
1001+
MOV (R2)+, FRND$I ; сохранить результат в III
1002+
MOV (R2)+, FRND$I+2
1003+
MOV -(R2), R1 ; восстановить R0,R1
1004+
MOV -(R2), R0
1005+
;RAN7:
1006+
27$: MOV R1, RNDSAV+2 ; сохранить сгенерированное значение
1007+
MOV R0, RNDSAV
1008+
;RAN6:
1009+
26$: MOV RNDSAV+2, 2(SP) ; заменяем параметр на стеке на результат
1010+
MOV RNDSAV, (SP)
1011+
FRND$X: JMP @#000000 ; возврат, адрес подставляется в начале
1012+
;
1013+
FRND$I: .WORD 040101, 027076 ; III: Рабочие ячейки FRND
1014+
RNDSAV: .WORD 040101, 027076 ; Предыдущее псевдо-случайное число: старшее, младшее
1015+
1016+
;#####################################################################
1017+
;## FSQR
1018+
;## Need FADD
1019+
;## Need FDIV
1020+
;## Need ERRR
1021+
; Квадратный корень из плавающего числа (float).
1022+
; На основе кода процедуры SQRG из реализации Бейсик Вильнюс для УКНЦ.
1023+
; Для отрицательных чисел выдаётся ошибка; для 0.0 результат 0.0.
1024+
; Алгоритм: итерационный метод Ньютона с начальным приближением
1025+
; На входе: (SP) - адрес возврата
1026+
; (SP+2)(SP+4) - число с плавающей точкой (старшее слово, младшее слово)
1027+
; На выходе: (SP)(SP+2) - результат
1028+
FSQR:
1029+
MOV (SP)+, FSQR$X+2 ; адрес возврата
1030+
CLR -(SP) ; выделить слово для счётчика итераций (3)
1031+
MOV SP,R4 ; R4 указывает на вершину стека (временный указатель)
1032+
TST (R4)+ ; пропустить младшее слово аргумента
1033+
MOV (R4), R1 ; R1 = старшее слово аргумента
1034+
BMI FSQR$K ; если отрицательное - ошибка
1035+
BEQ 0$ ; если ноль - результат 0
1036+
MOV #3,-(SP) ; счётчик итераций (3) на стек
1037+
ASR R1 ; экспоненту делим на 2 (приближение)
1038+
ADD #20100, R1 ; коррекция мантиссы (начальное приближение)
1039+
CLR -(SP) ; младшее слово приближения = 0
1040+
MOV R1, -(SP) ; старшее слово приближения на стек
1041+
CLR -(SP) ; младшее слово аргумента (0)
1042+
MOV (R4), -(SP) ; старшее слово аргумента (копия)
1043+
CLR -(SP) ; младшее слово для операции
1044+
MOV R1, -(SP) ; старшее слово приближения (второй операнд)
1045+
3$: CALL FDIV ; деление: аргумент / приближение
1046+
CALL FADD ; сложение: (приближение + результат деления)
1047+
SUB #200, (SP) ; поделить результат на 2
1048+
DEC 4(SP) ; уменьшить счётчик итераций
1049+
BEQ 2$ ; если счётчик = 0, выходим
1050+
MOV 12(SP), -(SP) ; иначе скопировать тек.приближение для следующей итерации
1051+
MOV 12(SP), -(SP)
1052+
MOV 6(SP), -(SP)
1053+
MOV 6(SP), -(SP)
1054+
BR 3$ ; повторить
1055+
2$: MOV (SP)+, R0 ; снять старшее слово результата
1056+
MOV (SP)+, R1 ; снять младшее слово результата
1057+
TST (SP)+ ; удалить счётчик итераций
1058+
BR FSQR$E ; получили результат, выходим
1059+
0$: CLR R0 ; результат 0.0
1060+
CLR R1
1061+
; R0,R1 - старшее и младшее слова результата
1062+
FSQR$E: MOV R0, (SP) ; заменяем старшее слово результатом
1063+
MOV R1, 2(SP) ; заменяем младшее слово
1064+
FSQR$X: JMP @#000000 ; возврат, адрес подставляется в начале
1065+
;
1066+
FSQR$K: MOV FSQR$X+2, R5 ; откуда вызвали
1067+
MOV #3605., R0 ; Ошибка: корень из отрицательного числа
1068+
JMP ERRR
1069+
8991070
;#####################################################################
9001071
;## FADD
9011072
;## Need ERRR
@@ -1155,7 +1326,7 @@ FMUL$M: MOV R0,10(SP) ;
11551326
MOV (SP)+,R4
11561327
MOV (SP)+,R5
11571328
CMP (SP)+,(SP)+ ; удаление числа B из стека
1158-
FMUL$X: JMP @#000000 ; возврат
1329+
FMUL$X: JMP @#000000 ; возврат, адрес подставляется в начале
11591330
;
11601331
;MREZ0: Формирование нулевого результата
11611332
FMUL$0: TST (SP)+ ; очистка стека
@@ -1302,7 +1473,7 @@ FDIV$D: MOV (SP)+,R4 ;
13021473
FDIV$E: MOV (SP)+,R4 ; восстановление регистров
13031474
MOV (SP)+,R5
13041475
CMP (SP)+,(SP)+ ; удаление числа B из стека
1305-
FDIV$X: JMP @#000000 ; возврат
1476+
FDIV$X: JMP @#000000 ; возврат, адрес подставляется в начале
13061477
;
13071478
;DALYT: Подпрограмма деления мантисс
13081479
FDIV$T: ASL R5 ; сдвиг результата

runtime-UKNC.tmac

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ WRSNG:
122122
CALL FUNPK ; число печатается в буфер BUFOUT
123123
MOV #BUFOUT, R0
124124
CALL WRST ; выводим строку на экран
125-
9$: JMP @#000000
125+
9$: JMP @#000000 ; возврат, адрес подставляется в начале
126126

127127
;#####################################################################
128128
;## WRST
@@ -875,10 +875,10 @@ FRND:
875875
BPL 1$ ; > 0 => генерация следующего
876876
; Параметр < 0 ? инициализация
877877
CLR RNDSAV ; сбросить сохранённое значение
878-
MOV #040311, III ; начальное состояние = PI/2
879-
MOV #007733, III+2
880-
1$: MOV #III, R2 ; адрес состояния (старшее слово)
881-
MOV #III+2, R3 ; адрес состояния (младшее слово)
878+
MOV #040311, FRND$I ; начальное состояние = PI/2
879+
MOV #007733, FRND$I+2
880+
1$: MOV #FRND$I, R2 ; адрес состояния (старшее слово)
881+
MOV #FRND$I+2, R3 ; адрес состояния (младшее слово)
882882
MOV (R2), R0 ; загрузить текущее состояние
883883
MOV (R3), R1
884884
BEQ 21$ ; если ноль, особая инициализация
@@ -956,8 +956,8 @@ FRND:
956956
;
957957
17$: BIC #100000,(SP) ; сбросить знак результата
958958
MOV SP,R2
959-
MOV (R2)+, III ; сохранить результат в III
960-
MOV (R2)+, III+2
959+
MOV (R2)+, FRND$I ; сохранить результат в III
960+
MOV (R2)+, FRND$I+2
961961
MOV -(R2), R1 ; восстановить R0,R1
962962
MOV -(R2), R0
963963
;RAN7:
@@ -968,8 +968,8 @@ FRND:
968968
MOV RNDSAV, (SP)
969969
FRND$X: JMP @#000000 ; возврат, адрес подставляется в начале
970970
;
971-
III: .WORD 040101, 027076 ; Рабочие ячейки FRND
972-
RNDSAV: .WORD 040101, 027076 ; Предыдущее псевдо-случайере число
971+
FRND$I: .WORD 040101, 027076 ; III: Рабочие ячейки FRND
972+
RNDSAV: .WORD 040101, 027076 ; Предыдущее псевдо-случайное число: старшее, младшее
973973

974974
;#####################################################################
975975
;## FSQR
@@ -982,7 +982,7 @@ RNDSAV: .WORD 040101, 027076 ;
982982
; (SP+2)(SP+4) - число с плавающей точкой (старшее слово, младшее слово)
983983
; На выходе: (SP)(SP+2) - результат
984984
FSQR:
985-
MOV (SP)+, R5 ; адрес возврата
985+
MOV (SP)+, FSQR$X+2 ; адрес возврата
986986
CLR -(SP) ; выделить слово для счётчика итераций (3)
987987
MOV SP,R4 ; R4 указывает на вершину стека (временный указатель)
988988
TST (R4)+ ; пропустить младшее слово аргумента
@@ -1017,9 +1017,9 @@ FSQR:
10171017
; R0,R1 - старшее и младшее слова результата
10181018
FSQR$E: MOV R0, (SP) ; заменяем старшее слово результатом
10191019
MOV R1, 2(SP) ; заменяем младшее слово
1020-
JMP @R5 ; возврат
1020+
FSQR$X: JMP @#000000 ; возврат, адрес подставляется в начале
10211021
;
1022-
FSQR$K: MOV (SP), R5 ; откуда вызвали
1022+
FSQR$K: MOV FSQR$X+2, R5 ; откуда вызвали
10231023
MOV #3605., R0 ; Ошибка: корень из отрицательного числа
10241024
JMP ERRR
10251025

0 commit comments

Comments
 (0)