-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfloat32avr.asm
1764 lines (1514 loc) · 119 KB
/
float32avr.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
;
; Float32AVR - библиотека подпрограмм для работы с числами в формате бинарной плавающей точки одинарной точности.
; Кроме арифметики содержит вспомогательные подпрограммы для конвертации из ASCII и в ASCII.
;
; Copyright (с) 2024 Igor Voytenko <[email protected]>
;
; Частичная совместимость с IEEE 754:
; - Не реализованы спец. значения: inf, nan.
; - Не реализованы денормализованные числа.
; - Реализован только один режим округления: к ближайшему/к четному.
; - Реализован только положительный ноль.
;
; Тем не менее, граничные значения экспоненты -127 и 128 (0 и 255 в коде со смещением)
; остаются зарезервированными для спец. значений и денормализованных чисел
; чтобы можно было довести до полной совместимости в будущем
; а также для удобства тестирования и сравнения с эталонной IEEE 754 реализацией прямо сейчас.
;
; Обработка исключительных ситуаций.
; В случае возникновения исключительной ситуации (деление на ноль, переполнение)
; происходит прыжок на адрес, который должен быть предварительно загружен в Z-регистр перед вызовом подпрограммы.
;
; Байты исходной мантиссы делимого,
; расширенные GUARD-байтом для безопасного сдвига влево.
.DEF MANTA0=R8
.DEF MANTA1=R9
.DEF MANTA2=R10
.DEF MANTAG=R2
;
; Байты исходной мантиссы делителя,
; Расширенные GUARD-байтом для формирования доп. кода отрицательной мантиссы.
.DEF MANTB0=R12
.DEF MANTB1=R13
.DEF MANTB2=R14
.DEF MANTBG=R3
;
; Байты доп. кода отрицательной мантиссы делителя.
.DEF MANTB0NEG=R4
.DEF MANTB1NEG=R5
.DEF MANTB2NEG=R6
.DEF MANTBGNEG=R7
;
; Расширенные экспоненты.
.DEF EXPA0=R11 ; Первый операнд.
.DEF EXPA1=R20 ;
.DEF EXPR0=R11 ; Результат.
.DEF EXPR1=R20 ;
.DEF EXPB0=R15 ; Второй операнд.
.DEF EXPB1=R21 ;
;
; Байты мантиссы частного.
.DEF Q0=R22
.DEF Q1=R23
.DEF Q2=R24
.DEF Q3=R25
.EQU QDIGITS=24+2 ; Количество цифр частного к вычислению: 24 + R + G + S (S определяется вне цикла).
.DEF STEPS=R17 ; Счетчик цикла.
.EQU RGSMASK=0b00000111 ; Маска для извлечения RGS-битов при округлении.
.DEF RGSBITS=R18 ; Дополнительные биты мантиссы частного + STICKY-бит для корректного округления.
.DEF RSIGN=R0 ; Знак результата (частное/произведение/алгебраическая сумма).
;
; Мантисса произведения.
.DEF MANTP0=R17
.DEF MANTP1=R18
.DEF MANTP2=R19
.DEF MANTP3=R23
.DEF MANTP4=R24
.DEF MANTP5=R25
.DEF GUARD=R7 ; GUARD-регистр для временного хранения R-бита мантиссы произведения.
.DEF STATUS0=R5 ; Регистр статуса после операции над младшим байтом.
.DEF STATUS1=R6 ; Регистр статуса после операции над старшим байтом.
.DEF SREGACC=R17 ; Регистр статуса после нескольких операций. например, побитовое И регистра STATUS.
;
; Делит два числа по схеме с неподвижным делителем без восстановления остатка.
;
; Вход:
; - R11, R10, R9, R8: Делимое.
; - R15, R14, R13, R12: Делитель.
;
; Выход:
; - R11, R10, R9, R8: Частное.
FDIV32: ;
; Фильтрация операндов.
CLR R16 ;
OR R16,R12 ;
OR R16,R13 ;
OR R16,R14 ;
OR R16,R15 ;
IN R16,SREG ;
SBRC R16,SREG_Z ; Делитель равен нулю?
IJMP ; Да, выбрасываем ошибку. Делимое при этом может быть как нулевым так и ненулевым - оба варианта некорректны.
CLR R16 ; Нет, проверяем делимое.
OR R16,R8 ;
OR R16,R9 ;
OR R16,R10 ;
OR R16,R11 ;
IN R16,SREG ;
SBRC R16,SREG_Z ; Делимое равно нулю?
RJMP SETZERO ; Да, возвращаем ноль.
; Нет, оба операнда ненулевые, вычисляем частное.
;
; Определение знака частного.
MOV RSIGN,R11 ; Копируем старший байт делимого.
MOV R1,R15 ; Копируем старший байт делителя.
LDI R16,0b10000000 ; Загружаем маску знака.
AND RSIGN,R16 ; Извлекаем знак делимого.
AND R1,R16 ; Извлекаем знак делителя.
EOR RSIGN,R1 ; Определяем знак частного.
;
; Распаковка делимого.
ROL R10 ; MSB мантиссы делимого содержит LSB экспоненты. Сдвигаем его в бит переноса.
ROL R11 ; Избавляемся от знака делимого и восстанавливаем младший бит экспоненты.
ROR R10 ; Возвращаем на место старший байт мантиссы делимого.
OR R10,R16 ; Восстанавливаем скрытую единицу мантиссы.
;
; Распаковка делителя.
ROL R14 ; То же самое для делителя.
ROL R15 ;
ROR R14 ;
OR R14,R16 ;
;
; Вычисление экспоненты частного.
CLR EXPA1
CLR EXPB1
COM EXPB0 ; Формируем доп. код экспоненты делителя.
COM EXPB1 ;
LDI R16,1 ;
ADD EXPB0,R16 ;
LDI R16,0 ;
ADC EXPB1,R16 ;
ADD EXPA0,EXPB0 ; EXPA=EXPA-EXPB.
ADC EXPA1,EXPB1 ;
LDI R16,127 ; Восстанавливаем результат в коде со смещением.
ADD EXPA0,R16 ;
LDI R16,0 ;
ADC EXPA1,R16 ;
;
; Формирование доп. кода мантиссы делителя.
CLR MANTAG ;
CLR MANTBG ;
MOV MANTB0NEG,MANTB0 ; Копируем положительную мантиссу делителя.
MOV MANTB1NEG,MANTB1 ;
MOV MANTB2NEG,MANTB2 ;
MOV MANTBGNEG,MANTBG ;
COM MANTB0NEG ; Поскольку 2^N-|B|=(2^N-1-|B|)+1=COM(|B|)+1,
COM MANTB1NEG ; то инвертируем биты положительной мантиссы
COM MANTB2NEG ;
COM MANTBGNEG ;
LDI R16,1 ; и прибавляем единицу,
ADD MANTB0NEG,R16 ; не забывая про возможное появление бита переноса.
LDI R16,0 ;
ADC MANTB1NEG,R16 ;
ADC MANTB2NEG,R16 ;
ADC MANTBGNEG,R16 ;
;
; Вычисление мантиссы частного.
LDI STEPS,QDIGITS ; Количество шагов равно количеству вычисляемых цифр частного.
CLR Q0 ; Зануляем мантиссу частного.
CLR Q1 ;
CLR Q2 ;
CLR Q3 ;
SUBMANTB: ADD MANTA0,MANTB0NEG ; Вычитаем из мантиссы делимого или остатка
ADC MANTA1,MANTB1NEG ; мантиссу делителя,
ADC MANTA2,MANTB2NEG ; умноженную на вес
ADC MANTAG,MANTBGNEG ; очередной цифры частного.
CALCDIGIT: IN R16,SREG ;
SBRS R16,SREG_N ; Остаток отрицательный?
SBR Q0,1 ; Нет, устанавливаем текущую цифру частного в 1.
DEC STEPS ; Вычислены все цифры частного?
BREQ RESTPOSREM ; Да, восстанавливаем последний положительный остаток.
CLC ; Освобождаем и зануляем LSB для следующей цифры частного.
ROL Q0 ;
ROL Q1 ;
ROL Q2 ;
ROL Q3 ;
CLC ; Сдвигаем остаток влево вместе с виртуальной
ROL MANTA0 ; разрядной сеткой, которая привязана к нему.
ROL MANTA1 ; Неподвижная мантисса делителя в этой сетке
ROL MANTA2 ; станет эквивалентна умноженной на вес следующей
ROL MANTAG ; младшей цифры частного, которую мы будем выяснять.
IN R16,SREG ;
SBRS R16,SREG_N ; Остаток положительный?
RJMP SUBMANTB ; Да, отнимаем мантиссу делителя.
ADD MANTA0,MANTB0 ; Нет, прибавляем мантиссу делителя.
ADC MANTA1,MANTB1 ;
ADC MANTA2,MANTB2 ;
ADC MANTAG,MANTBG ;
RJMP CALCDIGIT ; Определяем следующую цифру частного.
RESTPOSREM: IN R16,SREG ;
SBRS R16,SREG_N ; Последний остаток уже положительный?
RJMP CALCSTICKY ; Да, переходим к вычислению STICKY-бита.
ADD MANTA0,MANTB0 ; Нет, восстанавливаем до последнего положительного.
ADC MANTA1,MANTB1 ;
ADC MANTA2,MANTB2 ;
ADC MANTAG,MANTBG ;
;
; Вычисление STICKY-бита для корректного округления к ближайшему.
;
; Если остаток ненулевой, значит справа от частного существуют ненулевые биты.
; S=1, R>0
; S=0, R=0
CALCSTICKY: COM MANTA0 ; Вычисление доп. кода остатка.
COM MANTA1 ; Инвертируем остаток: 2^N-1-A < 2^N (для всех значений A).
COM MANTA2 ; Прибавляем единицу: 2^N-1-A+1=2^N-A < 2^N (только для ненулевых A).
COM MANTAG ; Следовательно, только при нулевом остатке
LDI R16,1 ; из старшего байта будет единица переноса.
ADD MANTA0,R16 ; А это значит, что S=NOT(C), где C - бит переноса.
LDI R16,0 ;
ADC MANTA1,R16 ;
ADC MANTA2,R16 ;
ADC MANTAG,R16 ;
IN R16,SREG ; Конвертируем бит переноса в S-бит.
LDI R17,1 ;
EOR R16,R17 ;
OUT SREG,R16 ;
ROL Q0 ; Добавляем справа к мантиссе частного значение S-бита.
ROL Q1 ;
ROL Q2 ;
ROL Q3 ;
;
; Нормализация мантиссы частного.
;
; Мантисса частного лежит в интервале (0.5, 2)
; поэтому денормализация возможна только на 1 разряд вправо.
SBRC Q3,2 ; Целочисленная единица в частном есть?
RJMP CHECKEXP ; Да, частное нормализовано, проверяем экспоненту.
CLC ; Нет, нормализуем влево на 1 разряд.
ROL Q0 ;
ROL Q1 ;
ROL Q2 ;
ROL Q3 ;
LDI R16,0xFF ; Уменьшаем экспоненту частного на 1.
LDI R17,0XFF ;
ADD EXPR0,R16 ;
ADC EXPR1,R17 ;
;
; Проверка экспоненты на переполнение/антипереполнение.
;
; Переполнение: EXP > 127+127=254. По стандарту - установка inf. Текущая реализация - выброс исключения.
; Антипереполнение: EXP < -126+127=1. По стандарту - переход к денормализованному числу. Текущая реализация - установка частного в ноль.
CHECKEXP: MOV R18,EXPR0 ; Копируем расширенную экспоненту частного.
MOV R19,EXPR1 ;
LDI R16,255 ; Формируем -1 в доп. коде.
LDI R17,255 ;
ADD R16,R18 ; Если истинная экспонента меньше минимального представимого значения (-126),
ADC R17,R19 ; то в коде со смещением после вычитания единицы будет получено отрицательное число.
IN R16,SREG ;
SBRC R16,SREG_N ; Несмещенная экспонента меньше -126?
RJMP SETZERO ; Да, антипереполнение, возвращаем ноль.
;
LDI R16,1 ; Нет, проверяем экспоненту на переполнение.
LDI R17,0 ; Если истинная экспонента больше максимального представимого значения (127),
ADD R16,R18 ; то в коде со смещением после прибавления единицы старший байт расширенной экспоненты
ADC R17,R19 ; будет отличен от нуля.
COM R17 ; Если старший байт содержит ноль,
LDI R16,1 ; то вычисление доп. кода даст ноль.
ADD R16,R17 ; Несмещенная экспонента меньше 128?
BREQ ROUND ; Да, переполнения нет, переходим к округлению.
IJMP ; Нет, переполнение, прыжок на обработчик ошибок, указанный в Z.
;
; Округление к ближайшему.
;
; NOTE: Округление происходит уже после нормализации (если была денормализация).
;
; Возможные сочетания битов RS. Для краткости GUARD-бит здесь не учитывается, иллюстрируется сама идея округления.
; RS
; --
; 00: Точное значение. |ERR| = 0.
; 01: Отбрасываем. |ERR| < 2^-24=2^-23/2=ULP/2. Ошибка меньше половины веса последнего разряда мантиссы одинарной точности.
; 10: Если такая ситуация имеет место, значит делимое имеет ненулевые разряды за пределами исходной сетки одинарной точности, что невозможно в нашем случае (обоснование - в доке).
; 11: Отбрасываем и прибавляем 2^-23. |ERR| < 2^-24=ULP/2.
;
; Комментарии к последнему случаю:
; Q - истинная мантисса частного (бесконечная точность).
; Q' - округленное значение.
; Q' = Q-(2^-24+A)+2^-23, где A - биты за пределами сетки вправо от R, индикатором которых является S-бит, следовательно, A < 2^-24.
; 2^-23 = 2^-24+2^-24 = 2^-24+(A+B), где (A+B) = 2^-24, но A > 0, следовательно B < 2^-24.
; тогда можем записать Q' = Q-2^-24-A+2^-24+A+B = Q+B, где B < 2^-24.
; Поэтому в последнем случае |ERR| < 2^-24=ULP/2.
ROUND: MOV RGSBITS,Q0 ; Извлекаем RGS-биты из младшего байта мантиссы частного.
LDI R16,RGSMASK ;
AND RGSBITS,R16 ;
LDI STEPS,3 ; Отбрасываем RGS-биты в мантиссе частного.
RSHIFT3: CLC ; Мы вычисляли 26 цифр частного + S-бит,
ROR Q3 ; поэтому после сдвига все цифры мантиссы частного
ROR Q2 ; поместятся в трех младших байтах.
ROR Q1 ;
ROR Q0 ;
DEC STEPS ;
BRNE RSHIFT3 ;
LDI R16,0xFC ; Если в RGS установлен бит R и есть ненулевые биты справа от него,
ADD RGSBITS,R16 ; тогда в RGS находится число больше 4, а значит, отбрасывая RGS
IN R16,SREG ; мы получаем ошибку больше ULP/2.
SBRC R16, SREG_N ; Отбросили больше ULP/2?
RJMP PACK ; Нет, пакуем частное.
LDI R16,1 ; Да, прибавляем 2^-23.
ADD Q0,R16 ; Переполнения при этом не будет, потому что
LDI R16,0 ; нормализованная мантисса, которая даст переполнение - больше максимальной возможной нормализованной мантиссы,
ADC Q1,R16 ; а денормализованная мантисса, которая даст после нормализации переполнение,
ADC Q2,R16 ; может быть получена только если делимое имеет ненулевые разряды за пределами одинарной точности, что невозможно в нашем случае.
;
; Упаковка знака, мантиссы и экспоненты частного и запись на место делимого.
; NOTE: Нормализованная и округленная мантисса частного сейчас занимает 3 младших байтах.
PACK: ROL Q0 ; Сдвигаем мантиссу влево, убирая целочисленную единицу.
ROL Q1 ;
ROL Q2 ;
CLC ; Выдвигаем вправо LSB экспоненты в разряд переноса,
ROR EXPR0 ; одновременно освобождая MSB под знак.
ROR Q2 ; Возвращаем мантиссу на место
ROR Q1 ; с LSB экспоненты вместо целочисленной единицы.
ROR Q0 ;
OR EXPR0,RSIGN ; Устанавливаем разряд знака.
;
; Запись мантиссы частного на место делимого.
MOV MANTA0,Q0
MOV MANTA1,Q1
MOV MANTA2,Q2
RJMP EXIT
;
; Умножает два числа по схеме с неподвижным множителем.
; NOTE: Мы рассматриваем умножение множителя на множимое, т.е. B*A.
;
; Вход:
; - R11, R10, R9, R8: Множимое.
; - R15, R14, R13, R12: Множитель.
;
; Выход:
; - R11, R10, R9, R8: Произведение.
FMUL32: ;
; Фильтрация операндов.
CLR R16 ;
OR R16,R8 ;
OR R16,R9 ;
OR R16,R10 ;
OR R16,R11 ;
IN R16,SREG ;
SBRC R16,SREG_Z ; Множимое равно нулю?
RJMP SETZERO ; Да, возвращаем ноль.
CLR R16 ; Нет, проверяем множитель.
OR R16,R12 ;
OR R16,R13 ;
OR R16,R14 ;
OR R16,R15 ;
IN R16,SREG ;
SBRC R16,SREG_Z ; Множитель равен нулю?
RJMP SETZERO ; Да, возвращаем ноль.
;
; Определение знака произведения.
MOV RSIGN,R11 ; Копируем старший байт множимого.
MOV R1,R15 ; Копируем старший байт множителя.
LDI R16,0b10000000 ; Загружаем маску знака.
AND RSIGN,R16 ; Извлекаем знак множимого.
AND R1,R16 ; Извлекаем знак множителя.
EOR RSIGN,R1 ; Определяем знак произведения.
;
; Распаковка множимого.
ROL R10 ; MSB мантиссы содержит LSB экспоненты. Сдвигаем его в бит переноса.
ROL R11 ; Избавляемся от знака и восстанавливаем младший бит экспоненты.
SEC ; Восстанавливаем скрытую единицу мантиссы.
ROR R10 ; Возвращаем на место старший байт мантиссы.
;
; Распаковка множителя.
ROL R14 ; То же самое что и для множимого.
ROL R15 ;
SEC ;
ROR R14 ;
;
; Вычисление экспоненты произведения.
;
; Поскольку экспоненты представлены в коде со смещением, то
; их значения всегда являются положительными числами в диапазоне [1,254].
CLR EXPA1
CLR EXPB1
ADD EXPA0,EXPB0 ; EXPA=EXPA+EXPB.
ADC EXPA1,EXPB1 ; Сумма экспонент содержит избыточное значение 127.
LDI R16,-127 ; Необходимо отнять это значение.
LDI R17,255 ; Формируем доп. код для -127 в двойной сетке.
ADD EXPA0,R16 ; Восстанавливаем сумму экспонент
ADC EXPA1,R17 ; в коде со смещением.
;
; Вычисление мантиссы произведения.
LDI R22,24 ; Количество шагов цикла равно количеству цифр множимого.
CLR MANTP0 ; Зануляем произведение.
CLR MANTP1 ;
CLR MANTP2 ;
CLR MANTP3 ;
CLR MANTP4 ;
CLR MANTP5 ;
NEXTDIGIT: ROR MANTA2 ; Извлекаем очередную цифру множимого.
ROR MANTA1 ;
ROR MANTA0 ;
IN R16,SREG ;
SBRS R16,SREG_C ; Цифра равна 1?
RJMP LOOPCOND0 ; Нет, равна 0, множитель не прибавляем.
ADD MANTP3,MANTB0 ; Да, прибавляем множитель к аккумулятору.
ADC MANTP4,MANTB1 ; Младшие 3 байта множителя в двойной сетке нулевые,
ADC MANTP5,MANTB2 ; поэтому достаточно сложить только старшие байты.
LOOPCOND0: DEC R22 ; Это была последняя цифра множимого?
BREQ CHECKOVF0 ; Да, мантисса произведения вычислена, проверяем её на переполнение.
ROR MANTP5 ; Нет, делим аккумулятор на 2.
ROR MANTP4 ;
ROR MANTP3 ;
ROR MANTP2 ;
ROR MANTP1 ;
ROR MANTP0 ;
RJMP NEXTDIGIT ; Переходим к следующей цифре множимого.
CHECKOVF0: IN R16,SREG ;
SBRS R16,SREG_C ; Мантисса произведения дала переполнение?
RJMP ROUNDPROD ; Нет, переходим к её округлению.
ROR MANTP5 ; Да, нормализуем мантиссу произведения вправо на 1 разряд.
ROR MANTP4 ;
ROR MANTP3 ;
ROR MANTP2 ;
ROR MANTP1 ;
ROR MANTP0 ;
LDI R16,1 ; Корректируем экспоненту.
ADD EXPA0,R16 ;
LDI R16,0 ;
ADC EXPA1,R16 ;
;
; Округление мантиссы произведения.
;
; После установки S-бита и извлечения пары RS
; MANTP2 может содержать следующие значения:
; - 0b11000000
; - 0b10000000
; - 0b01000000
; - 0b00000000
ROUNDPROD: CLR GUARD
CLC
ROL MANTP0 ; Сдвигаем R-БИТ в GUARD-регистр.
ROL MANTP1 ; Теперь младшая часть содержит только биты после R.
ROL MANTP2 ;
ROL GUARD ;
COM MANTP0 ; Если после R-БИТА все биты нулевые,
COM MANTP1 ; то вычисление доп. кода младшей части
COM MANTP2 ; даст бит переноса.
LDI R16,1 ; Поэтому отсутствие бита переноса
ADD MANTP0,R16 ; используем как признак того,
LDI R16,0 ; что после R-бита есть хотя бы один ненулевой бит.
ADC MANTP1,R16 ;
ADC MANTP2,R16 ;
IN R16,SREG ;
SBRS R16,SREG_C ; После r-бита есть ненулевые биты?
SBR MANTP2,0b10000000 ; Да, устанавливаем S-бит.
ROR GUARD ; Нет, вся младшая часть нулевая (включая S-бит, поэтому явно обнулять S-бит нет необходимости).
ROR MANTP2 ; Восстанавливаем R-бит.
ROR MANTP1 ;
ROR MANTP0 ;
LDI R16,0b11000000 ; Извлекаем RS-биты.
AND MANTP2,R16 ;
CLR GUARD ; Интерпретируем регистр с RS-битами как число и формируем его доп. код.
COM MANTP2 ; Доп. код формируем в двойной сетке, т.к. для представления в доп. коде
COM GUARD ; значений 0b11000000 И 0b10000000 со знаком минус
LDI R16,1 ; одинарной сетки уже не достаточно.
ADD MANTP2,R16 ;
LDI R16,0 ;
ADC GUARD,R16 ;
CLR STATUS0 ; Разность между опорным значеним 0b10000000 и числовой интерпретацией RS
CLR STATUS1 ; однозначно связана с направлением округления (см. доку).
LDI R16,0b10000000 ; За признак берем нулевой результат и знак полученной разности.
ADD MANTP2,R16 ;
IN STATUS0,SREG ; Сохраняем флаги после операции с младшим байтом.
LDI R16,0 ;
ADC GUARD,R16 ;
IN STATUS1,SREG ; Сохраняем флаги после операции со старшим байтом.
SBRS STATUS1,SREG_N ; RS=0b11000000? [NOTE: Отрицательная разность возможна только в ситуации 0b10000000-0b11000000.]
RJMP HALFWAY ; Нет, проверяем следующий вариант.
LDI R16,1 ; Да, младшая часть больше ulp/2. Округляем в большую сторону.
ADD MANTP3,R16 ; Отбрасываем младшую часть и прибавляет ULP.
LDI R16,0 ; Это эквивалентно прибавлению к младшей части величины меньше ULP/2,
ADC MANTP4,R16 ; приводящему к занулению младшей части и появлению бита переноса в MANTP3.
ADC MANTP5,R16 ;
RJMP CHECKOVF1 ;
HALFWAY: AND STATUS1,STATUS0 ; Разность нулевая, если флаг Z был установлен для каждого байта.
SBRS STATUS1,SREG_Z ; RS=0b10000000?
RJMP CHECKEXP1 ; Нет, RS=0b01000000 или RS=0b00000000. Младшая часть меньше ULP/2, просто отбрасываем её. Переполнение при округлении не возможно - пропускаем проверку.
LDI R16,0b00000001 ; Да, симметричное округление. Младшая часть равна ULP/2, округляем к четному.
AND R16,MANTP3 ; Извлекаем ULP в R16.
ADD MANTP3,R16 ; Если ULP=1, то старшая часть нечетная
LDI R16,0 ; и прибавление R16 (который также содержит 1) даст переход к четному.
ADC MANTP4,R16 ; Если же ULP=0, то значение уже четное
ADC MANTP5,R16 ; и прибавление R16 (который также содержит 0) никакого эффекта не даст, оставляя значение четным.
;
; Проверка мантиссы произведения на переполнение после округления.
CHECKOVF1: IN R16,SREG ;
SBRS R16,SREG_C ; Округление дало переполнение?
RJMP CHECKEXP1 ; Нет, переходим к проверке экспоненты.
ROR MANTP5 ; Да, нормализуем мантиссу произведения вправо на 1 разряд.
ROR MANTP4 ;
ROR MANTP3 ;
LDI R16,1 ; Корректируем экспоненту.
ADD EXPA0,R16 ;
LDI R16,0 ;
ADC EXPA1,R16 ;
;
; Проверка итогового произведения на переполнение/антипереполнение по экспоненте.
;
; Если экспонента меньше -126 (-126+127=1 в коде со смещением), то произведение слишком мало для представления в одинарном float и мы переходим к нулю.
; Если экспонента больше 127 (127+127=254 в коде со смещением), то произведение слишком велико и мы выбрасываем исключение.
CHECKEXP1: MOV R18,EXPR0 ; Копируем расширенную экспоненту произведения.
MOV R19,EXPR1 ;
LDI R16,255 ; Формируем -1 в доп. коде.
LDI R17,255 ;
ADD R16,R18 ; Если истинная экспонента меньше минимального представимого значения (-126),
ADC R17,R19 ; то в коде со смещением после вычитания единицы будет получено отрицательное число.
IN R16,SREG ;
SBRC R16,SREG_N ; Истинная экспонента меньше -126?
RJMP SETZERO ; Да, антипереполнение, возвращаем ноль.
;
LDI R16,1 ; Нет, проверяем экспоненту на переполнение.
LDI R17,0 ; Если истинная экспонента больше максимального представимого значения (127),
ADD R16,R18 ; то в коде со смещением после прибавления единицы старший байт расширенной экспоненты будет отличен от нуля.
ADC R17,R19 ; Истинная экспонента меньше 128?
BREQ PACKPROD ; Да, переполнения нет, переходим к упаковке.
IJMP ; Нет, переполнение, прыжок на обработчик ошибок, указанный в Z.
;
; Упаковка мантиссы и экспоненты произведения.
PACKPROD: ROL MANTP3 ; Сдвигаем мантиссу влево, убирая целочисленную единицу.
ROL MANTP4 ; NOTE: Достаточно сдвинуть только старший байт мантиссы.
ROL MANTP5 ;
CLC ; Выдвигаем вправо LSB экспоненты в разряд переноса,
ROR EXPR0 ; одновременно освобождая MSB под знак.
ROR MANTP5 ; Возвращаем мантиссу на место
ROR MANTP4 ; с LSB экспоненты вместо целочисленной единицы.
ROR MANTP3 ;
OR EXPR0,RSIGN ; Устанавливаем разряд знака.
MOV MANTA0,MANTP3 ; Запись мантиссы произведения на место мантиссы множимого.
MOV MANTA1,MANTP4
MOV MANTA2,MANTP5
RJMP EXIT
; Выход из FMUL32.
EXIT: RET
;
; Установка результата в ноль.
;
; Выполняется в следующих случаях:
; - Антипереполнение результата для любой операции.
; - Делимое равно нулю.
; - Хотя бы один сомножитель равен нулю.
; - Оба слагаемых равны нулю.
; - Результат вычитания равен нулю.
SETZERO: CLR MANTA0
CLR MANTA1
CLR MANTA2
CLR EXPA0
RJMP EXIT
;
; Вычисляет разность двух чисел.
;
; Вход:
; - R11, R10, R9, R8: Уменьшаемое.
; - R15, R14, R13, R12: Вычитаемое.
;
; Выход:
; - R11, R10, R9, R8: Разность.
FSUB32: LDI R16,0b10000000 ; B=-B.
EOR B3,R16 ;
RJMP FADD32 ;
;
; Складывает два числа.
;
; Вход:
; - R11, R10, R9, R8: Первое слагаемое.
; - R15, R14, R13, R12: Второе слагаемое.
;
; Выход:
; - R11, R10, R9, R8: Сумма.
FADD32: ;
; Своп.
; Установка наибольшего (по модулю) операнда первым.
MOV R0,R8 ; Копируем A.
MOV R1,R9 ;
MOV R2,R10 ;
MOV R3,R11 ;
MOV R4,R12 ; Копируем B.
MOV R5,R13 ;
MOV R6,R14 ;
MOV R7,R15 ;
LDI R16,0b01111111 ;
AND R3,R16 ; Вычисляем |A|.
AND R7,R16 ; Вычисляем |B|.
COM R4 ; Вычисляем доп. код |B|.
COM R5 ;
COM R6 ;
COM R7 ;
LDI R16,1 ;
ADD R4,R16 ;
LDI R16,0 ;
ADC R5,R16 ;
ADC R6,R16 ;
ADC R7,R16 ;
ADD R4,R0 ; |A|-|B|.
ADC R5,R1 ; Перезаписываем -|B|, чтобы сохранить нетронутым |A|.
ADC R6,R2 ;
ADC R7,R3 ;
; |A|-|B|>=0?
BRGE HANDLEZERO ; Да, своп не нужен. Переходим к обработке нулевых операндов.
; Нет, делаем своп.
MOV R3,R11 ; Бэкапим A. Но поскольку регистры R0..R3 уже хранят |A|, остается только восстановить знак.
MOV R8,R12 ; Записываем B на место A.
MOV R9,R13 ;
MOV R10,R14 ;
MOV R11,R15 ;
MOV R12,R0 ; Восстанавливаем из бэкапа A на место B.
MOV R13,R1 ;
MOV R14,R2 ;
MOV R15,R3 ;
;
; Обработка нулевых операндов.
;
; Возможные ситуации до свопа (где 1 - любое ненулевое значение операнда):
; 0,0
; 0,1
; 1,0
; 1,1
;
; После свопа остаются только следующие варианты:
; 0,0
; 1,0
; 1,1
;
; Следовательно, если после свопа первый операнд нулевой, значит оба нулевые, результат - ноль.
; Если второй операнд нулевой, значит первый ненулевой, результат - первый операнд.
HANDLEZERO: CLR R16 ;
OR R16,MANTA0 ;
OR R16,MANTA1 ;
OR R16,MANTA2 ;
OR R16,EXPA0 ; A=0?
BREQ SETZERO ; Да, и A и B равны нулю, возвращаем ноль.
CLR R16 ; Нет, проверяем B.
OR R16,MANTB0 ;
OR R16,MANTB1 ;
OR R16,MANTB2 ;
OR R16,EXPB0 ; B=0?
BREQ EXIT ; Да, возвращаем A (A уже находится в регистре результата).
; Нет, ни A ни B не равны нулю, продолжаем вычисления.
;
; Определение знака суммы.
;
; Берется знак операнда A, который после свопа удовлетворяет выражению |A|>=|B|.
; Если |A|>|B| и знаки разные, то знак разности равен знаку наибольшего (по модулю) операнда, т.е. A.
; Если же знаки одинаковые, то знак суммы равен знаку любого операнда, в т.ч. A.
; Если |A|=|B| и знаки одинаковые, то знак суммы также равен знаку любого операнда, в т.ч. A.
; Если же знаки разные, то в силу равенства модулей, разность будет равна нулю и будет установлен положительный знак, независимо от знаков A и B.
CALCSIGN: MOV RSIGN,R11 ; Копируем старший байт A.
LDI R16,0b10000000 ; Формируем маску для извлечения знака, который хранится в MSB.
AND RSIGN,R16 ; Извлекаем знак A.
;
; Бэкап знака B.
;
; Он будет нужен для определения операции: сложение или вычитание.
MOV R1,R15 ; Копируем старший байт B.
AND R1,R16 ; Извлекаем знак B. Маска знака уже содержится в R16.
;
; Распаковка операндов.
ROL R8 ; Распаковка A.
ROL R9 ;
ROL R10 ; Выдвигаем в бит переноса LSB экспоненты.
ROL R11 ; Восстанавливаем экспоненту в старшем байте.
SEC ; Восстанавливаем в мантиссе A неявную единицу.
ROR R10 ;
ROR R9 ;
ROR R8 ;
ROL R12 ; Распаковка B
ROL R13 ;
ROL R14 ;
ROL R15 ;
SEC ;
ROR R14 ;
ROR R13 ;
ROR R12 ;
;
; Расширяем экспоненту A на один байт влево.
CLR EXPA1 ;
;
; Расширение мантисс до RGS.
;
; Эти регистры стыкуются справа от мантиссы A и B.
CLR R6 ; RGS мантиссы A.
CLR R7 ; RGS мантиссы B.
;
; Выравнивание порядков.
;
; Экспоненты обоих операндов представлены в коде со смещением и принимают значения в отрезке [1,254].
; После свопа порядок A будет либо больше либо равен порядку B. Это значит, что разность экспонент лежит в отрезке [0,253].
; Из всего этого следует, что нет необходимости вычислять корректный доп. код в двойной сетке (см. обоснование в доке).
;
; Здесь может потребоваться окруление до S-бита при денормализации мантиссы B.
MOV R17,EXPA0 ; Копируем экспоненту A.
MOV R16,EXPB0 ; Копируем экспоненту B.
COM R16 ; Вычисляем младший байт доп. кода экспоненты B.
INC R16 ;
ADD R17,R16 ; EXP(A)-EXP(B)=0? [NOTE: R17 теперь содержит разность экспонент в отрезке [0,253].]
BREQ CHOOSEOP ; Да, порядки равны, выравнивание не требуется.
LDI R16,31 ; Нет, выясняем, в каком отрезке лежит разность: [1,30] или [31,253].
COM R16 ; Формируем доп. код числа -31 в пределах байта. [NOTE: Необходимости в двойной сетке нет.]
INC R16 ;
ADD R16,R17 ; (EXP(A)-EXP(B))-31<0? [NOTE: Если истинная разность в двойной сетке отрицательная, то бита переноса из младшего байта не будет.]
BRCC SHIFTMANTB ; Да, разность в отрезке [1,30], сдвигаем мантиссу B и формируем S-бит.
CLR MANTB0 ; Нет, разность в отрезке [31,253];
CLR MANTB1 ; просто устанавливаем значение мантиссы B как 2^-31 (округление до S-бита).
CLR MANTB2 ;
LDI R16,0b00000001 ;
MOV R7,R16 ;
RJMP CHOOSEOP ;
;
; Пошаговый сдвиг мантиссы B на разность экспонент вправо.
;
; Разность экспонент здесь принимает значения в отрезке [1,30].
; Если за пределами RGS-зоны оказался хотя бы один единичный бит, то происходит установка S-бита.
SHIFTMANTB: CLR R16 ; R16 будет хранить в LSB значение бита переноса после каждого сдвига.
CLC ;
ROR MANTB2 ; Сдвигаем мантиссу B вправо на 1 разряд вместе с RGS-битами.
ROR MANTB1 ;
ROR MANTB0 ;
ROR R7 ;
ROL R16 ; Извлекаем бит переноса в R16.
OR R7,R16 ; Если C!=0, значит за пределами RGS оказался единичный бит, значит устанавливаем S-бит.
DEC R17 ; Мантисса B сдвинута на разность порядков?
BREQ CHOOSEOP ; Да, переходим к выбору арифметической операции.
RJMP SHIFTMANTB ; Нет, сдвигаем дальше.
;
; Выбор арифметической операции.
CHOOSEOP: EOR R1,R0 ; SIGN(A)=SIGN(B)?
BRNE DIFF ; Нет, знаки разные, переходим к вычитанию.
; Да, вычисляем сумму.
;
; Вычисление суммы модулей мантисс.
;
; Здесь возможно только переполнение результата.
; Мантисса суммы записывается на место мантиссы A.
SUM: ADD R6,R7 ; Складываем мантиссы A и B.
ADD R8,R12 ; У мантиссы A RGS-зона всегда нулевая, поэтому бит переноса не возможен.
ADC R9,R13 ;
ADC R10,R14 ;
; Переполнение есть?
BRCC ROUNDSUM ; Нет, переходим к округлению.
ROR R10 ; Да, нормализуем мантиссу вправо.
ROR R9 ;
ROR R8 ;
ROR R6 ;
CLR R16 ; Устанавливаем S-бит, если при нормализации был потерян единичный бит.
ROL R16 ;
OR R6,R16 ;
INC EXPA0 ; Корректируем экспоненту.
RJMP ROUNDSUM ; В худшем случае экспонента уже равна 254, поэтому прибавление единицы не даст бит переноса в старший байт.
;
; Вычисление разности модулей мантисс.
;
; NOTE: Здесь в худшем случае возможна денормализация результата вправо в отрезке [0,24] при A=1 и B=((2^24)-1)*2^-23*2^-1.
; Допустим, что после вычитания мы можем получить денормализацию больше, чем на 24 разряда вправо (при этом помним, что мантисса A всегда нормализована),
; тогда мантисса числа B должна содержать больше, чем 24 разряда, что невозможно.
DIFF: COM R7 ; Вычисляем псевдо доп. код мантиссы B. Это дополнение до 2 вместо 4.
COM MANTB0 ; Результат всегда положительный, поэтому нет необходимости в истинном доп. коде: в доп. бите слева всегда
COM MANTB1 ; будет единица, а из младшей части всегда будет бит переноса, зануляющий разряд истинного доп. кода.
COM MANTB2 ;
LDI R16,1 ;
ADD R7,R16 ;
CLR R16 ;
ADC MANTB0,R16 ;
ADC MANTB1,R16 ;
ADC MANTB2,R16 ;
LDI SREGACC,0b00000010 ; Маска для Z-флага.
ADD R6,R7 ; Складываем RGS-регистры.
IN R16,SREG ; Извлекаем из регистра статуса только Z-флаг.
AND SREGACC,R16 ;
ADD MANTA0,MANTB0 ; Складываем следующую пару байт мантисс. NOTE: У мантиссы A RGS-зона всегда нулевая, поэтому бит переноса из предыдущей операции не возможен.
IN R16,SREG ;
AND SREGACC,R16 ;
ADC MANTA1,MANTB1 ; Складываем следующую пару байт.
IN R16,SREG ;
AND SREGACC,R16 ;
ADC MANTA2,MANTB2 ; Складываем последнюю пару байт.
IN R16,SREG ;
AND SREGACC,R16 ; Результат нулевой? NOTE: SREGACC=(STATUS0)&(STATUS1)&(STATUS2)&(STATUS3)&(0b00000010), где STATUS<N> - регистр статуса после сложения очередной пары байт мантисс.
BRNE SETZERO1 ; Да, устанавливаем положительный ноль. NOTE: Если флаг Z был установлен для каждой пары байт, то SREGACC окажется ненулевым.
SBRC MANTA2,7 ; Мантисса разности денормализована?
RJMP ROUNDSUM ; Нет, мантисса нормализована, переходим к округлению.
; Да, выполняем нормализацию и коррекцию экспоненты.
CLR R16 ; Счетчик степени денормализации.
LDI R17,255 ; Увеличиваем счетчик степени денормализации на -1, получая отрицательное значение сразу в доп. коде.
NORM: CLC ;
ROL R6 ; Нормализуем влево.
ROL MANTA0 ;
ROL MANTA1 ;
ROL MANTA2 ;
ADD R16,R17 ; DEC R16. NOTE: Конечно, мы могли бы использовать инструкцию DEC.
SBRS MANTA2,7 ; Мантисса разности нормализовалась?
RJMP NORM ; Нет, продолжаем сдвиг.
ADD EXPA0,R16 ; Да, корректируем экспоненту: EXPA-K, где K=R16 - степень денормализации.
ADC EXPA1,R17 ; R17,R16: расширили доп. код R16 до двух байт (воспользовались тем, что R17 уже содержит 255).
;
; Округление.
;
; После сдвига R-бита происходит проверка C- и Z-битов в регистре статуса.
; Если C=1 и Z=0 после сдвига, то это ситуация симметричного округления, иначе - округление в большую сторону.
ROUNDSUM: MOV R16,R6 ; Копируем RGS.
CLC ;
ROL R16 ; R-бит равен нулю?
BRCC CHECKEXP2 ; Да, RGS=000|001|010|011. Отбрасываем RGS, ошибка ERR<ULP/2.
BREQ HALFWAY1 ; Нет, RGS=100, симметричное округление, ERR=ULP/2.
LDI R16,1 ; Нет, RGS=101|110|111.
RJMP ADDULP ; Отбрасываем RGS и прибавляем ULP. Ошибка ERR<ULP/2.
HALFWAY1: LDI R16,1 ; Извлекаем значение разряда ULP.
AND R16,R8 ;
ADDULP: ADD R8,R16 ; Прибавляем ULP.
CLR R16 ; Если значение нечетное, то ULP=1, и прибавление ULP даст четный результат.
ADC R9,R16 ; Если значение уже четное, то ULP=0, и прибавление нуля не изменит результат.
ADC R10,R16 ; Есть переполнение?
BRCC CHECKEXP2 ; Нет, переходим к проверке экспоненты.
ROR MANTA2 ; Да, нормализуем мантиссу A. Поскольку переполнение при округлении, два младших байта мантиссы уже нулевые.
INC EXPA0 ; Корректируем экспоненту.
;
; Проверка экспоненты на переполнение/антипереполнение.
;
; Экспонента в коде со смещением принимает значения в отрезке [-22,255].
; NOTE: Возьмем разность чисел A=1*2^-125 и B=((2^24)-1)*2^-23*2^-126.
; Эта разность даст максимальную денормализацию вправо на 24 разряда.
; Следовательно, после нормализации порядок разности будет равен -125-24=-149 или -149+127=-22 в коде со смещением.
; Точно такой же вывод получим, взяв A=(1+2^-23)*2^-126 и B=1*2^-126.
;
; Если есть переполнение, то экспонента равна 255 и вычитание её из 255 даст ноль.
; Если нет переполнения, то вычитание экспоненты из 255 даст положительное значение.
; Если есть антипереполнение, то экспонента принимает значения в отрезке [-22,0] и вычитание единицы из экспоненты всегда даст отрицательное значение.
; Если нет антипереполнения, то, поскольку переполнение уже исключено, экспонента лежит в [1,254] и вычитание единицы всегда даст неотрицательное значение.
CHECKEXP2: LDI R17,255 ; Записываем 255 в два байта.
LDI R18,0 ;
MOV R21,EXPA0 ; Копируем расширенную экспоненту A.
MOV R22,EXPA1 ;
COM R21 ; Вычисляем доп. код экспоненты в двух байтах.
COM R22 ; NOTE: Необходимости в доп. коде в двойной сетке нет:
LDI R16,1 ; достаточно проверить результат вычитания на ноль и проверить бит переноса.
ADD R21,R16 ;
CLR R16 ;
ADC R22,R16 ;
ADD R17,R21 ; 255-EXP(A).
IN STATUS0,SREG ; Сохраняем флаги после сложения младших байт.
ADC R18,R22 ;
IN STATUS1,SREG ; Сохраняем флаги после сложения старших байт.
AND STATUS0,STATUS1 ; Результат нулевой, если флаг Z был установлен для каждого байта.
SBRC STATUS0,SREG_Z ; Экспонента равна 255?
IJMP ; Да, переполнение, прыжок на обработчик ошибок, указанный в регистре Z.
LDI R16,255 ; Нет, проверяем на антипереполнение.
LDI R17,255 ;
ADD R16,EXPA0 ; EXP(A)-1.
ADC R17,EXPA1 ; Результат отрицательный?
BRMI SETZERO1 ; Да, антипереполнение, экспонента лежит в [0,-22] и не может быть представлена. Возвращаем ноль.
; Нет, экспонента лежит в [1,254] и представима в одинарном float.
;
; Упаковка суммы.
ROL MANTA0 ; Выдвигаем целочисленную единицу мантиссы суммы в бит переноса.
ROL MANTA1 ;
ROL MANTA2 ;
ROL RSIGN ; Выдвигаем знак в бит переноса.
ROR EXPA0 ; Вдвигаем знак в MSB экспоненты и выдвигаем LSB экспоненты в бит переноса.
ROR MANTA2 ; Восстанавливаем исходные биты мантиссы,
ROR MANTA1 ; вдвигая в MSB старшего байта мантиссы вместо целочисленной единицы LSB экспоненты.
ROR MANTA0 ;
RJMP EXIT1
; Выход.
EXIT1: RET
;
; Установка результата в ноль.
;
; Выполняется в следующих случаях:
; - Антипереполнение результата для любой операции.
; - Делимое равно нулю.
; - Хотя бы один сомножитель равен нулю.
; - Оба слагаемых равны нулю.
; - Результат вычитания равен нулю.
SETZERO1: CLR MANTA0
CLR MANTA1
CLR MANTA2
CLR EXPA0
RJMP EXIT1
;
; Усекает число в формате плавающей точки до целого.
;
; Работает только с положительными нормализованными десятичными числами в отрезке [1,10).