-
Notifications
You must be signed in to change notification settings - Fork 1
/
nv_math16_macs.asm
1053 lines (980 loc) · 37.6 KB
/
nv_math16_macs.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
//////////////////////////////////////////////////////////////////////////////
// nv_math16_macs.asm
// Copyright(c) 2021 Neal Smith.
// License: MIT. See LICENSE file in root directory.
//////////////////////////////////////////////////////////////////////////////
// Contains macros for 16 bit math operations
// importing this will not cause code or data to be allocated in the program
// unless nv_c64_util_data hasn't already been imported in which case it
// will be.
#importonce
#if !NV_C64_UTIL_DATA
.error "Error - nv_math16_macs.asm: NV_C64_UTIL_DATA not defined. Import nv_c64_util_data.asm"
#endif
// the #if above doesn't seem to always work so..
// if data hasn't been imported yet, import it into default location
#importif !NV_C64_UTIL_DATA "nv_c64_util_default_data.asm"
#import "nv_branch16_macs.asm"
#import "nv_processor_status_macs.asm"
//////////////////////////////////////////////////////////////////////////////
// inline macro to add two 16 bit values and store the result in another
// 16bit value. Carry and overflow bits set appropriately
// full name: nv_adc16x_mem16x_mem16x
// params:
// addr1 is the address of the low byte of op1
// addr2 is the address of the low byte of op2
// result_addr is the address to store the result.
// Accum changes
// X Reg unchanged
// Y Reg unchanged
// Status flags:
// Carry set if carry from the MSB addition occured, ie if unsigned result
// would exceed 16 bit unsigned max (65535).
// Carry clear if no carry from MSB occured, ie if the unsigned result
// does fit in a 16 bit unsigned int (0 - 65535)
// Overflow set: if signed result outside bound of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int
.macro nv_adc16x(addr1, addr2, result_addr)
{
lda addr1
clc
adc addr2
sta result_addr
lda addr1+1
adc addr2+1
sta result_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add a signed 8 bit value in memory to a 16 bit value
// in memory and store the result in another 16bit value.
// full name: nv_adc16x_mem16x_mem8s
// params:
// addr16 is the address of the low byte of 16 bit operand.
// whether signed or unsigned the result will be the same
// addr8 is the address of the signed 8 bit operand. As an 8 bit
// signed number, if the sign bit is 1 then it will be
// extended to create a 16bit value that will be added to the
// value at addr16. The created 16 bit value will have all 8 high
// bits set to match the sign bit from the original 8 bit value.
// For example, if the 8 bit value at addr8 is $FF (-1) then
// instead of adding $00FF to the 16 bit number we'll be adding
// $FFFF which is -1 so that the result will be as expected.
// result16_addr is the address to store the 16bit result.
// Accum changes
// X Reg changes
// Y Reg unchanged
// Status flags:
// Carry set if carry from the MSB addition occured, ie if unsigned result
// after sign extending addr8 would exceed 16 bit unsigned
// max (65535).
// Carry clear if no carry from MSB occured, ie if the unsigned result
// after sign extending addr8 does fit in a 16 bit
// unsigned int (0 - 65535)
// Overflow set: if signed result outside bound of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int
.macro nv_adc16x_mem16x_mem8s(addr16, addr8, result16_addr)
{
ldx #0
lda addr8
bpl Op2Positive
ldx #$ff
Op2Positive:
stx scratch_byte
clc
adc addr16
sta result16_addr
lda addr16+1
adc scratch_byte
sta result16_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add an unsigned 8 bit value in memory to a 16 bit value
// (signed or unsigned) in memory and store 16bit result in memory.
// full name: nv_adc16x_mem16x_mem8u
// params:
// addr16 is the address of the LSB of 16 bit operand
// addr8 is the address of the unsigned 8 bit operand (0-255). Since its
// unsigned, when the value is $FF, the result won't be to
// adding a negative 1 but will be adding 255 to the 16 bit value.
// result16_addr is the address to store the result.
// Accum changes
// X Reg unchanged
// Y Reg unchanged
// Status flags:
// Carry set if carry from the MSB addition occured, ie if unsigned result
// after sign extending addr8 would exceed 16 bit unsigned
// max (65535).
// Carry clear if no carry from MSB addition occured, ie if unsigned result
// does fit in a 16 bit unsigned int (0 - 65535)
// Overflow set: if signed result outside bounds of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int (-32768 and +32767)
.macro nv_adc16x_mem16x_mem8u(addr16, addr8, result16_addr)
{
lda addr16
clc
adc addr8
sta result16_addr
lda addr16+1
bcc SkipAddition
adc #0
SkipAddition:
sta result16_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add the accum (as an unsigned byte) to a 16 bit
// word in memory and store the result to a 16 bit word in memory.
// result16_addr = addr16 + accum
// full name: nv_adc16x_mem16x_a8u
// params:
// addr16: is the address of the LSB of 16 bit operand
// accum: contains the value to add to addr16 Since this is an
// unsigned operation, when the value is $FF, the result won't be to
// adding a negative 1 but will be adding 255 to the 16 bit value.
// result16_addr is the address to store the result.
// accum: changes
// x reg: unchanged
// y reg: unchanged
// Status flags:
// Carry set if carry from the MSB addition occured, ie if unsigned result
// after sign extending addr8 would exceed 16 bit unsigned
// max (65535).
// Carry clear if no carry from MSB addition occured, ie if unsigned result
// does fit in a 16 bit unsigned int (0 - 65535)
// Overflow set: if signed result outside bounds of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int (-32768 and +32767)
// old name: nv_adc16_a_unsigned
.macro nv_adc16x_mem16x_a8u(addr16, result16_addr)
{
clc
adc addr16 // add LSB of addr16 with accum
sta result16_addr // above addition is LSB of result
lda addr16+1 // load MSB of addr16 to update
bcc SkipAdd // carry is clear, we are done MSB is unchanged
adc #0 // add 0, carry will be set if appropriate
SkipAdd:
sta result16_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add one 16 bit values in memory to an immediate value
// and store the result in another 16bit value.
// result_addr = addr1 + num
// full name: nv_adc16x_mem16x_immed16x
// params:
// addr1 is the address of the LSB of 16 bit value in memory
// num is the 16 bit immeidate number to add
// result_addr is the address of the LSB of the 16 bit memory location
// to store the result.
// Accum changes
// X Reg unchanged
// Y Reg unchanged
// Status flags:
// Carry set if carry from the MSB addition occured, ie if unsigned result
// would exceed 16 bit unsigned max (65535).
// Carry clear if no carry from MSB occured, ie if the unsigned result
// does fit in a 16 bit unsigned int (0 - 65535)
// Overflow set: if signed result outside bound of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int
.macro nv_adc16x_mem_immed(addr1, num, result_addr)
{
lda addr1
clc
adc #(num & $00FF)
sta result_addr
lda addr1+1
adc #((num >> 8) & $00FF)
sta result_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to increment a 16bit value in memory
// macro params:
// addr16: the address of the LSB of a 16bit value in memory to increment
// after the increment operation this value will be incremented.
// if the value was $FFFF it will be $0000 after the operation.
// flags:
// Zero flag will be set if the result of the inc is $0000
// Negative flag will not be set reliably
.macro nv_inc16x_mem16x(addr16)
{
inc addr16
bne Done // if the result was zero then need to carry to MSB
inc addr16+1 // inc the MSB
Done:
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to multiply one unsigned 16 bit value by an 8 bit immed value
// and store the result in another 16bit value.
// result16_addr = addr1 * num8
// full name: nv_mul16u_mem16u_immed8u
// params:
// addr1 is the address of the LSB of 16 bit value in memory
// num is the immeidate 8 bit number to multiply addr1 by
// result16_addr is the address of the LSB of the 16 bit memory location
// to store the result.
// proc_flags is a bit mask specifying which processor status
// flags will be set. see nv_mul16_x for more info.
// Accum: changes
// X Reg: changes
// Y Reg: unchanged.
.macro nv_mul16u_mem16u_immed8u(addr1, num8, result16_addr, proc_flags)
{
.if (num8 > 255)
{
.error "ERROR - nv_mul16_immed8: num8 too large"
}
ldx #num8
nv_mul16u_mem16u_x8u(addr1, result16_addr, proc_flags)
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to multiply one unsigned16 bit value by an 8 bit unsigned
// value in x reg and store result in a 16bit word in memory.
// This is an unsigned multiplication. For example if an operand
// is $FF its multiplying by 255 not by -1
// full name: nv_mul16u_mem16u_x8u
// Can optionally set overflow and/or zero processor status flags
// macro params:
// addr1 is the address of the LSB of 16 bit value in memory
// result16_addr is the address of the LSB of the 16 bit memory location
// to store the result.
// proc_flags set the bits in this 8 bit value to be
// one or more (ORed together) of the NV_PROCSTAT_XXX consts
// The following bits can be checked, and if they
// are then the corresponding flag will be set if appropriate
// NV_PROCSTAT_CARRY: pass value with this bit set if
// you want carry flag to be set
// in the case that the result is too large
// to fit in an unsigned 16 bit value.
// If carry in status register
// is set after this executes that means
// the reslt only has the low 16 bits
// of the multiplication result and the
// rest is lost
// NV_PROCSTAT_ZERO: pass value with this bit set if you
// want the zero flag set in the case
// were multiplication result is zero.
// params:
// x reg should be set to the 8 bit number to multiply by prior to
// this macro
// Accum: changes
// X Reg: changes
// Y Reg: unchanged
// old name: nv_mul16_x
.macro nv_mul16u_mem16u_x8u(addr1, result16_addr, proc_flags)
{
//.if ((proc_flags & NV_PROCSTAT_OVERFLOW) != 0)
//{ // clear overflow flag
// clv
//}
.if (proc_flags != NV_PROCSTAT_NONE)
{ // if we care about any flag then push the flags on stack
// later we can them off and set appropriately.
//php // push on the stack the proc status flags
lda #0
sta scratch_byte
}
cpx #$00
beq MultByZero
lda addr1
beq MultByZero
nv_store16_immed(scratch_word, $0000)
LoopTop:
nv_adc16x(addr1, scratch_word, scratch_word)
.if ((proc_flags & NV_PROCSTAT_CARRY) !=0)
{ // user cares about overflow so check the carry flag
bcc NoCarry
// if there was a carry then we had an overflow
//pla // pull proc status from stack to accum
//ora #NV_PROCSTAT_OVERFLOW // set overflow flag
//pha // push updated proc status to stack
lda scratch_byte
ora #NV_PROCSTAT_CARRY
sta scratch_byte
NoCarry:
}
dex
bne LoopTop
nv_xfer16_mem_mem(scratch_word, result16_addr)
jmp Done
MultByZero:
nv_store16_immed(result16_addr, $0000)
.if ((proc_flags & NV_PROCSTAT_ZERO) != 0)
{
//pla // pull the flags from stack
//ora #NV_PROCSTAT_ZERO // set zero flag
//pha // push updated flags back to stack
lda scratch_byte
ora #NV_PROCSTAT_ZERO
sta scratch_byte
}
Done:
.if (proc_flags != NV_PROCSTAT_NONE)
{ // if we care about any flag update flags to set any flag set above
// which is stored in scratch_byte.
.if ((proc_flags & NV_PROCSTAT_CARRY) != 0)
{
clc // clear carry flag
}
.if ((proc_flags & NV_PROCSTAT_ZERO) != 0)
{
lda #1 // clear zero flag
}
php // push processor status register to stack
pla // pull processor status from stack to accum
ora scratch_byte // set any flags saved above
pha // push updated flags to the stack
plp // pull updated flags from stack to status reg
}
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to multiply one 16 bit value by an 8 bit value in y reg
// and store the result in another 16bit value. This is an unsigned
// multiplication. For example if an operand is $FF its multiplying
// by 255 not by -1
// full name: nv_mul16u_mem16u_y8u
// Can optionally set overflow and/or zero processor status flags
// macro params:
// addr1 is the address of the LSB of 16 bit value in memory
// result16_addr is the address of the LSB of the 16 bit memory location
// to store the result.
// proc_flags set the bits in this 8 bit value to be
// one or more (ORed together) of the NV_PROCSTAT_XXX consts
// The following bits can be checked, and if they
// are then the corresponding flag will be set if appropriate
// NV_PROCSTAT_CARRY: pass value with this bit set if
// you want carry flag to be set
// in the case that the result is too large
// to fit in an unsigned 16 bit value.
// If carry in status register
// is set after this executes that means
// the reslt only has the low 16 bits
// of the multiplication result and the
// rest is lost
// NV_PROCSTAT_ZERO: pass value with this bit set if you
// want the zero flag set in the case
// were multiplication result is zero.
// params:
// y reg should be set to the 8 bit number to multiply by prior to
// this macro
// Accum: changes
// X Reg: unchanged
// Y Reg: changes
// old name: nv_mul16_y
.macro nv_mul16u_mem16u_y8u(addr1, result16_addr, proc_flags)
{
//.if ((proc_flags & NV_PROCSTAT_OVERFLOW) != 0)
//{ // clear overflow flag
// clv
//}
.if (proc_flags != NV_PROCSTAT_NONE)
{ // if we care about any flag then push the flags on stack
// later we can them off and set appropriately.
//php // push on the stack the proc status flags
lda #0
sta scratch_byte
}
cpy #$00
beq MultByZero
lda addr1
beq MultByZero
nv_store16_immed(scratch_word, $0000)
LoopTop:
nv_adc16x(addr1, scratch_word, scratch_word)
.if ((proc_flags & NV_PROCSTAT_CARRY) !=0)
{ // user cares about overflow so check the carry flag
bcc NoCarry
// if there was a carry then we had an overflow
//pla // pull proc status from stack to accum
//ora #NV_PROCSTAT_OVERFLOW // set overflow flag
//pha // push updated proc status to stack
lda scratch_byte
ora #NV_PROCSTAT_CARRY
sta scratch_byte
NoCarry:
}
dey
bne LoopTop
nv_xfer16_mem_mem(scratch_word, result16_addr)
jmp Done
MultByZero:
nv_store16_immed(result16_addr, $0000)
.if ((proc_flags & NV_PROCSTAT_ZERO) != 0)
{
//pla // pull the flags from stack
//ora #NV_PROCSTAT_ZERO // set zero flag
//pha // push updated flags back to stack
lda scratch_byte
ora #NV_PROCSTAT_ZERO
sta scratch_byte
}
Done:
.if (proc_flags != NV_PROCSTAT_NONE)
{ // if we care about any flag update flags to set any flag set above
// which is stored in scratch_byte.
.if ((proc_flags & NV_PROCSTAT_CARRY) != 0)
{
clc // clear carry flag
}
.if ((proc_flags & NV_PROCSTAT_ZERO) != 0)
{
lda #1 // clear zero flag
}
php // push processor status register to stack
pla // pull processor status from stack to accum
ora scratch_byte // set any flags saved above
pha // push updated flags to the stack
plp // pull updated flags from stack to status reg
}
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// rotate bits right in a 16 bit location in memory
// addr = addr >> num
// full name: nv_lsr16u_mem16u_immed8u
// params:
// addr is the address of the lo byte and addr+1 is the MSB
// num is the number of rotations to do.
// zeros will be rotated in to the high bits
// status flags:
// Carry flag will be set if the last rotation rotated off a 1 from low bit
// Carry flag will be clear if the last rotation rotated off a 0 from low bit
// Use this to divide by 2 or any power of two.
// Accum: unchanged
// X Reg: unchanged
// Y Reg: changes
// status flags: flags change but not reliably set
.macro nv_lsr16u_mem16u_immed8u(addr, num)
{
.if (num > $FF)
{
.error "ERROR - nv_lsr16u_mem16u_immed8u: num8 too large"
}
clc
ldy #num
beq Done
Loop:
lsr addr+1
ror addr
dey
bne Loop
Done:
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Rotate bits left in a 16 bit location in memory.
// full name: nv_asl16u_mem16u_immed8u
// params:
// addr is the address of the LSB and addr+1 is the MSB of the
// 16 bit unsigned word to rotate
// num is an immediate value that is the nubmer of rotations to do.
// zeros will be rotated in to the low bits
// Use this to multiply by 2 or any power of two.
// Accum: unchanged
// X Reg: unchanged
// Y Reg: changes see nv_asl16u_mem16u_y8u
// old name: nv_asl16_immed
.macro nv_asl16u_mem16u_immed8u(addr, num)
{
.if (num > $FF)
{
.error "ERROR - nv_asl16u_mem16u_immed8u: num8 too large"
}
ldy #num
nv_asl16u_mem16u_y8u(addr)
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Rotate bits left in a 16 bit word in memory.
// Full name: nv_asl16u_mem16u_y8u
// params:
// addr: is the address of the LSB and addr+1 is the MSB of the
// word on which to perform the rotation.
// y reg: must be loaded with the nubmer of rotations to do.
// Zeros will be rotated in to the low bits of
// Use this to multiply by 2 or any power of two.
// Status Flags:
// Carry flag set if last rotate from bit high bit of addr's MSB was a 1
// Carry flag clear if last rotate from bit high bit of addr's MSB was a 0
// Accum: unchanged
// Y reg: changes, will be zero after macro executes
// X reg: unchanged
// old name nv_asl16_y
.macro nv_asl16u_mem16u_y8u(addr)
{
cpy #0
clc // must clear after the cpy above because cpy changes carry
beq Done
Loop:
asl addr
rol addr+1
dey
bne Loop
Done:
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to negate a 16 bit number at addr specified
// Accum: changes
// X Reg: unchanged
// Y Reg: unchanged
.macro negate16(addr16, result_addr16)
{
lda addr16
eor #$FF
sta result_addr16
lda addr16+1
eor #$FF
sta result_addr16+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to do twos compliment on a 16 but number in memory
// and place result in specified memory location.
// Accum: changes
// X Reg: unchanged
// Y Reg: unchanged
.macro nv_twos_comp_16_old(addr16, result_addr16)
{
// Twos Compliment is achieved by:
// bitwise negate of all 16 bits then
// add with carry a 1
// This could be done with the following macros:
// negate16(addr16, result_addr16)
// nv_adc16x_mem_immed(result_addr16, 1, result_addr16)
// But to make it a little faster we'll combine the two steps
// and save a few loads and stores
// negate the LSB of the operand and leave in accum
// to do the first step of the add
lda addr16
eor #$FF
// do the add of the negated LSB and immediate #1
// store this in the LSB of the result, its the final LSB of result
clc
adc #$01
sta result_addr16
// done with the LSB the carry flag is set if the LSB addition had carry
// Now we need to negate the Operand MSB and then add (with carry)
// the immediate value of #0
// negate the MSB of the operand and keep in accum
lda addr16+1
eor #$FF
// add in the #0 (with carry)
adc #$00
// store the final MSB into the result
sta result_addr16+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to do twos compliment on a 16 but number in memory
// and place result in specified memory location.
// Accum: changes
// X Reg: unchanged
// Y Reg: unchanged
.macro nv_twos_comp_16(addr16, result_addr16)
{
// Twos Compliment is achieved by:
// bitwise negate of all 16 bits then
// add with carry a 1
// This could be done with the following macros:
// negate16(addr16, result_addr16)
// nv_adc16x_mem_immed(result_addr16, 1, result_addr16)
// but twos compliment of a number is also the same as 0 - number, so we'll
// use this as a slightly faster way to get the result
sec
lda #$00
sbc addr16
sta result_addr16
lda #$00
sbc addr16+1
sta result_addr16+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline mcaro to subtract contents at addr2 from those at addr1
// result16_adder = addr1 - addr2
// full name: nv_sbc16x_mem16x_mem16x
// Status flags:
// Carry set if unsigned result is greater than zero, or more generally
// it does fit in the unsigned 16bit int range (0 - 65535)
// Carry clear if no carry from MSB occured, ie if the unsigned result
// is less than zero or more generally does not
// fit in a 16 bit unsigned int (0 - 65535)
// Overflow set: if signed result outside bound of
// 16 bit signed int (-32768 and +32767)
// Overflow clear if signed result falls within the bound of
// 16 bit signed int
// Neg set if result has sign/high bit set
// Neg clear if result has sign/high bit clear
.macro nv_sbc16(addr1, addr2, result16_addr)
{
sec
lda addr1
sbc addr2
sta result16_addr
lda addr1+1
sbc addr2+1
sta result16_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inlne macro to store 16 bit immediate value into the word with LSB
// at lsb_addr
// Accum: changes
// X Reg: unchanged
// Y Reg: unchanged
.macro nv_store16_immed(lsb_addr, value)
{
lda #(value & $00FF)
sta lsb_addr
lda #(value >> 8)
sta lsb_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to move one 16 bit word in memory to another location
// in memory.
// Macro Params:
// lsb_src_addr: LSB of the source for the copy
// lsb_dest_addr: LSB of the destination for the copy
// Note: Accum will be modified
// X and Y registers will be unchanged
.macro nv_xfer16_mem_mem(lsb_src_addr, lsb_dest_addr)
{
lda lsb_src_addr
sta lsb_dest_addr
lda lsb_src_addr+1
sta lsb_dest_addr+1
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add two 16 bit BCD values and store the result in another
// 16bit BCD value. Carry bit will be set if carry occured.
// full name: nv_bcd_adc16u_mem16u_mem16u
// params:
// addr1 is the address of the LSB of op1
// addr2 is the address of the LSB of op2
// result_addr is the address to store the result.
// Note: clears decimal mode after the addition is done
// Processor Status flags:
// Carry flag will be set if carry from MSB addition (decimal addition)
// causee carry. ie if MSB addition was $99 + $01, carry is set
// Carry flag will be clear if carry from MSB addition (decimal addition)
// did not cause carry.
// No other flags are reliably set.
// Accum: changes
// X Reg: No change
// Y Reg: No Change
.macro nv_bcd_adc16(addr1, addr2, result_addr)
{
sed
lda addr1
clc
adc addr2
sta result_addr
lda addr1+1
adc addr2+1
sta result_addr+1
cld
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline macro to add one 16 bit value in memory to an immediate value
// and store the result in another 16 bit value in memory.
// result_addr = addr1 + num
// full name: nv_bcd_adc16u_mem16u_immed16u
// params:
// addr1 is the address of the LSB of 16 bit value in memory that
// must be a valid BCD number.
// num: is the immeidate 16bit number to add. It must be a valid BCD
// number which is hex values with no letters in it.
// result_addr is the address of the LSB of the 16 bit memory location
// to store the BCD result.
// Note: clears decimal mode after the addition is done
// Processor Status flags:
// Carry flag will be set if carry from MSB addition (decimal addition)
// causee carry. ie if MSB addition was $99 + $01, carry is set
// Carry flag will be clear if carry from MSB addition (decimal addition)
// did not cause carry.
// No other flags are reliably set.
// Accum: changes
// X Reg: No change
// Y Reg: No Change
// old name: nv_bcd_adc16_immed
.macro nv_bcd_adc16_mem_immed(addr1, num, result_addr)
{
sed
lda addr1
clc
adc #(num & $00FF)
sta result_addr
lda addr1+1
adc #((num >> 8) & $00FF)
sta result_addr+1
cld
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline mcaro to subtract 16 bit BCD values. All ops and result are BCD
// result_addr = addr1 - addr2
// full name: nv_bcd_sbc16u_mem16u_mem16u
// params:
// addr1: Operand for subtraction. Must be valid BCD
// addr2: Operand for subtraction. Must be valid BCD
// result_addr: address of word to recieve the result of subtraction.
// Processor Status flags:
// Carry flag will be set if no borrow was required from MSB subtraction
// ie if MSB subtraction was $04 - $01, carry is set
// Carry flag will be clear if borrow was required from MSB subtraction
// (decimal subtraction.) ie $01 - $04 carry is clear
// No other flags are reliably set.
// Accum: changes
// X Reg: No change
// Y Reg: No Change
.macro nv_bcd_sbc16(addr1, addr2, result_addr)
{
sed // set decimal (BCD) mode
sec
lda addr1
sbc addr2
sta result_addr
lda addr1+1
sbc addr2+1
sta result_addr+1
cld // clear decimal (BCD) mode
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline mcaro to subtract 16 bit immed value from BCD value in memory.
// All ops and result are BCD
// result_addr = addr1 - num
// full name: nv_bcd_sbc16u_mem16u_immed16u
// params:
// addr1: Operand for subtraction. Must be valid BCD
// num: immediate value for subtraction. Must be valid BCD
// result_addr: address of word to recieve the result of subtraction.
// Processor Status flags:
// Carry flag will be set if no borrow was required from MSB subtraction
// ie if MSB subtraction was $04 - $01, carry is set
// Carry flag will be clear if borrow was required from MSB subtraction
// (decimal subtraction.) ie $01 - $04 carry is clear
// No other flags are reliably set.
// Accum: changes
// X Reg: No change
// Y Reg: No Change
.macro nv_bcd_sbc16_mem_immed(addr1, num, result_addr)
{
sed // set decimal (BCD) mode
sec // set carry for subtraction
lda addr1
sbc #(num & $00FF) // subtract LSBs
sta result_addr // store LSB of result
lda addr1+1
sbc #((num >> 8) & $00FF) // subtract MSBs
sta result_addr+1 // store MSB of result
cld // clear decimal (BCD) mode
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// inline mcaro to subtract 16 bit immed value from value in memory.
// result_addr = addr1 - num
// full name: nv_sbc16u_mem16u_immed16u
// params:
// addr1: Operand for subtraction.
// num: immediate value for subtraction.
// result_addr: address of word to recieve the result of subtraction.
// Processor Status flags:
// Carry flag will be set if no borrow was required from MSB subtraction
// ie if MSB subtraction was $04 - $01, carry is set
// Carry flag will be clear if borrow was required from MSB subtraction
// (decimal subtraction.) ie $01 - $04 carry is clear
// No other flags are reliably set.
// Accum: changes
// X Reg: No change
// Y Reg: No Change
.macro nv_sbc16_mem_immed(addr1, num, result_addr)
{
sec // set carry for subtraction
lda addr1
sbc #(num & $00FF) // subtract LSBs
sta result_addr // store LSB of result
lda addr1+1
sbc #((num >> 8) & $00FF) // subtract MSBs
sta result_addr+1 // store MSB of result
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// macro routine to test if one rectangle overlaps another
// This routine will work on rectangles of any size.
// If its known that one rectangle can completely fit inside the other one
// than another then the macro nv_util_check_small_rect_in_big_rect
// rect1_addr: address of a rectangle. A rectangle is defined by
// 8 bytes, which are interpreted as two 16 bit xy pairs
// as such:
// x_left: .word
// y_top: .word
// x_right: .word
// y_bottom: .word
// rect2_addr: address of another rectangle
// load accum to 1 if they overlap or 0 if they do not overlap
.macro nv_check_rect_overlap16(rect1_addr, rect2_addr)
{
.label r1_left = rect1_addr
.label r1_top = rect1_addr + 2
.label r1_right = rect1_addr + 4
.label r1_bottom = rect1_addr + 6
.label r2_left = rect2_addr
.label r2_top = rect2_addr + 2
.label r2_right = rect2_addr + 4
.label r2_bottom = rect2_addr + 6
// this is the algorithm to determine if rects overlap
// if ((r2.left is between r1.left and r1.right) or
// (r2.right is between r1.left and r1.right)) and
// ((r2.bottom is below r1.top) and (r2.top is above r1.bottom)))
// then
// {
// rects overlap
// }
// else
// {
// do same comparison with reverse (use r1 for r2 and r2 for r1 in above if)
// }
nv_check_range16(r2_left, r1_left, r1_right, false)
bne OneVertSideBetween
nv_check_range16(r2_right, r1_left, r1_right, false)
bne OneVertSideBetween
jmp TryReverse
OneVertSideBetween:
nv_blt16(r2_bottom, r1_top, TryReverse)
nv_bgt16(r2_top, r1_bottom, TryReverse)
jmp RectOverlap
TryReverse:
nv_check_range16(r1_left, r2_left, r2_right, false)
bne OneVertSideBetween2
nv_check_range16(r1_right, r2_left, r2_right, false)
bne OneVertSideBetween2
jmp NoRectOverlap
OneVertSideBetween2:
nv_blt16(r1_bottom, r2_top, NoRectOverlap)
nv_bgt16(r1_top, r2_bottom, NoRectOverlap)
// fall through to RectOverlap
RectOverlap:
lda #1
jmp AccumLoaded
NoRectOverlap:
lda #0
AccumLoaded:
}
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// macro to test if one 16 bit number is within a range
// the number to test and the range bounds are 16 bit numbers at
// specified memory locations.
// macro params:
// test_num_addr: is the address of LSB of the 16 bit number to test
// num_high_addr: is the address of the LSB of the 16 bit number that is
// the high bound to check
// num_low_addr: is the address of the LSB of the 16 bit number that is
// the low bound to check
// inclusive: should be set to true if the bound numbers are considered
// in range, or false if the bound numbers are outsid the range.
// accum: will be set to 1 if test num is between num low and num high.
// X Reg: unchanged
// Y Reg: unchanged
.macro nv_check_range16(test_num_addr, num_low_addr, num_high_addr, inclusive)
{
.if (inclusive)
{
nv_blt16(test_num_addr, num_low_addr, ResultFalse)
nv_bgt16(test_num_addr, num_high_addr, ResultFalse)
}
else
{
nv_ble16(test_num_addr, num_low_addr, ResultFalse)
nv_bge16(test_num_addr, num_high_addr, ResultFalse)
}