-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcalc.S
2199 lines (1845 loc) · 70.8 KB
/
calc.S
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
; ****************************************************************************
;
; Calculator macro interpreter
;
; ****************************************************************************
#include "include.inc"
.text
; ----------------------------------------------------------------------------
; Shift random generator
; ----------------------------------------------------------------------------
; OUTPUT: R25:R24:R23:R22 = new seed
; R1 = 0
; DESTROYS: R31, R30, R27, R26, R21..R18, R0
; ----------------------------------------------------------------------------
; RandSeed = RandSeed * 214013 + 2531011;
.global RandShift
RandShift:
; ----- load seed -> R25:R24:R23:R22
ldd r22,Y+DATA_RANDSEED+0
ldd r23,Y+DATA_RANDSEED+1
ldd r24,Y+DATA_RANDSEED+2
ldd r25,Y+DATA_RANDSEED+3
; ----- prepare multiplier 214013 = 0x000343FD -> R21:R20:R19:R18
ldi r18,0xfd
ldi r19,0x43
ldi r20,0x03
ldi r21,0x00
; ----- multiply
; INPUT: R25:R24:R23:R22 = 1st multiplier (N3:N2:N1:N0)
; R21:R20:R19:R18 = 2nd multiplier (N7:N6:N5:N4)
; OUTPUT: R25:R24:R23:R22 = result (M3:M2:M1:M0)
; R1=0
; DESTROYS: R31, R30, R27, R26, R0
rcall MulDD
; ----- add 2531011 = 0x00269EC3, neg -2531011 = 0xFFD9613D
subi r22,0x3d
sbci r23,0x61
sbci r24,0xd9
sbci r25,0xff
; ----- save seed
std Y+DATA_RANDSEED+0,r22
std Y+DATA_RANDSEED+1,r23
std Y+DATA_RANDSEED+2,r24
std Y+DATA_RANDSEED+3,r25
ret
; ----------------------------------------------------------------------------
; Random number (0 inc. .. 1 exc., C_RAND)
; ----------------------------------------------------------------------------
; CALCULATOR STACK: +1
; ----------------------------------------------------------------------------
.global CalcRand
CalcRand:
; ----- shift random generator, get random number
; OUTPUT: R25:R24:R23:R22 = new seed
; R1 = 0
; DESTROYS: R31, R30, R27, R26, R21..R18, R0
rcall RandShift
; ----- prepare number (R_M1..R_M10 = R23..R14)
clr R_M10
clr R_M9
movw R_M8,R_M10
movw R_M6,r22
movw R_M4,r24
ldi R_M2,lo8(EXP_BIAS-1) ; exponent 0.xxxx
ldi R_M1,hi8(EXP_BIAS-1)
; ----- normalize number
ldi r24,33 ; max. number of shifts
2: sbrc R_M3,7 ; check highest bit of mantissa
rjmp 4f ; highest bit is on the position, number is normalized
clc ; clear carry
; INPUT: R_M3..R_M10 mantissa
; C = input carry
; OUTPUT: R_M3..R_M10 mantissa shifted left
; C = output carry
; DESTROYS: -
rcall CalcMantRol ; rotate mantissa left (with carry)
subi R_M2,1 ; decrement exponent LOW
sbc R_M1,R_ZERO
dec r24 ; bit counter
brne 2b
; underflow, number is zero
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
rcall CalcClearNum ; clear result
; ----- clear hidden bit
4: andi R_M3,0x7f
; ----- create new number on top of stack
; OUTPUT: R31:R30 (Z) = new number
; DESTROYS: -
; CALCULATOR STACK: +1
rcall CalcNew ; create new number -> Z
; ----- save number
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum
; ----------------------------------------------------------------------------
; Square x^2 (C_SQR)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R27..R4
; ----------------------------------------------------------------------------
.global CalcSqr
CalcSqr:
; DESTROYS: R31, R30, R27..R24
; CALCULATOR STACK: +1
rcall CalcDup ; duplicate
; DESTROYS: all
; CALCULATOR STACK: -1
rjmp CalcMul ; multiplication
; ----------------------------------------------------------------------------
; Reciprocal value 1/x (C_REC)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R27..R4
; ----------------------------------------------------------------------------
.global CalcRec
CalcRec:
ldi r24,CONST_1
; INPUT: R24 = index of the constant in ConstTab
; OUTPUT: R1 = 0
; DESTROYS: R31, R30, R27..R24, R0
; CALCULATOR STACK: +1
rcall CalcConst ; load constant "1"
; DESTROYS: R31, R30, R27..R23
rcall CalcExc ; exchange operands
; DESTROYS: all
; CALCULATOR STACK: -1
rjmp CalcDiv ; divide
; ----------------------------------------------------------------------------
; Increment +1 (C_INC)
; ----------------------------------------------------------------------------
; DESTROYS: all
; ----------------------------------------------------------------------------
.global CalcInc
CalcInc:
ldi r24,CONST_1
; INPUT: R24 = index of the constant in ConstTab
; OUTPUT: R1 = 0
; DESTROYS: R31, R30, R27..R24, R0
; CALCULATOR STACK: +1
rcall CalcConst ; load constant "1"
; DESTROYS: all
; CALCULATOR STACK: -1
rjmp CalcAdd ; add 1
; ----------------------------------------------------------------------------
; Decrement -1 (C_DEC)
; ----------------------------------------------------------------------------
; DESTROYS: all
; ----------------------------------------------------------------------------
.global CalcDec
CalcDec:
ldi r24,CONST_M1
; INPUT: R24 = index of the constant in ConstTab
; OUTPUT: R1 = 0
; DESTROYS: R31, R30, R27..R24, R0
; CALCULATOR STACK: +1
rcall CalcConst ; load constant "-1"
; DESTROYS: all
; CALCULATOR STACK: -1
rjmp CalcAdd ; sub 1
; ----------------------------------------------------------------------------
; Compare (C_LTEQ..C_EQU)
; ----------------------------------------------------------------------------
; INPUT: R24 = literal
; DESTROYS: R31, R30, R27..R10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; Leaves 0/1 on top of calculator stack.
.global CalcCmp
CalcCmp:
; ----- get last 2 numbers -> 1st number X, 2nd number Z
; OUTPUT: R27:R26 (X) = pre-last number on calculator stack
; R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop2
; ----- prepare comparison code -> R24
; <= 000, >= 001, <> 010
; > 100, < 101, = 110
subi r24,C_LTEQ
; ----- swap operands in cases >= and <
; <= 00, <> 01
; > 10, = 11
lsr r24 ; shift right
; <= xx00, <> xx01
; > xx10, = xx11
brcc 1f ; skip if bit 0 is clear
; DESTROYS: R31, R30, R27..R23
push r24 ; push comparison flags
; DESTROYS: R31, R30, R27..R23
rcall CalcExc ; swap two numbers on the stack
pop r24 ; pop comparison flags
; ----- substract numbers (result leaves on top of stack)
; top number: 0 if numbers are equal
1: push r24
; DESTROYS: all
; CALCULATOR STACK: -1
rcall CalcSub
pop r24 ; pop comparison flags
; ----- process result
; top number = 1st number - 2nd number
; <= 00, <> 01, > 10, = 11
; DESTROYS: R31, R30, R25, R24
push r24 ; push comparison flags
sbrc r24,0 ; skip if bit 0 is clear (= equal flag)
rcall CalcNot ; invert result number if bit 0 is set
; top number = 1st number - 2nd number = dif
; dif = 0 (numbers are equal): <= 00 (->0), <> 01 (->1), > 10 (->0), = 11 (->1)
; dif <> 0: <= 00 (dif), <> 01 (->0), > 10 (dif), = 11 (->0)
; DESTROYS: R31, R30, R27..R23, R1, R0
rcall CalcGr0 ; greater-0, sets 1 if number was > 0, or 0 otherwise
; dif > 0: <= 00 (->1), <> 01 (->0), > 10 (->1), = 11 (->0)
; dif < 0: <= 00 (->0), <> 01 (->0), > 10 (->0), = 11 (->0)
pop r24 ; pop comparison flags
; ----- apply a terminal NOT by bit 2 of comparison flag - inverts meaning of <= and <>
; DESTROYS: R31, R30, R25, R24
sbrs r24,1 ; skip if bit 1 (old bit 2) is set
rcall CalcNot ; NOT result if bit 1 is clear
; dif = 0: <= 00 (->1), <> 01 (->0), > 10 (->0), = 11 (->1)
; dif > 0: <= 00 (->0), <> 01 (->1), > 10 (->1), = 11 (->0)
; dif < 0: <= 00 (->1), <> 01 (->1), > 10 (->0), = 11 (->0)
CalcCmp9:
ret
; ----------------------------------------------------------------------------
; Check if number if less than zero (C_LT0)
; ----------------------------------------------------------------------------
; Set number to 1 if number is < 0 (not equ), or 0 otherwise.
.global CalcLt0
CalcLt0:
; ----- get last number on calculator stack -> Z
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop
; ----- prepare mask for check
ldi r24,0 ; XOR mask
; ----- check sign bit
CalcLt02: ; jump here from CalcGr0
ldd r25,Z+2 ; get sign bit (from first byte of mantissa)
eor r25,r24 ; check sign bit
; ----- set result
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
brmi CalcZ1 ; set to 1 in case of negative number (or positive from CalcGr0)
; CalcZ0 must follow
; ----------------------------------------------------------------------------
; Set number in Z to value 0
; ----------------------------------------------------------------------------
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
; ----------------------------------------------------------------------------
.global CalcZ0
CalcZ0:
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
rcall CalcClearNum ; clear number R_M1..R_M10
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum ; save number into Z
; ----------------------------------------------------------------------------
; Set number in Z to value 1
; ----------------------------------------------------------------------------
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
; ----------------------------------------------------------------------------
.global CalcZ1
CalcZ1:
; OUTPUT: R_M1..R_M10 number (= 0)
; DESTROYS: -
rcall CalcClearNum ; clear number R_M1..R_M10
ldi R_M1,hi8(EXP_BIAS)
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum ; save number into Z
; ----------------------------------------------------------------------------
; Set number in Z to max. value
; ----------------------------------------------------------------------------
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
; ----------------------------------------------------------------------------
.global CalcZMax
CalcZMax:
ldi R_M2,lo8(EXP_MAX)
CalcZMax2:
ldi R_M1,hi8(EXP_MAX)
ldi R_M3,0x7f
ldi R_M4,0xff
ldi R_M5,0xff
ldi R_M6,0xff
movw R_M8,R_M6
movw R_M10,R_M6
; INPUT: R31:R30 (Z) = pointer to number
; R_M1..R_M10 number
; DESTROYS: -
rjmp CalcSaveNum ; save number
; ----------------------------------------------------------------------------
; Set number in Z to overflow value
; ----------------------------------------------------------------------------
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
; ----------------------------------------------------------------------------
.global CalcZOver
CalcZOver:
ldi R_M2,lo8(EXP_OVER)
rjmp CalcZMax2
; ----------------------------------------------------------------------------
; Check if number if greater than zero (C_GR0)
; ----------------------------------------------------------------------------
; Set number to 1 if number is > 0 (not equ), or 0 otherwise.
.global CalcGr0
CalcGr0:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
breq CalcCmp9 ; zero number left unchanged
; ----- prepare mask for check
ldi r24,0xff ; negative mask
rjmp CalcLt02
; ----------------------------------------------------------------------------
; Check if number if less or equ zero (C_LTEQ0)
; ----------------------------------------------------------------------------
; Set number to 1 if number is <= 0, or 0 otherwise.
.global CalcLtEq0
CalcLtEq0:
rcall CalcGr0 ; set to 1 if number is > 0 (not equ), or 0 otherwise
rjmp CalcNot ; inverse result
; ----------------------------------------------------------------------------
; Check if number if greater or equ zero (C_GREQ0)
; ----------------------------------------------------------------------------
; Set number to 1 if number is >= 0, or 0 otherwise.
.global CalcGrEq0
CalcGrEq0:
rcall CalcLt0 ; set to 1 if number is < 0 (not equ), or 0 otherwise
; CalcNot must follow
; ----------------------------------------------------------------------------
; NOT operator (C_NOT)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24, R_M1,...R_M10
; ----------------------------------------------------------------------------
; Returns value 0 or 1.
.global CalcNot
CalcNot:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
; ----- set result
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
breq CalcZ1 ; set to 1 in case of zero
rjmp CalcZ0 ; set to 0 in case of not zero
; ----------------------------------------------------------------------------
; OR operator (C_OR)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; Result is zero, if both values are zero; else a non-zero value.
; e.g. 0 OR 0 returns 0.
; -3 OR 0 returns -3.
; 0 OR -3 returns 1.
; -3 OR 2 returns 1.
.global CalcOr
CalcOr:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
; ----- destroy last number (saves SREG)
rcall CalcDel2 ; set new end of stack (saves SREG)
; ----- if second number is zero, first operand will stay unchanged
breq CalcCmp9 ; second number is zero -> first number will contain result (0 or nonzero number)
; ----- second number is not zero, result will be TRUE - set number in Z to +1
; OUTPUT: R31:R30 (Z) = last number on calculator stack (=STKEND-NUM_BYTES)
; DESTROYS: R24, R_M1..R_M10
; CalcTopZ1 must follow
; ----------------------------------------------------------------------------
; Set number on top of the stack to 1
; ----------------------------------------------------------------------------
; OUTPUT: R31:R30 (Z) = last number on calculator stack (=STKEND-NUM_BYTES)
; DESTROYS: R24, R_M1..R_M10
; ----------------------------------------------------------------------------
.global CalcTopZ1
CalcTopZ1:
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop ; get last number -> Z
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
rjmp CalcZ1 ; set new last number to 1
; ----------------------------------------------------------------------------
; AND operator (C_AND)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24, R_M1,...R_M10
; CALCULATOR STACK: -1
; ----------------------------------------------------------------------------
; Result is zero, if any of the values is zero; else a non-zero value.
; e.g. -3 AND 2 returns -3.
; -3 AND 0 returns 0.
; 0 and -2 returns 0.
; 0 and 0 returns 0.
.global CalcAnd
CalcAnd:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
; ----- destroy last number (saves SREG)
rcall CalcDel2 ; set new end of stack (saves SREG)
; ----- if second number is not zero, first operand will stay unchanged
brne CalcCmp9 ; second number is not zero -> first number will containg result
; ----- second number is zero, result will be FALSE - set number in Z to 0
; CalcTopZ0 must follow
; ----------------------------------------------------------------------------
; Set number on top of the stack to 0
; ----------------------------------------------------------------------------
; OUTPUT: R31:R30 (Z) = last number on calculator stack (=STKEND-NUM_BYTES)
; DESTROYS: R_M1,...R_M10
; ----------------------------------------------------------------------------
.global CalcTopZ0
CalcTopZ0:
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop ; get last number -> Z
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
rjmp CalcZ0 ; set number Z to 0
; ----------------------------------------------------------------------------
; Signum (C_SGN)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24, R_M1..R_M10
; ----------------------------------------------------------------------------
; Replace number on top of calculator stack by value -1, 0, 1.
.global CalcSgn
CalcSgn:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
breq CalcSgn9 ; zero number left unchanged
; ----- load sign (bit 7 in 1st byte of mantissa)
ldd r25,Z+2 ; R25 <- first byte of mantissa
; ----- set number in Z to +1
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
rcall CalcZ1
; ----- save sign of the number back to mantissa
andi r25,0x80 ; mask sign bit (= number +1 or -1)
CalcSgn8:
std Z+2,r25 ; set sign back to mantissa
CalcSgn9:
ret
; ----------------------------------------------------------------------------
; Unary minus, Negate (C_NEG)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24
; ----------------------------------------------------------------------------
.global CalcNeg
CalcNeg:
; ----- get last number and check if zero -> Z, R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
breq CalcSgn9 ; zero number left unchanged
; ----- load sign (bit 7 in 1st byte of mantissa)
ldd r25,Z+2 ; R25 <- first byte of mantissa
subi r25,0x80 ; flip sign
rjmp CalcSgn8 ; set new sign
; ----------------------------------------------------------------------------
; Absolute value (C_ABS)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25
; ----------------------------------------------------------------------------
.global CalcAbs
CalcAbs:
; ----- get last number on calculator stack -> Z
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop
; ----- load sign (bit 7 in 1st byte of mantissa)
ldd r25,Z+2 ; R25 <- first byte of mantissa
andi r25,0x7f ; reset sign bit
rjmp CalcSgn8 ; set new sign
; ----------------------------------------------------------------------------
; Integer precise truncation towards zero (C_TRUNCPREC)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R27..R23, R_M1,...R_M10
; ----------------------------------------------------------------------------
; - no rounding epsilon delta, simple clears fractional bits
.global CalcTruncPrec
CalcTruncPrec:
; ----- get last number and check if zero -> Z, R25:R24
; OUTPUT: R31:R30 (Z) = last number on calculator stack
; R25:R24 = exponent (0 = number is zero, 0xFFFF = overflow)
; ZY = number is 0
; CY = number is overflow
; DESTROYS: -
rcall CalcTopCheck
subi r24,lo8(EXP_BIAS) ; check bias
sbci r25,hi8(EXP_BIAS)
brcc CalcTruncPrec2 ; 1 or greater
; ----- number is smaller than +-1, can be made zero
; INPUT: R31:R30 = float number
; DESTROYS: R_M1,...R_M10
rjmp CalcZ0 ; clear number to 0
; ----- prepare number of bits to clear
CalcTruncPrec2:
tst r25 ; check exponent HIGH
brne CalcTruncPrec9 ; number is too high, always integer (no fractional part)
subi r24,MANT_BITS-1 ; subtract max. bits from exponent
brcc CalcTruncPrec9 ; number is too high, always integer (no fractional part)
neg r24 ; number of bits = MANT_BITS-1..1
; ----- prepare number of bytes to clear
CalcTruncPrec3:
adiw r30,NUM_BYTES ; shift pointer behind end of number
mov r22,r24 ; R22 <- number of bits
lsr r22
lsr r22
lsr r22 ; number of bits / 8 = number of bytes
breq CalcTruncPrec6 ; no whole byte
; ----- reset whole bytes to 0
CalcTruncPrec4:
st -Z,R_ZERO ; set 8 bits to 0
dec r22 ; byte counter
brne CalcTruncPrec4 ; next byte
; ----- reset remaining bits
CalcTruncPrec6:
andi r24,7 ; remaining bits
breq CalcTruncPrec9 ; no bits
ldi r22,0xff ; mask
CalcTruncPrec8:
lsl r22
dec r24
brne CalcTruncPrec8 ; prepare mask
ld r24,-Z
and r24,r22 ; mask bits
st Z,r24
CalcTruncPrec9:
ret
; ----------------------------------------------------------------------------
; Round nearest (C_ROUND)
; ----------------------------------------------------------------------------
.global CalcRound
CalcRound:
rcall Calc
; offset 0
.byte C_CONST(CONST_05) ; load 0.5 constant (x,0.5)
.byte C_DUP2 ; pre-duplicate (x,0.5,x)
.byte C_GR0 ; greater 0 (x,0.5,1/0)
.byte C_JUMPT ; jump if true, jump if > 0 (x,0.5)
.byte 1 ; jump to offset 6 (6-5=1)
; offset 5
.byte C_NEG ; negate if < 0 (x,-0.5)
; offset 6
.byte C_ADD ; add rounding correction
.byte C_PRECOR ; add pre-correction
.byte C_TRUNCPREC ; precise truncation
.byte C_END ; end
.balign 2 ; align
ret
; ----------------------------------------------------------------------------
; Round nearest fraction -0.5..+0.5 (C_ROUNDFRAC)
; ----------------------------------------------------------------------------
.global CalcRoundFrac
CalcRoundFrac:
rcall Calc
.byte C_DUP ; duplicate (x,x)
.byte C_ROUND ; round nearest (x,round(x))
.byte C_SUB ; difference -0.5..+0.5 (x-round(x))
.byte C_END ; end
.balign 2 ; align
ret
; ----------------------------------------------------------------------------
; Integer truncation towards zero (C_TRUNC)
; ----------------------------------------------------------------------------
.global CalcTrunc
CalcTrunc:
rcall Calc
; offset 0: calculate round nearest and its difference
.byte C_DUP ; duplicate (x,x)
.byte C_ROUND ; round nearest (x,r) ... r=round(x)
.byte C_DUP2 ; pre-duplicate (x,r,x)
.byte C_DUP2 ; pre-duplicate (x,r,x,r)
.byte C_SUB ; subtract (x,r,d) ... d=x-round(x)
; offset 5: check if number was negative
.byte C_EXC ; exchange (x,d,r)
.byte C_EXC2 ; exchange (r,d,x)
.byte C_LT0 ; less 0 (r,d,0/1)
.byte C_JUMPT ; jump if true, jump if x < 0 (r,d)
.byte 6 ; jump to offset 16 (16-10=6)
; offset 10: number is positive
.byte C_LT0 ; difference is less 0 (r,0/1)
.byte C_JUMPF ; jump if false, jump if difference not < 0 (r)
.byte 7 ; jump to offset 20 (20-13=7)
; offset 13: decrement if difference < 0
.byte C_DEC ; decrement (r-1)
.byte C_JMP ; jump to end
.byte 4 ; jump to offset 20 (20-16=4)
; offset 16: number is negative
.byte C_GR0 ; greater 0 (r,0/1)
.byte C_JUMPF ; jump if false, jump if difference not > 0 (r)
.byte 1 ; jump to offset 20 (20-19=1)
; offset 19: increment if difference > 0
.byte C_INC ; increment
; offset 20: end
.byte C_END ; end
; offset 21:
.balign 2 ; align
; offset 22:
ret
; ----------------------------------------------------------------------------
; Round down (C_FLOOR)
; ----------------------------------------------------------------------------
.global CalcFloor
CalcFloor:
rcall Calc
; offset 0: calculate round nearest and its difference
.byte C_DUP ; duplicate (x,x)
.byte C_ROUND ; round nearest (x,r) ... r=round(x)
.byte C_EXC ; exchange (r,x)
.byte C_DUP2 ; duplicate (r,x,r)
.byte C_SUB ; subtract (r,x-r)
.byte C_LT0 ; less 0 (r,0/1)
.byte C_JUMPF ; jump if false, jump if difference not < 0 (r)
.byte 1 ; jump to offset 9 (9-8=1)
; offset 8: decrement if difference < 0
.byte C_DEC ; decrement
; offset 9: end
.byte C_END ; end
; offset 10:
.balign 2 ; align
; offset 10:
ret
; ----------------------------------------------------------------------------
; Round up (C_CEIL)
; ----------------------------------------------------------------------------
.global CalcCeil
CalcCeil:
rcall Calc
; offset 0: calculate round nearest and its difference
.byte C_DUP ; duplicate (x,x)
.byte C_ROUND ; round nearest (x,r) ... r=round(x)
.byte C_EXC ; exchange (r,x)
.byte C_DUP2 ; duplicate (r,x,r)
.byte C_SUB ; subtract (r,x-r)
.byte C_GR0 ; greater 0 (r,0/1)
.byte C_JUMPF ; jump if false, jump if difference not > 0 (r)
.byte 1 ; jump to offset 9 (9-8=1)
; offset 8: increment if difference > 0
.byte C_INC ; increment
; offset 9: end
.byte C_END ; end
; offset 10:
.balign 2 ; align
; offset 10:
ret
; ----------------------------------------------------------------------------
; Normalize angle argument (C_ARG)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R27..R23, R1, R0
; ----------------------------------------------------------------------------
; Result lies in range 0..2*PI
.global CalcArg
CalcArg:
rcall Calc ; (x)
.byte C_CONST(CONST_PI2) ; add 2*PI constant
.byte C_MOD2 ; modulus floor
.byte C_END ; end
.balign 2 ; align
ret
; ----------------------------------------------------------------------------
; Add serie member to accumulator and loop if meaningful (C_ADDLOOP)
; ----------------------------------------------------------------------------
; INPUT: R_LITH:R_LITL = pointer to literals
; R_ZERO = 0
; OUTPUT: R_LITH:R_LITL = new pointer to literals
; DESTROYS: R31, R30, R27..R22
; ----------------------------------------------------------------------------
.global CalcAddLoop
CalcAddLoop:
; ----- get 2 top numbers
; OUTPUT: R27:R26 (X) = pre-last number on calculator stack
; R31:R30 (Z) = last number on calculator stack
; DESTROYS: -
rcall CalcTop2
; ----- 1st number (accumulator) is infinity - stop iteration
ld r23,X+ ; exponent HIGH
ld r22,X ; exponent LOW
ldi r24,hi8(EXP_OVER)
cpi r22,lo8(EXP_OVER)
cpc r23,r24
breq CalcAddLoop6 ; infinity
; ----- 2nd number (serie member) is zero or infinity - stop iteration
ldd r25,Z+0 ; exponent HIGH
ldd r24,Z+1 ; exponent LOW
adiw r24,1
sbiw r24,1
breq CalcAddLoop6 ; 2nd number is zero - end iteration
brcs CalcAddLoop6 ; infinity
; ----- compare exponents
sub r22,r24
sbc r23,r25 ; difference of exponents
brcs CalcAddLoop4 ; member is greater than accumulator - continue
cpi r22,MANT_BITS + MANT_BITS/16 ; check distance
cpc r23,R_ZERO
brcc CalcAddLoop6 ; member is too small, break
; ----- add numbers
CalcAddLoop4:
; DESTROYS: all
; CALCULATOR STACK: -1
rcall CalcAdd ; add numbers
; ----- jump to loop begin
; INPUT: R_LITH:R_LITL = pointer to literals
; OUTPUT: R_LITH:R_LITL = new pointer to literals
; DESTROYS: R25, R24
; NOTES: Jump offset is relative to next byte after JUMP intruction
rjmp CalcJump
; ----- stop loop - only destroy literal with jump offset
CalcAddLoop6:
; DESTROYS: R31, R30
; CALCULATOR STACK: -1
rcall CalcDel ; delete member
; INPUT: R_LITH:R_LITL = pointer to literals
; OUTPUT: R_LITH:R_LITL = new pointer to literals
; R24 = next literal
; DESTROYS: -
rjmp CalcLit ; load literal -> R24
; ----------------------------------------------------------------------------
; Cosine (C_COS)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24
; USES: TEMP_1, TEMP_2, TEMP_3, TEMP_4
; ----------------------------------------------------------------------------
; cos(x) = sin(x+PI/2)
.global CalcCos
CalcCos:
; ----- add angle PI/2
rcall Calc ; calculator
.byte C_CONST(CONST_PI05) ; load constant PI/2
.byte C_ADD ; add PI/2 to angle
.byte C_END ; end
.balign 2 ; align
; CalcSin must follow
; ----------------------------------------------------------------------------
; Sine (C_SIN)
; ----------------------------------------------------------------------------
; DESTROYS: R31, R30, R25, R24
; USES: TEMP_1, TEMP_2, TEMP_3, TEMP_4
; ----------------------------------------------------------------------------
; Taylor serie xi+1 = xi * -x^2/((2*i)*(2*i+1)) ... x0 = x
; sin(x) = x/1! - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ...
.global CalcSin
CalcSin:
rcall Calc ; calculator
; offset 0: normalize angle into range 0..PI*2
.byte C_ARG ; normalize angle (x) (uses TEMP_1, TEMP_2)
; offset 1: normalize angle into range 0..PI (if x >= PI then x = 2*PI - x)
.byte C_DUP ; duplicate (x,x)
.byte C_CONST(CONST_PI) ; load constant PI (x,x,PI)
.byte C_LT ; check if x < PI (x,1/0)
.byte C_SETMEM(TEMP_1) ; save 1/0 into temp 1 (0=result will be negative)
.byte C_JUMPT ; skip if x < PI (x)
.byte 3 ; jump to offset 10 (10-7=3)
; offset 7:
.byte C_CONST(CONST_PI2) ; load constant PI*2 (x,PI*2)
.byte C_EXC ; exchange (PI*2,x)
.byte C_SUB ; subtract (PI*2-x)
; offset 10: number is zero, result will be zero
.byte C_JUMPZ ; skip if x = 0 (x)
.byte 39 ; jump to offset 51 (51-12=39)
; offset 12: number is PI, result will be zero
.byte C_DUP ; duplicate (x,x)
.byte C_CONST(CONST_PI) ; load constant PI (x,x,PI)
.byte C_SUB ; subtract (x,x-PI)
.byte C_JUMPF ; jump if x = PI, result will be 0
.byte 34 ; jump to offset 51 (51-17=34)
; offset 17: prepare square -x^2 into TEMP_2
.byte C_DUP ; duplicate (x,x)
.byte C_SQR ; square (x, x^2)
.byte C_NEG ; negate (x, -x^2)
.byte C_SETMEMDEL(TEMP_2) ; save -x^2 into TEMP_2
; offset 21: prepare factorial coefficient 1 into TEMP_3
.byte C_CONST(CONST_1) ; load constant 1 (x,1)
.byte C_SETMEMDEL(TEMP_3) ; save 1 into TEMP_3