-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathruntime-BK0010.tmac
More file actions
1815 lines (1773 loc) · 62.5 KB
/
runtime-BK0010.tmac
File metadata and controls
1815 lines (1773 loc) · 62.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; Шаблон рантайма для БК-0010
;
;#####################################################################
;## INIT
; Инициализация программы
MOV SP, SAVESP
;#####################################################################
;## TERM
; Завершение программы
SAVESP = . + 2
MOV #776, SP ; restore SP
RETURN ; return to Monitor/OS/etc.
;#####################################################################
;## WRCH
; Печать одного символа; R0 = символ
WRCH:
EMT 16 ; передача символа драйверу дисплея
RETURN
;#####################################################################
;## WREOL
; Печать перевода строки
WREOL:
MOV #12, R0 ; перевод строки (LF)
EMT 16 ; передача символа драйверу дисплея
RETURN
;#####################################################################
;## WRAT
; Подпрограмма PRINT AT(C,R), перемещение курсора
; R0 = строка 0..255, R1 = колонка 0..255
WRAT:
MOV R0, R2
EMT 24 ; установка курсора по координатам; R1 = col, R2 = row
RETURN
;#####################################################################
;## WRSPC
; Подпрограмма: PRINT SPC(N), печать пробелов
; R0 = количество пробелов, 1..255
WRSPC:
MOV R0, R1
MOV #040, R0 ; пробел
1$: EMT 16 ; передача символа драйверу дисплея
SOB R1, 1$
RETURN
;#####################################################################
;## WRTAB
;## Need WRSPC
; Подпрограмма: PRINT TAB(N), вывод пробелов пока не достигнем колонки N
; R0 = N
WRTAB:
EMT 26 ; чтение координат курсора; R1 = col, R2 = row
SUB R1, R0 ; сколько пробелов надо добавить
BLOS 9$ ; меньше ноля или ноль => выходим
JMP WRSPC ; добавляем пробелы
9$: RETURN
;#####################################################################
;## WRCOM
;## Need WRCH
; Подпрограмма PRINT , - перемещение курсора до начала следующей зоны, по 16 символов на зону
WRCOM:
MOV #40, R0 ;STUB
CALL WRCH ;STUB
RETURN ;STUB
;#####################################################################
;## WRINT
; Печать целого числа как -32768..32767
; R0 = целое число
WRINT:
MOV R0, R4
BPL 1$
MOV #'-, R0 ; минус
EMT 16 ; передача символа драйверу дисплея
NEG R4
1$: CLR R1 ; флаг значащих цифр
MOV #023420, R2 ; 10000
CALL 50$
MOV #001750, R2 ; 1000
CALL 50$
MOV #000144, R2 ; 100
CALL 50$
MOV #000012, R2 ; 10
CALL 50$
MOV R4, R0 ; остаток 0..9
ADD #060, R0 ; +'0'
BR 58$ ; последняя цифра всегда печатается
; вычитаем и печатаем цифру
50$: MOV #060, R0 ; '0'
52$: SUB R2, R4
BMI 54$
INC R0
INC R1
BR 52$
54$: ADD R2, R4
TST R1 ; проверяем на подавление лидирующих нулей
BNE 58$
CMP R0, #060
BEQ 60$ ; подавляем
58$: EMT 16 ; передача символа драйверу дисплея
60$: RETURN
;#####################################################################
;## WRSNG
;## Need FUNPK
;## Need WRST
; Печать Single числа
; На стеке: (SP) - адрес возврата
; (SP+2)(SP+4) - плавающее число float
WRSNG:
MOV (SP)+, 9$+2 ; сохраняем адрес возврата
CALL FUNPK ; число печатается в буфер BUFOUT
MOV #BUFOUT, R0
CALL WRST ; выводим строку на экран
9$: JMP @#000000 ; возврат, адрес подставляется в начале
;#####################################################################
;## WRST
; Печать строки
; R0 = адрес строки, первый байт строки содержит длину строки
WRST:
CLR R2
BISB (R0)+, R2 ; берём длину строки
BEQ WRSTRX ; пустая строка => выходим
; Точка входа: Печать строки R0, R2 = длина (не нулевая)
WRSTR1: MOV R0, R1
1$: MOVB (R1)+, R0
EMT 16 ; передача символа драйверу дисплея
SOB R2, 1$
WRSTRX: RETURN
;#####################################################################
;## STOP
;## Need WRST
;## Need WRINT
; Показываем где остановились на операторе STOP и зацикливаемся.
; На входе: R0 = номер строки 1..65535, R1 = номер строки исходного кода на Бейсике.
STOP:
MOV R1, -(SP)
MOV R0, -(SP)
MOV #STOP$1, R0 ; "STOP "
CALL WRST
MOV (SP)+, R0 ; номер строки
BEQ 1$ ; не задан => пропускаем
MOV R0, -(SP)
MOV #STOP$2, R0 ; "in line "
CALL WRST
MOV (SP)+, R0 ; номер строки
CALL WRINT ; выводим целое число
1$: MOV #STOP$3, R0 ; "at src line "
CALL WRST
MOV (SP)+, R0 ; номер строки исходного кода
CALL WRINT ; выводим целое число
BR . ; зацикливаемся
;
STOP$1: .ASCII <7>/STOP in/
STOP$2: .ASCII <6.>/ line /
STOP$3: .ASCII <10.>/ src line /
.EVEN
;#####################################################################
;## ERRR
;## Need WRST
;## Need WRINT
; Показ сообщения об ошибке времени выполнения.
; На входе: R0 = номер ошибки, R5 = адрес откуда вызвана процедура.
ERRR:
MOV R0, -(SP)
MOV #ERRR$1, R0
CALL WRST
MOV (SP)+, R0 ; номер ошибки
CALL WRINT
;TODO: Вывести " AT " и адрес R5 в восьмеричном виде
BR . ; зацикливаемся здесь
;
ERRR$1: .ASCII <6>/ERROR /
.EVEN
;#####################################################################
;## GETCR
; Получает координаты курсора
; R1 = колонка, R2 = строка
GETCR:
EMT 26
RETURN
;#####################################################################
;## CURSR
; Высвечивает/гасит курсор
; R0 = гасить если 0, высветить если не 0
CURSR:
; MOVB R0, @#000056 ; PRGAHK - Признак гашения курсора
;TODO
RETURN
;#####################################################################
;## INPI
; На входе: ничего
; Результат: R0 = введённое целое число.
INPI:
;TODO
RETURN
;#####################################################################
;## IMUL
;## Need ERRR
; Целочисленное умножение двух 16-разрядных чисел.
; На основе кода процедур SAND/DAUG из реализации Бейсик Вильнюс для БК-0010.
; На входе: R0, R1 = множители.
; Результат: R0
; При переполнении (результат не влезает в 16 бит) генерируется ошибка.
IMUL:
CLR R2 ; R0 = 0 (счётчик знаков: 0 ? чётное число минусов, 1 ? нечётное)
TST R0 ; первый операнд
BGE 1$ ; если >=0, пропустить
INC R2 ; иначе увеличить счётчик знаков (один минус)
NEG R0 ; взять модуль
1$: TST R1 ; второй операнд
BGE 2$ ; если >= 0, пропустить
DEC R2 ; иначе уменьшить счётчик знаков
NEG R1 ; взять модуль
2$: CALL DAUG ; умножить R1 * R0 (беззнаковое) -> результат в R5 (старшее) и R1 (младшее)
TST R5 ; проверить старшее слово (если не 0, то переполнение)
BLT IMULOV ; если R5 < 0 ? переполнение
TST R2 ; проверить счётчик знаков
BEQ 3$ ; если 0, пропустить
NEG R1 ; иначе сделать результат отрицательным
3$: MOV R1, R0 ; результат в R0
RETURN ; возврат
;
IMULOV: MOV (SP), R5 ; откуда вызвали
MOV #1706., R0 ; Ошибка: переполнение
JMP ERRR
;
; DAUG - умножение двух 16-разрядных положительных чисел (R1 * R0).
; Вход: R1, R0 ? положительные числа (0..32767).
; Выход: результат в R5 (старшее слово) и R1 (младшее).
; Если результат не помещается в 32 бита (переполнение), возвращает R5 = -1.
; Алгоритм: метод "сдвиг и сложение" с выбором меньшего множителя для ускорения.
DAUG: MOV R1, R5 ; R5 = меньший множитель (пока что R1)
CMP R5, R0
BLOS 3$ ; если R1 <= R0, то R5 уже меньший
MOV R0, R5 ; иначе R5 = R0 (меньший)
MOV R1, R0 ; R0 = R1 (больший)
3$: CLR R1 ; R1 = 0 (младшая часть результата)
4$: TST R5 ; проверка окончания цикла
BEQ 7$ ; если R5 = 0, выход
TST R0 ; проверка на переполнение при сдвиге (если R0 отрицательный)
BMI 6$ ; если старший бит R0 установлен ? переполнение
ASR R5 ; сдвинуть R5 вправо (проверить младший бит)
BCC 5$ ; если бит 0, то используем сдвиг (умножение на 2)
ADD R0, R1 ; иначе добавить R0 к младшей части результата
BVS 6$ ; если знаковое переполнение при сложении ? ошибка
5$: ASL R0 ; сдвинуть R0 влево (умножить на 2)
BR 4$ ; повторить
6$: MOV #-1, R5 ; признак переполнения
7$: RETURN
;#####################################################################
;## IDIV
;## Need ERRR
; Целочисленное деление 16-разрядных целых чисел со знаком.
; На основе кода процедуры DAL из реализации Бейсик Вильнюс для БК-0010.
; Вход: R0 = делитель, R1 = делимое
; Результат: R0 = частное, R1 = остаток
; Если делитель = 0, генерируется ошибка.
; При делении -32768 на -1 - ошибка переполнение.
IDIV:
MOV R0, R2 ; делитель
;
CLR R0 ; R0 = 0 (флаги знаков: бит0 ? знак делимого, бит1 ? знак делителя)
MOV R1, R3 ; R3 = копия делимого (для получения модуля)
BPL 1$ ; если делимое >= 0, пропустить
NEG R3 ; иначе взять модуль делимого
BMI IDIVOV ; если после NEG осталось отрицательное (было -32768) ? переполнение
INC R0 ; установить бит0 (делимое отрицательное)
1$: TST R2 ; проверить делитель
BEQ IDIV0 ; если ноль ? ошибка
BPL 3$ ; если делитель > 0, пропустить
NEG R2 ; взять модуль делителя
BIS #2, R0 ; установить бит1 (делитель отрицательный)
3$: MOV R2, R5 ; сохранить модуль делителя в R5
CLR R1 ; R1 = 0 (здесь будет частное)
5$: CMP R2, R3 ; сравнить текущий делитель (сдвигаемый) с делимым
BHI 4$ ; если делитель > делимого, выход из цикла масштабирования
ASL R2 ; иначе умножить делитель на 2 (сдвиг влево)
BR 5$ ; повторить
4$: CMP R2, R5 ; сравнить с исходным модулем делителя
BEQ 6$ ; если равны ? делитель изначально был больше делимого (частное 0)
CLC ; иначе подготовка к делению: сбросить C
ROR R2 ; сдвинуть делитель вправо (вернуться на предыдущий шаг)
ASL R1 ; сдвинуть частное влево (освободить младший бит)
CMP R2, R3 ; сравнить делитель с текущим остатком
BHI 4$ ; если делитель > остатка, бит частного = 0, перейти к следующему шагу
SUB R2, R3 ; иначе вычесть делитель из остатка
INC R1 ; установить младший бит частного в 1
BR 4$ ; продолжить деление
6$: ASR R0 ; сдвинуть R0 вправо: младший бит (знак делимого) -> C
BCC 7$ ; если C=0 (делимое было положительным), пропустить
NEG R1 ; иначе сделать частное отрицательным
NEG R3 ; и остаток отрицательным (знак делимого)
7$: TST R0 ; проверить бит1 (знак делителя)
BEQ 8$ ; если делитель был положительным, пропустить
NEG R1 ; иначе инвертировать знак частного (знаки разные)
8$:
MOV R1, R0 ; частное
MOV R3, R1 ; остаток
RETURN
;
IDIV0: MOV (SP), R5 ; откуда вызвали
MOV #1811., R0 ; Ошибка: деление на ноль
JMP ERRR
IDIVOV: MOV (SP), R5 ; откуда вызвали
MOV #1806., R0 ; Ошибка: переполнение
JMP ERRR
;#####################################################################
;## ITOF
; Конвертация Integer в плавающее число типа Single.
; На основе кода процедуры IR из реализации Бейсик Вильнюс для УКНЦ.
; Вход: R0 = целое значение -32768..32767
; Результат: два слова на стеке
ITOF:
MOV (SP), R5 ; адрес возврата из процедуры
MOV R0, (SP) ; под старшую часть (исходное целое)
CLR -(SP) ; под младшую часть
MOV R0, R1 ; Берём исходное целое и проверяем:
BGT 1$ ; если число > 0, переходим к нормализации
BEQ 4$ ; если число = 0, возвращаем 0.0 (на стеке уже два 0-ых слова)
NEG R1 ; Число отрицательное ? берём модуль в R1
1$: ROL -(SP) ; Уменьшаем SP и вращаем влево новое слово на вершине стека
MOV #220, R2 ; Загружаем в R2 начальное значение = 144. = 128. + 16.,
; где 16 ? максимальное число сдвигов.
CLRB 4(SP) ; Обнуляем мл.байт старшего слова результата (адрес SP+4)
; под мантиссу
2$: ROL R1 ; Сдвигаем R1 влево на 1 бит; старший бит уходит в C
BCS 3$ ; Если C=1 (найдена старшая единица), выходим из цикла
DEC R2 ; Иначе уменьшаем счётчик экспоненты
BR 2$ ; Продолжаем
3$: MOVB R1, 5(SP) ; Копируем мл.байт R1 (часть мантиссы) в ст.байт
; старшего слова результата (адрес SP+5).
CLRB R1 ; Обнуляем мл.байт R1, оставляя только ст.байт (биты 8?15)
BISB R2, R1 ; Логическое ИЛИ младшего байта R2 с младшим байтом R1
; Поскольку мл.байт R1 = 0, в него записывается мл.байт R2
; R2 = 128 + позиция старшей единицы (смещённая экспонента)
SWAB R1 ; Меняем байты в R1 местами: ст.байт = (128+позиция)
; мл.байт = ст.байт исходной мантиссы.
ROR (SP)+ ; Вращаем вправо слово по адресу SP (временное слово со знаком)
; и увеличиваем SP. Младший бит этого слова (знак) попадает в C.
ROR R1 ; Вращаем R1 вправо: старший бит заполняется из C (знак),
; младший бит (часть мантиссы) уходит в C.
RORB 3(SP) ; Вращаем вправо байт по адресу SP+3 (ст.байт старшего слова).
; Старший бит этого байта заполняется из C, что сохраняет
; вытесненный бит мантиссы.
MOV R1, (SP) ; результат младшая часть
4$: JMP @R5 ; возврат из процедуры
;#####################################################################
;## FTOI
;## Need ERRR
; Конвертация числа float в 16-битное целое со знаком.
; На основе кода процедуры RI из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) адрес возврата;
; (SP+2)(SP+4) плавающее число.
; На выходе стек очищен.
; Результат: R0 = целое число.
; Если число выходит за пределы -32768..32767 или не является целым, генерируется ошибка.
FTOI:
MOV (SP)+, R5 ; адрес возврата из процедуры
;
CLR R2 ; R2 будет аккумулятором целой части
INC R2 ; неявная единица мантиссы (1.xxxx)
MOV (SP)+, R1 ; забираем младшее слово числа (SP теперь на старшем слове)
ROL (SP) ; извлекаем знак в C, сдвигая старшее слово
ROL R1 ; знак попадает в младший бит R1
ROL -(SP) ; используем слово перед числом как временное для хранения знака
MOVB R1,R0 ; R0 = мл.байт R1 (знак + часть мантиссы)
CLRB R1 ; выделяем байт экспоненты
SWAB R1 ; экспонента в младшем байте R1
SUB #201, R1 ; минус 129: (экспонента - 128 - 1) = количество сдвигов для нормализации
BLT 1$ ; экспонента < 129 -> число < 1 -> целая часть 0
BEQ 2$ ; экспонента = 129 -> число = 1.0 -> сдвиг не нужен
CMP #17, R1 ; проверяем, не слишком ли велика экспонента (>15)
BLT FTOIOV ; экспонента <=15 -> нормализация
; экспонента >=16 -> число не помещается в 16 бит
SWAB R0 ; подготавливаем старшие биты мантиссы
CLRB R0
BISB 3(SP), R0 ; добавляем ещё 8 битов из временного слова
4$: ROL R0 ; сдвигаем 32-битную пару (R0,R2) влево
ROL R2
DEC R1 ; повторяем нужное число раз
BGT 4$
2$: NEG R2 ; результат пока положительный, меняем знак (если нужно)
BVS 5$ ; переполнение (R2 = -32768) требует специальной обработки
BGT FTOIOV ; если результат стал положительным после NEG ? ошибка (был 0)
7$: ROR (SP)+ ; извлекаем знак из временного слова
BCS 6$ ; если знак отрицательный, оставляем R2 как есть
NEG R2 ; иначе делаем положительным
6$: MOV R2, R0 ; помещаем результат в R0
TST (SP)+ ; подчищаем стек
JMP @R5 ; возврат
1$: CLR R2 ; число < 1 -> результат 0
BR 7$
5$: ROR (SP)+ ; извлекаем знак для особого случая (-32768)
BCS 6$ ; если знак отрицательный, -32768 допустим
;
FTOIOV: MOV (SP), R5 ; откуда вызвали
MOV #2006., R0 ; Ошибка: недопустимое значение или переполнение
JMP ERRR
;#####################################################################
;## FFIX
; Отбрасывание дробной части плавающего числа (float).
; И для положительных и для отрицательных чисел идёт к нулю.
; На основе кода процедуры SVD1 из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - плвающее число (старшее слово, младшее слово)
; Результат: (SP)(SP+2) - плвающее число (старшее слово, младшее слово)
FFIX:
MOV (SP)+, R5 ; адрес возврата
;
MOV (SP)+,R0 ; взять старшее слово числа
MOV (SP)+,R1 ; взять младшее слово числа
MOV R0,R3 ; копия старшего слова
ROL R3 ; сдвиг влево: знак в C, экспонента ? в старший байт
CLRB R3 ; оставить только старший байт (экспонента + старшие биты мантиссы)
SWAB R3 ; перенести его в младший байт (теперь R3 = смещённая экспонента)
SUB #230,R3 ; вычесть 152 (коррекция для получения количества сдвигов)
BGE 2$ ; если результат ≥ 0, число >= 1 -> переход к сохранению
CMP #177750,R3 ; сравнить с -28 (граница очень малых чисел)
BLT 1$ ; если R3 < -28, число очень маленькое ? перейти к отбрасыванию дробной части
CLR R0 ; иначе (число в (0,1)) результат 0
CLR R1
BR 2$ ; на сохранение
;
1$: MOV R3,-(SP) ; сохранить счётчик сдвигов
3$: ROR R0 ; сдвиг вправо (деление на 2) ? отбрасывание младших битов
ROR R1
INC R3 ; увеличить счётчик
BLT 3$ ; повторять, пока R3 < 0 (всего |R3| раз)
MOV (SP)+,R3 ; восстановить исходный счётчик
4$: ASL R1 ; сдвиг влево (умножение на 2) ? возврат масштаба
ROL R0
INC R3
BLT 4$ ; повторять, пока R3 < 0 (снова |R3| раз)
2$: MOV R1,-(SP) ; младшее слово результата на стек
MOV R0,-(SP) ; старшее слово результата на стек
;
JMP @R5 ; возврат
;#####################################################################
;## FINT
;## Need FFIX
;## Need FCMP
; Для плавающего числа (float) получает целое, меньшее чем аргумент.
; Для положительных чисел идёт к нулю, для отрицательных чисел идёт от нуля.
; На основе кода процедуры INT из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - плвающее число (старшее слово, младшее слово)
; Результат: (SP)(SP+2) - плвающее число (старшее слово, младшее слово)
FINT:
TST 2(SP) ; проверить знак числа (старшее слово)
BPL FFIX ; положительное или 0 => так же как FIX
; Отрицательное число: нужно округлить вниз, от нуля
CMP -(SP),-(SP) ; резервируем место на стеке
MOV 10(SP),-(SP) ; дублируем исходное число на стек
MOV 10(SP),-(SP)
MOV 2(SP),-(SP) ; дублируем исходное число ещё раз
MOV 2(SP),-(SP)
; На стеке: исх.число, исх.число, (SP+10)(SP+12) резерв, адрес возврата, исх.число
CALL FFIX ; отбрасываем дробную часть
MOV (SP),10(SP) ; копируем результат FIX в зарезерв. место на стеке
MOV 2(SP),12(SP)
CALL FCMP ; сравниваем два floats на стеке, снимает 4 слова
; На стеке: результат FIX, адрес возврата, исходное число
BEQ 1$ ; числа равны => нет дробной части
CLR -(SP) ; младшее слово константы 1.0
MOV #40200,-(SP) ; старшее слово константы 1.0
CALL FSUB ; вычитаем 1.0 из результата FIX
1$:
; На стеке: результат INT, (SP+4) адрес возврата, (SP+6)(SP+10) исходное число
MOV (SP)+,4(SP) ; копируем результат поверх исходного числа
MOV (SP)+,4(SP) ; и сдвигаем указатель стека
RETURN ; возврат
;#####################################################################
;## FINT
;## Need FFIX
;## Need FCMP
;## Need FSUB
; Для плавающего числа (float) получает целое, меньшее чем аргумент.
; Для положительных чисел идёт к нулю, для отрицательных чисел идёт от нуля.
; На основе кода процедуры INT из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - плвающее число (старшее слово, младшее слово)
; Результат: (SP)(SP+2) - плвающее число (старшее слово, младшее слово)
FINT:
TST 2(SP) ; проверить знак числа (старшее слово)
BPL FFIX ; положительное или 0 => так же как FIX
; Отрицательное число: нужно округлить вниз, от нуля
CMP -(SP),-(SP) ; резервируем место на стеке
MOV 10(SP),-(SP) ; дублируем исходное число на стек
MOV 10(SP),-(SP)
MOV 2(SP),-(SP) ; дублируем исходное число ещё раз
MOV 2(SP),-(SP)
; Теперь на стеке: исх.число, исх.число, (SP+10)(SP+12) резерв, адрес возврата, исх.число
CALL FFIX ; отбрасываем дробную часть
MOV (SP),10(SP) ; копируем результат FIX в зарезерв. место на стеке
MOV 2(SP),12(SP)
CALL FCMP ; сравниваем два floats на стеке, снимает 4 слова
; Теперь на стеке: результат FIX, адрес возврата, исходное число
BEQ 1$ ; числа равны => нет дробной части
CLR -(SP) ; младшее слово константы 1.0
MOV #40200,-(SP) ; старшее слово константы 1.0
CALL FSUB ; вычитаем 1.0 из результата FIX
1$:
; Теперь на стеке: результат INT, (SP+4) адрес возврата, (SP+6)(SP+10) исходное число
MOV (SP)+,4(SP) ; копируем результат поверх исходного числа
MOV (SP)+,4(SP) ; и сдвигаем указатель стека
RETURN ; возврат
;#####################################################################
;## FCMP
; Сравнение двух плавающих чисел (float).
; На основе кода процедуры SCMP из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - число B (старшее слово, младшее слово)
; (SP+6)(SP+10) - число A (старшее слово, младшее слово)
; На выходе стек очищен
; Результат: R0 = 0 (равны), >0 (первое больше), <0 (первое меньше);
; флаги: Z и N по числу R0, V=0, C=0.
FCMP:
MOV (SP)+, R5 ; адрес возврата
MOV @PC,R0
MOV 4(SP),R1 ; старшее слово числа A
BGE 1$ ; положительное -> 1$
ASL R0 ; отрицательное ? установить младший бит R0
MOV (SP)+,R2 ; снять старшее слово числа B
BLT 2$
BR 4$
1$: MOV (SP)+,R2 ; снять старшее слово числа B
BLT 5$
2$: CMP R1,R2 ; сравнить старшие слова
BNE 3$
CMP 4(SP),(SP) ; сравнить младшие слова
BNE 3$
CLR R0 ; равны
3$: ROR R0 ; знак в старший бит
BCS 5$
4$: NEG R0 ; отрицательный результат
5$: ADD #6,SP ; очистить стек
TST R0
JMP @R5 ; возврат
;#####################################################################
;## FSGN
; Знак плавающего чисела (float).
; На основе кода процедуры SGN из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - число (старшее слово, младшее слово)
; Результат: (SP+2)(SP+4) - число +1.0, -1.0 или 0.0, в зависимости от знака исходного числа.
FSGN:
MOV (SP)+, R5 ; адрес возврата
TST (SP) ; проверить ст.слово (если 0, то число 0)
BEQ 1$ ; ноль -> оставляем 0.0 на стеке
ASL (SP) ; сдвинуть ст.слово влево: знак -> C, обнулить мл.бит
MOV #100400,(SP) ; временно записать константу
ROR (SP) ; вращение вправо: в ст.бит попадает знак из C, остальное из константы
CLR 2(SP) ; обнулить младшее слово (для +1.0 или -1.0)
1$: JMP @R5 ; возврат
;#####################################################################
;## FUNPK
; Печать плавающего числа (float) в буфер.
; На основе кода процедур EGO/UNPCK из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) - адрес возврата
; (SP+2)(SP+4) - число с плавающей точкой (старшее слово, младшее слово)
; На выходе: число преобразовано в строку в буфере BUFOUT.
; (SP) = длина строки
;
BUFOUT: .BLKB 30
FUNPK:
MOV (SP)+,R3 ; адрес возврата
MOV (SP)+,R0 ; снимаем старшее слово числа в R0
MOV (SP),R1 ; берём младшее слово числа, не снимая, в R1
MOV #20,(SP) ; помещаем длину буфера на стек
MOV #BUFOUT,-(SP) ; адрес буфера вывода
MOV #7,-(SP) ; максимальное количество цифр целой части
MOV R3,-(SP) ; помещаем адрес возврата на стек
;
; UNPCK - преобразование 32-битного числа с плавающей точкой (DEC float) в строку
; На входе: число в R0 (старшее слово) и R1 (младшее слово).
; На стеке: (SP+12) - длина буфера
; (SP+10) - указатель на начало буфера
; Регистры: R4,R5 - рабочие, R2,R3 - временные.
; На выходе: строка записана в буфер, начиная с адреса, который был в (SP+10).
; (SP) = R2 = длина строки
; (SP+2) - адрес в буфере, указывает на последний символ
;UNPCK:
MOV #7,R3 ; R3 = 7 (максимальное количество цифр целой части)
;MOV R4,SAVJMP ; сохраняем R4 в глобальной переменной
MOV R5,-(SP) ; сохраняем R5 на стеке
MOV R3,-(SP) ; сохраняем R3 = 7 на стеке
; Теперь (SP)=7, (SP+2)=R5, (SP+4)=адр.возврата, (SP+6)=7, (SP+10)=20, (SP+12)=BUFOUT
CLR R2 ; обнуляем R2 = счётчик экспоненты
CLR R5 ; обнуляем R5 = счётчик знаков после запятой
MOV 12(SP),R4 ; R4 = начало буфера
ADD R4,10(SP) ; (SP+10) = начало буфера + длина = конец буфера
;
; Извлечение компонент числа: знак, экспонента, мантисса.
; Исходное число хранится в R0 (старшее слово) и R1 (младшее слово).
; После обработки R2 будет содержать истинную экспоненту (со знаком),
; а R0 и R1 - мантиссу (R0 - старшие 16 бит, R1 - младшие 16 бит).
ROL R0 ; сдвигаем старшее слово, чтобы извлечь знак
ROR (SP) ; знак (C) помещается в младший бит слова, на которое указывает SP
; (это сохранённое значение 7, используется как флаг)
SWAB R0 ; меняем байты местами, чтобы ст.байт (эксп-та+мантисса) оказался в младшем
BISB R0,R2 ; R2 получает ст.байт исходного R0 (экспонента + старшие биты мантиссы)
BNE NE0 ; если R2 != 0, число не ноль
;
; Число равно нулю - сразу переходим к формированию строки "0"
CLR R4 ; R4 = 0 (нет цифр)
INC R5 ; R5 = 1 (особый признак для формата G)
BR ISLIND ; переход к выводу
;
NE0: SEC ; устанавливаем C=1
ROR R0 ; восстанавливаем исходный старший бит R0 (часть мантиссы)
CLRB R0 ; обнуляем мл.байт R0 (оставляем только ст.байт)
SWAB R1 ; меняем байты R1: ст.байт R1 (биты 8-15 мантиссы) становится младшим
BISB R1,R0 ; теперь R0 = ст.байт исх-го R0 (эксп-та+старшие биты) | мл.байт исх-го R1
CLRB R1 ; обнуляем мл.байт R1, оставляя только ст.байт (биты 16-23 мантиссы)
SUB #200,R2 ; R2 = (смещённая экспонента) - 128 = истинная экспонента (со знаком)
BLT NEGAT ; если экспонента < 0, число < 1
BEQ ZERO ; если экспонента = 0, особый случай (мантисса = 1.0...)
;
; Экспонента положительная - число >= 1.
; Нужно выделить целую часть и преобразовать её в десятичные цифры.
POSIT: TST R0 ; проверяем старший бит R0 (бит 23 мантиссы)
BLT MAZINT ; если бит установлен, мантисса >= 2^23 - требуется коррекция масштаба
ASL R1 ; иначе сдвигаем мантиссу влево (умножение на 2)
ROL R0
DEC R2 ; уменьшаем экспоненту (компенсируем сдвиг)
BGT POSIT ; продолжаем, пока экспонента > 0
BR ZERO ; когда экспонента стала 0, переходим к формированию строки
;
MAZINT: CALL DG4DL5 ; умножаем мантиссу на 4/5 (масштабирование для десятичного разложения)
INC R5 ; увеличиваем счётчик десятичных знаков
SUB #3,R2 ; корректируем экспоненту (эквивалент сдвига вправо на 3 бита)
BGT POSIT ; продолжаем сдвиги, если экспонента ещё положительна
BEQ ZERO ; если экспонента стала 0, переходим к выводу
;
; Экспонента отрицательная - число < 1.
; Выделяем дробную часть, генерируя цифры после запятой.
NEGAT: CMP R0,#146314 ; сравниваем мантиссу с константой, соответствующей 1.0 в формате
BHIS PDALYT ; если мантисса >= 1.0, переходим к делению на 2
CMP R2,#-3 ; иначе проверяем экспоненту
BGT PDALYT ; если экспонента > -3, идём на деление
CALL DG5DL4 ; иначе умножаем мантиссу на 5/4 (масштабирование)
DEC R5 ; уменьшаем счётчик знаков после запятой
ADD #2,R2 ; корректируем экспоненту (увеличиваем на 2)
BR TOLIAU ; продолжаем
PDALYT: CALL DALYT2 ; делим мантиссу на 2 (сдвиг вправо)
TOLIAU: INC R2 ; увеличиваем экспоненту
BNE NEGAT ; пока экспонента не станет 0, повторяем
;
; Теперь экспонента = 0, мантисса приведена к диапазону [1.0, 10.0).
; Начинаем извлекать десятичные цифры (целую часть и дробные).
ZERO: CLR R4 ; обнуляем ст.байт (R4) для накопления очередной цифры
DAUG10: CALL DG5DL4 ; умножаем мантиссу на 5/4 (эквивалент умножения на 1.25)
CALL DAUG8 ; умножаем на 8 - суммарно умножение на 10
; теперь R4 содержит целую часть (цифру от 0 до 9)
TST R4 ; проверяем, получилась ли ненулевая цифра
BNE ISLIND ; если да, начинаем вывод
DEC R5 ; иначе уменьшаем счётчик знаков (пропускаем ведущие нули)
BR DAUG10 ; продолжаем извлекать следующую цифру
;
; ISLIND - начало формирования строки.
; Здесь уже получены все десятичные цифры (в R5 - их количество).
; Происходит коррекция указателей и вставка десятичной точки.
ISLIND: MOV 6(SP),R3 ; R3 = 7 (максимальное количество цифр целой части)
SUB R3,R2 ; R2 = R2 - 7 (смещение для выравнивания)
DEC R2 ; дополнительная коррекция
;
KLIOP: CALL APVAL ; записываем очередную цифру в буфер (используется R4)
;
; Далее логика определения позиции десятичной точки и обработки знаков
;
CMP R5,#-1 ; проверяем счётчик цифр
BLT 1$ ; если цифр меньше -1, пропускаем
CMP R5,6(SP) ; сравниваем с 7
BGT 1$ ; если больше 7, пропускаем
CLRB (SP) ; сбрасываем флаг (слово на вершине стека)
ADD R5,R2 ; корректируем указатель
1$: SUB #4,10(SP) ; уменьшаем указатель конца буфера на 4 (резервируем место)
E: ADD 10(SP),R2 ; R2 = R2 + конец буфера (теперь R2 указывает на позицию для цифр)
MOV R2,R3 ; R3 - текущий указатель в буфере
TSTB (SP) ; проверяем флаг (был ли установлен особый формат)
BNE 1$ ; если флаг установлен, переход к другой ветке
TST R5 ; проверяем количество цифр
BLE TRUP ; если цифр <= 0, идём на TRUP
SUB R5,R3 ; иначе отступаем назад на R5 позиций
DEC R3
CALL ZENKL ; записываем знак числа
BR OPA ; переходим к форматированию
1$: DEC R5
INC R2
;
TRUP: DEC R3 ; отступаем на одну позицию
CALL ZENKL ; записываем знак
MOVB #56,(R3)+ ; записываем символ '8' (временный заполнитель)
PNUL: CMP R3,10(SP) ; проверяем, не достигли ли конца буфера
BHIS SKAITM
MOVB #60,(R3)+ ; заполняем нулями
BR PNUL
SKAITM: MOV R2,R3 ; восстанавливаем указатель
TSTB (SP) ; проверяем флаг
BEQ 1$
DEC R3 ; коррекция
BR OPA
1$: INC R3
SUB R5,R3 ; корректируем позицию
OPA: CALL FORMSK ; финальное форматирование (вставка десятичной точки и т.п.)
TST (SP)+ ; удаляем одно слово со стека (флаг)
BEQ VISKA1 ; если флаг не установлен, пропускаем вывод экспоненты
;
; Вывод экспоненты (формат E или G)
MOVB #105,(R3)+ ; символ 'E'
MOVB #'+,@R3 ; символ '+'
TST R5 ; проверяем знак экспоненты
BGE VIRS0
NEG R5 ; если отрицательная, меняем знак
MOVB #55,(R3) ; символ '-'
VIRS0: INC R3 ; переходим к следующему символу
EILE: MOVB #60,@R3 ; временно '0'
DESIMT: SUB #12,R5 ; вычитаем 10 (десятичное)
BLT VIENZ
INCB @R3 ; увеличиваем цифру
BR DESIMT
VIENZ: ADD #72,R5 ; преобразуем остаток в ASCII-цифру (0-9)
INC R3 ; переходим к следующему байту
MOVB R5,(R3)+ ; записываем младшую цифру экспоненты
;
; Завершение FUNPK
VISKA1: MOV R3,R2 ; сохраняем конечный указатель
VISKAS: MOVB #40,(R2)+ ; записываем пробел
MOV (SP)+,R5 ; восстанавливаем R5
MOV (SP)+,(SP) ; восстанавливаем SP ??
SUB 4(SP),R2 ; вычисляем длину строки
MOV R2,2(SP) ; сохраняем длину
MOV (SP)+, R5 ; адрес возврата
MOV (SP)+, R2 ; длина строки
MOV (SP), R0 ; адрес начала строки
MOV R5, (SP) ; адрес возврата
JMP WRSTR1 ; выводим полученную строку R0, R2 = длина
;
; DG4DL5 - умножение 24-битного числа (R1:R0) на 4/5 (0.8)
; путём многократного деления на 2 и сложения с исходным значением.
; Используется для извлечения десятичных цифр при выводе чисел.
; Вход: R0 ? старшее слово, R1 ? младшее слово (24-битное число).
; Выход: результат в R0,R1; R4 разрушается.
DG4DL5: MOV #10,R4 ; счётчик итераций = 10
CALL DALYT2 ; разделить число на 2 (сдвиг вправо)
MOV R1,-(SP) ; сохранить младшее слово (исходное/2)
MOV R0,-(SP) ; сохранить старшее слово (исходное/2)
IRDAR: CALL DL4 ; разделить текущее число на 4 (два сдвига)
DARSYK: CALL DALYT2 ; разделить на 2 (теперь всего деление на 8 от исходного)
ADD 2(SP),R1 ; прибавить сохранённое (исходное/2) к текущему
ADC R0
ADD (SP),R0 ; прибавить старшее слово (исходное/2)
NEG R4 ; изменить знак счётчика (управление циклами)
BLT DARSYK ; если R4 отрицательный, повторить внутренний цикл
DEC R4 ; иначе уменьшить счётчик
BGT IRDAR ; если ещё не 0, повторить внешний цикл
CMP (SP)+,(SP)+ ; очистить стек (два слова)
RETURN
;
; Подпрограмма DL4: беззнаковое деление 24-битного числа (R0:R1) на 4.
; Реализована через два вызова DALYT2 (деление на 2 дважды).
DL4: CLC
ROR R0
ROR R1
DALYT2: CLC
ROR R0
ROR R1
RETURN
;
; Подпрограмма DG5DL4: умножает 24-битное число (R1:R0:R4) на 5/4,
; т.е. вычисляет (5/4)*X. Используется в паре с DAUG8 для умножения на 10.
; Вход: R1 = младшее слово, R0 = среднее слово, R4 = ст.байт/слово (24 бита).
; Выход: результат в тех же регистрах (R1:R0:R4).
DG5DL4: MOV R0,-(SP) ; сохраняем исходное R0 (среднее слово)
MOV R1,-(SP) ; сохраняем исходное R1 (младшее слово)
CALL DL4 ; подпрограмма деления на 4 (сдвиг вправо на 2 бита)
; После DL4: (R1:R0:R4) = X/4, с учётом переносов.
ADC R1 ; коррекция переноса после деления (если DL4 не сохранила флаги)
ADC R0 ; аналогично
ADD (SP)+,R1 ; добавляем сохранённое исходное R1 (X младшее) к R1 (X/4 младшее)
ADC R0 ; перенос в среднее слово
ADC R4 ; перенос в ст.байт
ADD (SP)+,R0 ; добавляем сохранённое исходное R0 (X среднее) к R0 (X/4 среднее)
ADC R4 ; финальный перенос в ст.байт
RETURN ; результат: X + X/4 = (5/4)*X
;
; Подпрограмма DAUG8: умножает 24-битное число (R1:R0:R4) на 8.
; Используется как часть умножения на 10 (вместе с DG5DL4).
; Вход: R1 - младшее слово, R0 - среднее слово, R4 - ст.байт.
; Выход: результат в тех же регистрах (R1:R0:R4) = 8*X.
DAUG8: MOV #3,-(SP) ; счётчик = 3 (три сдвига влево)
DG2: ASL R1 ; сдвиг влево младшего слова (умножение на 2)
ROL R0 ; сдвиг влево среднего слова с переносом из младшего
ROL R4 ; сдвиг влево старшего байта с переносом из среднего
DEC (SP) ; уменьшаем счётчик
BGT DG2 ; повторяем 3 раза - всего умножение на 2^3 = 8
TST (SP)+ ; удаляем счётчик из стека
RETURN
;
; APVAL - добавление десятичной цифры в буфер и подготовка к следующей.
; На входе: R3 - текущий указатель в буфере (куда писать цифру).
; R4 - десятичная цифра (0?9), полученная из мантиссы.
; Стек содержит ранее сохранённые значения регистров и флаги.
; На выходе: цифра записывается в буфер, R5 увеличивается (счётчик знаков),
; R4,R0,R1 могут быть изменены.
; Если позиция R3 выходит за допустимые границы (0..10), ничего не делается.
; Если R3 = 0, выполняется особая коррекция: к цифре добавляется 5 (округляет?).
; Для позиций 1?10 выполняются вычисления, связанные с масштабированием,
; которые, видимо, корректируют мантиссу для извлечения следующей цифры.
APVAL: CMP R3,#12 ; R3 > 10?
BGT NEREIK ; если да - выход (недопустимая позиция)
TST R3 ; проверяем R3 на 0
BEQ SVEIKS ; если 0 - особая обработка
BLT NEREIK ; если отрицательное - выход
MOV R4,-(SP) ; сохраняем цифру
MOV R0,-(SP) ; сохраняем мантиссу (старшее слово)
MOV R1,-(SP) ; сохраняем мантиссу (младшее слово)
MOV #100000,R0 ; загружаем константу 2^15 (32768)
CLR R1 ; R1=0
DALINT: DEC R3 ; уменьшаем позицию
BEQ UZTEKS ; если достигли нуля - выход из цикла
CALL DG4DL5 ; умножаем (R1:R0:R4) на 4/5
CALL DL4 ; делим на 4 (фактически сдвиг на 2)
CALL DALYT2 ; делим на 2 (сдвиг на 1)
BR DALINT ; повторяем для каждой позиции
UZTEKS: CLR R4 ; обнуляем R4 (ст.байт)
ADD (SP)+,R1 ; добавляем сохранённую цифру к R1
ADC R0 ; перенос в R0
ADD (SP)+,R0 ; добавляем сохранённое старшее слово мантиссы
ADC R4 ; перенос в R4
ADD (SP)+,R4 ; добавляем сохранённую цифру в R4 (финальная коррекция)
PATIKR: CMP #12,R4 ; проверяем, не превысила ли цифра 10.
BGT NEREIK ; если >10 - выход (некорректно)
INC R5 ; увеличиваем счётчик знаков после запятой
NEREIK: RETURN
SVEIKS: ADD #5,R4 ; при R3=0 добавляем 5 к цифре (округление)
BR PATIKR ; и проверяем результат
;
; ZENKL - вставка знака числа в строку.
; На входе: R3 - указатель на текущую позицию в буфере (куда писать знак).
; На стеке (SP+2) - слово, содержащее флаг знака в младшем бите.
; На выходе: в буфер по адресу R3 записывается либо пробел,
; либо, если знак отрицательный, минус
; R3 увеличивается на 1.
ZENKL: MOV R3,14(SP) ; сохраняем указатель в стеке (для последующего использования)
MOVB #40,(R3)+ ; записываем пробел (ASCII пробел) и увеличиваем R3
ASL 2(SP) ; сдвигаем влево слово на стеке (флаг знака); бит знака попадает в C
BCC 1$ ; если C=0 (положительное число), то оставляем пробел
MOVB #55,-1(R3) ; иначе заменяем последний записанный байт на минус
1$: RETURN
;
; FORMSK - финальное форматирование строки: вставка десятичной точки,
; добавление недостающих нулей, округление.
; На входе: R2 - указатель на конец целой части (или начало буфера цифр),
; R3 - текущий указатель в буфере,
; R4 - текущая десятичная цифра (для добавления после точки),
; Стек содержит адрес конца буфера (SP+14) и другие служебные данные.
; На выходе: в буфер вставляется десятичная точка, дробные цифры,
; возможно, округление; R3 становится указателем на следующий свободный байт.
FORMSK: MOV R2,-(SP) ; сохраняем R2 (указатель на начало целой части)
DEC (SP) ; уменьшаем его на 1 (для удобства сравнений)
CMP #12,R4 ; сравниваем R4 с 10.
BGT NEDVIZ ; если цифра >10, пропускаем вставку десятичной точки
MOV R3,(SP) ; иначе сохраняем текущий указатель в стеке
MOVB #61,(R3)+ ; записываем символ '=' - возможно, десятичная точка?
SUB #12,R4 ; вычитаем 10 из R4 (коррекция для следующего шага)
NEDVIZ: CMP R2,R3 ; сравниваем указатель начала целой части и текущий
BNE NETASK ; если не равны, переходим
MOVB #56,(R3)+ ; иначе записываем '8' - возможно, заполнитель?
NETASK: CMP 14(SP),R3 ; проверяем, не достигли ли конца буфера
BLOS GRIZT ; если R3 <= конец буфера, выходим
TST R4 ; проверяем, есть ли цифра для вывода
BEQ 1$ ; если нет, пропускаем
MOV R3,(SP) ; сохраняем указатель
1$: ADD #60,R4 ; преобразуем цифру (0?9) в ASCII-символ
MOVB R4,(R3)+ ; записываем цифру
CLR R4 ; обнуляем R4 (цифра использована)
CALL DG5DL4 ; умножаем мантиссу на 5/4 (для следующей цифры)
CALL DAUG8 ; умножаем на 8 - суммарно умножаем на 10
BR NEDVIZ ; повторяем для следующей цифры
GRIZT: MOV (SP)+,R3 ; восстанавливаем R3 (текущий указатель)
INC R3 ; переходим к следующему символу
CMP R3,R2 ; сравниваем с указателем начала целой части
BHIS 2$ ; если R3 >= R2, то всё
MOV R2,R3 ; иначе восстанавливаем R2
2$: RETURN
;#####################################################################
;## FRND
;## Need FADD
;## Need FSUB
;## Need FMUL
;## Need FDIV
; Генератор псевдо-случайных чисел.
; На основе кода процедуры RAN из реализации Бейсик Вильнюс для УКНЦ.
; На входе: (SP) адрес возврата
; (SP+2)(SP+4) = управляющий параметр:
; >0: получить очередное псевдо-случайное число
; <0: задаёт начальное значение
; 0: вернуть предыдущее псевдо-случайное число
; Результат: (SP)(SP+2) псевдо-случайное число в диапазоне [0,1)
FRND:
MOV (SP)+, FRND$X+2 ; сохраняем адрес для выхода
TST (SP) ; проверяем старшее слово параметра
BEQ 26$ ; параметр 0 => отдать текущее значение
BPL 1$ ; > 0 => генерация следующего
; Параметр < 0 ? инициализация
CLR RNDSAV ; сбросить сохранённое значение
MOV #040311, FRND$I ; начальное состояние = PI/2
MOV #007733, FRND$I+2
1$: MOV #FRND$I, R2 ; адрес состояния (старшее слово)
MOV #FRND$I+2, R3 ; адрес состояния (младшее слово)
MOV (R2), R0 ; загрузить текущее состояние
MOV (R3), R1
BEQ 21$ ; если ноль, особая инициализация
; state = (state * 3 + 1) mod 2^31
ASL R1 ; умножить на 2
ROL R0
ADD (R2), R0 ; state*3
ADD (R3), R1
ADC R0
ADD (R3), R0 ; + state (младшее слово) -> state*3 + 1
BPL 2$ ; если нет переполнения знакового бита
ADD #100000,R0 ; инвертировать знаковый бит
;RAN2:
2$: MOV R0, (R2) ; сохранить новое состояние в III
MOV R1, (R3)
MOV #201,R2 ; начальная экспонента = 129.
4$: ASL R1 ; нормализация:
ROL R0 ; сдвигать мантиссу, пока старшая единица не выйдет в C
BCS 23$
DEC R2 ; уменьшать экспоненту
BR 4$
;RAN3: Упаковка в формат float: знак = 0, экспонента = R2, мантисса из старших битов R0,R1
23$: CLRB R1
BISB R0,R1
SWAB R1
CLRB R0
BISB R2,R0
SWAB R0
ROR R0 ; знак = 0
ROR R1
BR 25$
;RAN1: Если состояние обнулилось (например, при инициализации нулём), используем (1,3)
21$: MOV #3,R1
INC R0
BR 2$
;RAN5:
25$: TST RNDSAV ; RNDSAV ещё не заполнено? первый вызов после инициализации
BNE 27$ ; не ноль, значит уже было ? просто сохраняем новое
; Первое после инициализации: преобразуем целое состояние в float [0,1)
MOV R1,-(SP) ; поместить полученное число (R0,R1) на стек
MOV R0,-(SP)
CLR R5 ; R5 = 0 (счётчик масштабирования)
MOV 6(SP),-(SP) ; Дублируем число для операций
MOV 6(SP),-(SP)
BIC #100000,(SP) ; сбросить знак (работаем с положительным числом)
; Цикл приведения в диапазон [0,1)
13$: CMP #040200,(SP) ; сравнить старшее слово с 1.0
BGT 11$ ; если > 1.0 -> уменьшить
DEC R5 ; иначе увеличиваем счётчик
BGE 15$ ; если R5 >= 0, выходим (достигли нужного интервала)
; Число < 1.0, но R5 < 0 -> нужно делить на 10.0
CLR -(SP) ; константа 10.0 младшее слово
MOV #041040,-(SP) ; константа 10.0 старшее слово
CALL FDIV ; разделить число на 10.0
BR 13$ ; продолжить цикл
11$: MOV SP,R2
MOV (R2)+,6(R2) ; копирование числа
MOV (R2)+,6(R2)
INC R5
BLE 15$
CLR -(SP) ; константа 10.0 младшее слово
MOV #041040,-(SP) ; константа 10.0 старшее слово
CALL FMUL ; умножить на 10.0