-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRobotControl.asm
More file actions
1335 lines (1152 loc) · 36.6 KB
/
Copy pathRobotControl.asm
File metadata and controls
1335 lines (1152 loc) · 36.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; This program is used to detect intruder bots
; using the sonar sensors on the DE2 bot
ORG 0 ; Jump table is located in mem 0-4
; This code uses the timer interrupt for the control code.
JUMP Init ; Reset vector
RETI ; Sonar interrupt (unused)
JUMP CTimer_ISR ; Timer interrupt
RETI ; UART interrupt (unused)
RETI ; Motor stall interrupt (unused)
;***************************************************************
;* Initialization
;***************************************************************
Init:
; Always a good idea to make sure the robot
; stops in the event of a reset.
LOAD Zero
OUT LVELCMD ; Stop motors
OUT RVELCMD
STORE DVel ; Reset movement API variables
STORE DTheta
OUT SONAREN ; Disable sonar (optional)
OUT BEEP ; Stop any beeping (optional)
CALL SetupI2C ; Configure the I2C to read the battery voltage
CALL BattCheck ; Get battery voltage (and end if too low).
OUT LCD ; Display battery voltage (hex, tenths of volts)
LOADI &H130
OUT BEEP ; Short hello beep
WaitForSafety:
; This loop will wait for the user to toggle SW17. Note that
; SCOMP does not have direct access to SW17; it only has access
; to the SAFETY signal contained in XIO.
IN XIO ; XIO contains SAFETY signal
AND Mask4 ; SAFETY signal is bit 4
JPOS WaitForUser ; If ready, jump to wait for PB3
IN TIMER ; Use the timer value to
AND Mask1 ; blink LED17 as a reminder to toggle SW17
SHIFT 8 ; Shift over to LED17
OUT XLEDS ; LED17 blinks at 2.5Hz (10Hz/4)
JUMP WaitForSafety
WaitForUser:
; This loop will wait for the user to press PB3, to ensure that
; they have a chance to prepare for any movement in the main code.
IN TIMER ; Used to blink the LEDs above PB3
AND Mask1
SHIFT 5 ; Both LEDG6 and LEDG7
STORE Temp ; (overkill, but looks nice)
SHIFT 1
OR Temp
OUT XLEDS
IN XIO ; XIO contains KEYs
AND Mask2 ; KEY3 mask (KEY0 is reset and can't be read)
JPOS WaitForUser ; not ready (KEYs are active-low, hence JPOS)
LOAD Zero
OUT XLEDS ; clear LEDs once ready to continue
;***************************************************************
;* Arguments
;***************************************************************
D1: DW 0 ; Distance from course starting position to right wall
D2: DW 0 ; In area 2 distance from robot to baffle on left
D3: DW 0 ; In area 3, distance from robot to left wall
LEFTD: DW 0 ; Distance from segement start location to left wall/baffle
RIGHTD: DW 0 ; Distance from segment start location to right wall/baffle
RANGE: DW 0 ; Argument of the CheckRange method to help with movement
EXSIDE: DW 0 ; Uses -1 to track left wall and 1 to track right wall
TRAVELD: DW 0 ; Distance to travel by odometry
TURNDEG: DW 0 ; How far to turn at the end of the movement
TOPD: DW 0 ; How far to travel along the top of the baffle
LEFTD1: DW 0 ; Distance to check for intruders on left side in segment 1
TRAVELT: DW 0 ; Distance to travel using odometry (0) or wall detection (1)
BaffleRL: DW 0 ; 0 = Baffle on the left, 1 = Baffle on the right
HitCountL: DW 0 ; Number of times that a hit is detected on the left, used for baffle rejection
HitCountR: DW 0 ; Number of times that a hit is detected on the right, used for baffle rejection
;***************************************************************
;* Global variables
;***************************************************************
FRONTD: DW 0 ; Distance to check with front sensors
LOADI 700
STORE FRONTD
; Used as region of error within which a baffle detection is rejected
FIFTY: DW 0
LOADI 305
SHIFT 2
STORE FIFTY
SIXTYFIVE: DW 0
LOADI 396
SHIFT 2
STORE SIXTYFIVE
;***************************************************************
;* TODO list for possible improvements:
;* - Check if just reading DIST5 will work (line 184)
;* - Check if when moving back up segment 3 whether it's better
;* to just read in the sensor values rather than use previous
;* measurements due to issues with turning at wrong angles
;* - Check how chaning the variable for turn angle error will
;* affect how accurate our turns are (line 351)
;* - Check if TopD works generically given how inaccurate
;* sensor values are
;***************************************************************
;***************************************************************
;* Main code
;***************************************************************
Main:
OUT RESETPOS ; reset odometer in case wheels moved after programming
; configure timer interrupts to enable the movement control code
LOADI 10 ; fire at 10 Hz (10 ms * 10).
OUT CTIMER ; turn on timer peripheral
SEI &B0010 ; enable interrupts from source 2 (timer)
; at this point, timer interrupts will be firing at 10Hz, and
; code in that ISR will attempt to control the robot.
; If you want to take manual control of the robot,
; execute CLI &B0010 to disable the timer interrupt.
LOADI &B00000000 ; Disable Sonar Interrupts
OUT SONARINT
LOADI &B00101101 ; Enable Sensors 0 and 5
OUT SONAREN
CALL WAIT1
;***************************************************************
;* Current debugging info:
;* SSeg1 = CountL
;* SSeg2 = CountR
;* LCD = 00AA and 00BB for detection
;***************************************************************
;***************************************************************
;* Looping over course
;***************************************************************
DriveLoop:
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 1 *********************************
; Up the baffle on the right side
IN DIST5 ; Read in right sonar
STORE D1 ; Store for future calcuations
ADDI -280 ; Slightly less than width of intruder robot
STORE RIGHTD ; Distance used to check for intruders on right
LOADI 0
STORE BaffleRL ; IGNORE BAFFLE IF ON THE LEFT
IN DIST0 ; Read in left sonar
ADDI -280 ; Slightly less than width of intruder robot
STORE LEFTD ; Distance used to check for intruders on left
STORE LEFTD1 ; Distance for checking intruders on left for segment 1
; Distance to go before robot thinks its seeing wall instead of intruder
LOADI 625
SHIFT 2
STORE TRAVELD ; Travel until wall
; TODO: check if this works
IN DIST0 ; Read in left sonar again
SHIFT 1 ; Multiply by 2 to go same distance on other side
ADDI 200 ; Give it some buffer room
STORE TOPD ; Keep for when returning
LOADI 1 ; Wall tracking on right for this and next seg
STORE EXSIDE
LOADI 1
STORE TRAVELT ; Store 1 to travel until wall detected
LOADI 90
STORE TURNDEG
CALL DriveScan
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 2 *********************************
; Along the wall going left
IN DIST5
STORE D2
ADDI -280 ; Slightly less than width of intruder robot
STORE RIGHTD
LOADI 547 ; (66 - 12 - 11)in ~ 1094 mm / 2 = 547 mm
SHIFT 1
SUB D2 ; 1094 mm - D2 length
STORE LEFTD
LOAD TOPD
STORE TRAVELD ; Travel 2 meters
LOADI 0
STORE TRAVELT ; Travel based on odometry value for this and next seg (change to sonar with baffle)
CALL DriveScan
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 3 *********************************
; Down the baffle on the left side
; TODO: Check if just reading DIST5 will work
;LOADI 910 ; ~287 in, total width of course
;SHIFT 3
;SUB D1 ; Find RightD using original position
;ADDI -305 ; ~12in (account for width of robot)
;SUB TOPD ; Subtract distance traveled on top
IN DIST5
ADDI -280 ; Slightly less than width of intruder robot
STORE RIGHTD ; Store final value to find intruders
LOAD LEFTD1
STORE LEFTD
LOADI 625
SHIFT 2
STORE TRAVELD ; Travel 2.5 meters
LOADI 180
STORE TURNDEG
LOADI 0 ; Don't use wall tracking for last segment
STORE EXSIDE
CALL DriveScan
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 3 *********************************
; Up the baffle on the left side
LOADI 1
STORE BaffleRL ; IGNORE BAFFLE IF ON THE RIGHT
LOAD LEFTD1 ; Use original cacluated distance from baffle
STORE RIGHTD
LOAD RIGHTD ; Use RIGHTD that was just calculated
STORE LEFTD
;LOADI 625
;SHIFT 2
LOADI 600
SHIFT 1
STORE TRAVELD ; Travel 2.5 meters
LOADI -90
STORE TURNDEG
LOADI 1
STORE TRAVELT ; Travel until hitting wall
CALL DriveScan
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 2 *********************************
; Along the wall going right
IN DIST0
STORE D2
ADDI -280 ; Slightly less than width of intruder robot
STORE LEFTD
LOADI 547 ; (66 - 12 - 11)in ~ 1094 mm / 2 = 547 mm
SHIFT 1
SUB D2 ; 1094 mm - D2 length
STORE RIGHTD
LOAD TOPD
STORE TRAVELD ; Depending on how far traveled first time
LOADI -1 ; Wall track on left wall for this and next seg
STORE EXSIDE
LOADI 0
STORE TRAVELT ; Travel based on odometry for this and next seg (change to sonar with baffle)
CALL DriveScan
OUT RESETPOS
LOADI 0
STORE DTheta
OUT TIMER
;********************** SEGMENT 1 *********************************
; Down the baffle on the right side
IN DIST0 ; Read in right sonar
STORE D1
ADDI -280 ; Slightly less than width of intruder robot
STORE LEFTD ; Distance used to check for intruders on right
LOAD D1
ADDI -305 ; ~12in (width of our robot)
ADDI -280 ; Slightly less than width of intruder robot
STORE RIGHTD
;LOADI 625 ; Value from hardcoded odometry
;SHIFT 2
LOADI 600 ; Since not starting in clear zone 126in - 6in (wheels at 1/2 length robot)
SHIFT 2
STORE TRAVELD ; Travel ~3 meters
LOADI 180
STORE TURNDEG
CALL DriveScan
JUMP DriveLoop ; Continue looping forever
Die:
; Sometimes it's useful to permanently stop execution.
; This will also catch the execution if it accidentally
; falls through from above.
CLI &B1111 ; disable all interrupts
LOAD Zero ; Stop everything.
OUT LVELCMD
OUT RVELCMD
OUT SONAREN
LOAD DEAD ; An indication that we are dead
CALL CHIRP
; OUT SSEG2 ; "dEAd" on the sseg
Forever:
JUMP Forever ; Do this forever.
DEAD: DW &HDEAD ; Example of a "local" variable
;***************************************************************
;* Local Variables
;***************************************************************
WALLD: DW 0 ; Distance to the wall being tracked
PREVD: DW 0 ; Previous Wall Distance
WALLTIME: DW 0 ; Wall timer variable
HITTIME: DW 0 ; Baffle detection timer
BREAKOUT: DW 0 ; Timer to break out of stuck position
debug: DW 0;debug
;***************************************************************
;* Movement and intruder detection methods
;***************************************************************
DriveScan:
LOADI &B00101101 ; Enable Sensors 0 and 5
OUT SONAREN
LOADI 0
STORE DTheta ; Desired angle 0
;Reset Variables
LOADI 0
STORE WALLD
STORE PREVD
STORE WALLTIME
STORE HITTIME
STORE BREAKOUT
STORE HitCountL
STORE HitCountR
Driving: ; Start driving
LOAD FMid
STORE DVel
LOAD EXSIDE ; Setup wall tracking on correct side
JNEG SetLeft
JPOS SetRight
JUMP DriveOdometry
SetLeft:
IN DIST0
STORE WALLD
JUMP WallTrack
SetRight:
IN DIST5
STORE WALLD
WallTrack: ; Start wall tracking code
LOADI 10 ; This should be 1 second
STORE RANGE
IN TIMER
SUB WALLTIME
CALL CheckRange
JNEG DriveOdometry
IN TIMER ;reset the wall tracking timer
STORE WALLTIME
LOAD PREVD
ADDI -40
STORE RANGE
LOAD WALLD
CALL CheckRange
JNEG TooClose
LOAD PREVD
ADDI 40
STORE RANGE
LOAD WALLD
CALL CheckRange
JPOS TooFar
JUMP DriveOdometry
TooClose: IN Theta
ADD EXSIDE
ADD EXSIDE
ADD EXSIDE
STORE DTheta
LOADI &HAA
;OUT SSEG1
JUMP TrackDone
TooFar: IN Theta
SUB EXSIDE
SUB EXSIDE
SUB EXSIDE
STORE DTheta
LOADI &HBB
;OUT SSEG1
TrackDone:
LOAD WALLD
STORE PREVD
DriveOdometry:
; Check for intruders in front-left
IN DIST2
SUB FRONTD
JPOS NotFrontLeft
LOAD TRAVELT
JZERO OnlyOdometry1
LOAD TRAVELD
ADDI -250 ; Buffer of about 10 in.
OUT SSEG2
STORE RANGE
IN XPOS
OUT LCD
CALL CheckRange
JPOS SetTurn
OnlyOdometry1:
IN TIMER
STORE BREAKOUT
CALL IntruderDetectedFront
NotFrontLeft:
; Check for intruders on front-right
IN DIST3
SUB FRONTD
JPOS SideChecks
LOAD TRAVELT
JZERO OnlyOdometry2
LOAD TRAVELD
ADDI -250 ; Buffer of about 10 in.
OUT SSEG2
STORE RANGE
IN XPOS
OUT LCD
CALL CheckRange
JPOS SetTurn
OnlyOdometry2:
IN TIMER
STORE BREAKOUT
CALL IntruderDetectedFront
SideChecks: ; Check for intruders on sides
LOADI 1
STORE RANGE
IN TIMER
SUB HITTIME
CALL CheckRange
JNEG CheckDrive
IN TIMER
STORE HITTIME
IN DIST0
SUB LEFTD
JPOS NotOnLeft
LOAD HitCountL
ADDI 1 ;if you detect something on the left add to HitCountL
STORE HitCountL
CALL IntruderDetectedSide
JUMP CheckRight
NotOnLeft:
LOADI 0
STORE HitCountL
CheckRight:
IN DIST5
SUB RIGHTD
JPOS NotOnRight
LOAD HitCountR
ADDI 1 ;if you detect something on the right add one to the right hit count
STORE HitCountR
CALL IntruderDetectedSide
JUMP CheckDrive
NotOnRight:
LOADI 0
STORE HitCountR ;If there is nothing detected, reset the hit counters
CheckDrive:
; Debugging stuff
LOAD HitCountL
OUT SSEG1
LOAD HitCountR
OUT SSEG2
LOAD TRAVELT
JPOS Driving
LOAD TRAVELD
STORE RANGE
IN XPOS ; Read odometry value
CALL CheckRange
JNEG Driving ; Continue driving for given distance
SetTurn:
IN Theta
ADD TurnDeg
STORE DTheta
TurnAfter: ; Turn after odometry movement finished
LOADI 0
STORE DVel
CALL GetThetaErr
CALL Abs
ADDI -1 ; TODO: Check how changing this value will affect turning
JPOS TurnAfter
CALL WAIT1 ; Fix turn please
RETURN
;Check value in argument against value in RANGE
;Returns -1 if less than, 0 if equal to, 1 if greater than
CheckRange:
SUB RANGE
JNEG VNegative
JPOS VPositive
JZERO VZero
VNegative: LOADI -1
RETURN
VPositive: LOADI 1
RETURN
VZero: LOADI 0
RETURN
BaffleCheck:
;determie if enough distance has passed for the bot to be potentially detecting the baffle
IN XPOS
SUB FIFTY
JNEG IntruderDetectedSide
IN XPOS
SUB SIXTYFIVE
JPOS IntruderDetectedSide
RETURN
IntruderDetectedSide:
ADDI -6 ;check the current hit count against the number of consecutive hits
JZERO NotRejected ;beep when 6 consecutive hits have occurred
RETURN
NotRejected:
LOADI &H0330
OUT BEEP
RETURN
IntruderDetectedFront:
;IN XPOS
;SUB TRAVELD
;ADDI -305
;JPOS CheckFront
;RETURN
CheckFront:
LOADI 0 ; Stop the robot
STORE DVel
LOADI &H0330 ; Beep on intruder detection
OUT BEEP
LOAD FRONTD ; Check if intruder still in front
STORE RANGE
LOADI 200 ;Break out if we have been stuck for 20 seconds
STORE RANGE ;We are probably staring at the wall
IN TIMER
SUB BREAKOUT
CALL CheckRange
JPOS BreakoutTime
IN DIST2 ; Check front-left
CALL CheckRange
JNEG IntruderDetectedFront ; Stay in method if front-left sensor detects
IN DIST3 ; Check front-right
CALL CheckRange
JNEG IntruderDetectedFront ; Stay in method if front-right sensor detects
BreakoutTime:
RETURN
; Below this line is a library of subroutines and functions
; not written by myself or other group members
Chirp:
LOADI &H0320
OUT BEEP
CALL WAIT1
LOADI &H0330
OUT BEEP
CALL WAIT1
LOADI &H0340
OUT BEEP
CALL WAIT1
RETURN
; Timer ISR. Currently just calls the movement control code.
; You could, however, do additional tasks here if desired.
CTimer_ISR:
CALL ControlMovement
RETI ; return from ISR
; Control code. If called repeatedly, this code will attempt
; to control the robot to face the angle specified in DTheta
; and match the speed specified in DVel
DTheta: DW 0
DVel: DW 0
ControlMovement:
LOADI 50 ; used for the CapValue subroutine
STORE MaxVal
CALL GetThetaErr ; get the heading error
; A simple way to get a decent velocity value
; for turning is to multiply the angular error by 4
; and add ~50.
SHIFT 2
STORE CMAErr ; hold temporarily
SHIFT 2 ; multiply by another 4
CALL CapValue ; get a +/- max of 50
ADD CMAErr
STORE CMAErr ; now contains a desired differential
; For this basic control method, simply take the
; desired forward velocity and add the differential
; velocity for each wheel when turning is needed.
LOADI 510
STORE MaxVal
LOAD DVel
CALL CapValue ; ensure velocity is valid
STORE DVel ; overwrite any invalid input
ADD CMAErr
CALL CapValue ; ensure velocity is valid
STORE CMAR
LOAD CMAErr
CALL Neg ; left wheel gets negative differential
ADD DVel
CALL CapValue
STORE CMAL
; ensure enough differential is applied
LOAD CMAErr
SHIFT 1 ; double the differential
STORE CMAErr
LOAD CMAR
SUB CMAL ; calculate the actual differential
SUB CMAErr ; should be 0 if nothing got capped
JZERO CMADone
; re-apply any missing differential
STORE CMAErr ; the missing part
ADD CMAL
CALL CapValue
STORE CMAL
LOAD CMAR
SUB CMAErr
CALL CapValue
STORE CMAR
CMADone:
LOAD CMAL
OUT LVELCMD
LOAD CMAR
OUT RVELCMD
RETURN
CMAErr: DW 0 ; holds angle error velocity
CMAL: DW 0 ; holds temp left velocity
CMAR: DW 0 ; holds temp right velocity
; Returns the current angular error wrapped to +/-180
GetThetaErr:
; convenient way to get angle error in +/-180 range is
; ((error + 180) % 360 ) - 180
IN THETA
SUB DTheta ; actual - desired angle
CALL Neg ; desired - actual angle
ADDI 180
CALL Mod360
ADDI -180
RETURN
; caps a value to +/-MaxVal
CapValue:
SUB MaxVal
JPOS CapVelHigh
ADD MaxVal
ADD MaxVal
JNEG CapVelLow
SUB MaxVal
RETURN
CapVelHigh:
LOAD MaxVal
RETURN
CapVelLow:
LOAD MaxVal
CALL Neg
RETURN
MaxVal: DW 510
;***************************************************************
;* Subroutines
;***************************************************************
;*******************************************************************************
; Mod360: modulo 360
; Returns AC%360 in AC
; Written by Kevin Johnson. No licence or copyright applied.
;*******************************************************************************
Mod360:
; easy modulo: subtract 360 until negative then add 360 until not negative
JNEG M360N
ADDI -360
JUMP Mod360
M360N:
ADDI 360
JNEG M360N
RETURN
;*******************************************************************************
; Abs: 2's complement absolute value
; Returns abs(AC) in AC
; Neg: 2's complement negation
; Returns -AC in AC
; Written by Kevin Johnson. No licence or copyright applied.
;*******************************************************************************
Abs:
JPOS Abs_r
Neg:
XOR NegOne ; Flip all bits
ADDI 1 ; Add one (i.e. negate number)
Abs_r:
RETURN
;******************************************************************************;
; Atan2: 4-quadrant arctangent calculation ;
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;
; Original code by Team AKKA, Spring 2015. ;
; Based on methods by Richard Lyons ;
; Code updated by Kevin Johnson to use software mult and div ;
; No license or copyright applied. ;
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;
; To use: store dX and dY in global variables AtanX and AtanY. ;
; Call Atan2 ;
; Result (angle [0,359]) is returned in AC ;
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;
; Requires additional subroutines: ;
; - Mult16s: 16x16->32bit signed multiplication ;
; - Div16s: 16/16->16R16 signed division ;
; - Abs: Absolute value ;
; Requires additional constants: ;
; - One: DW 1 ;
; - NegOne: DW 0 ;
; - LowByte: DW &HFF ;
;******************************************************************************;
Atan2:
LOAD AtanY
CALL Abs ; abs(y)
STORE AtanT
LOAD AtanX ; abs(x)
CALL Abs
SUB AtanT ; abs(x) - abs(y)
JNEG A2_sw ; if abs(y) > abs(x), switch arguments.
LOAD AtanX ; Octants 1, 4, 5, 8
JNEG A2_R3
CALL A2_calc ; Octants 1, 8
JNEG A2_R1n
RETURN ; Return raw value if in octant 1
A2_R1n: ; region 1 negative
ADDI 360 ; Add 360 if we are in octant 8
RETURN
A2_R3: ; region 3
CALL A2_calc ; Octants 4, 5
ADDI 180 ; theta' = theta + 180
RETURN
A2_sw: ; switch arguments; octants 2, 3, 6, 7
LOAD AtanY ; Swap input arguments
STORE AtanT
LOAD AtanX
STORE AtanY
LOAD AtanT
STORE AtanX
JPOS A2_R2 ; If Y positive, octants 2,3
CALL A2_calc ; else octants 6, 7
CALL Neg ; Negatge the number
ADDI 270 ; theta' = 270 - theta
RETURN
A2_R2: ; region 2
CALL A2_calc ; Octants 2, 3
CALL Neg ; negate the angle
ADDI 90 ; theta' = 90 - theta
RETURN
A2_calc:
; calculates R/(1 + 0.28125*R^2)
LOAD AtanY
STORE d16sN ; Y in numerator
LOAD AtanX
STORE d16sD ; X in denominator
CALL A2_div ; divide
LOAD dres16sQ ; get the quotient (remainder ignored)
STORE AtanRatio
STORE m16sA
STORE m16sB
CALL A2_mult ; X^2
STORE m16sA
LOAD A2c
STORE m16sB
CALL A2_mult
ADDI 256 ; 256/256+0.28125X^2
STORE d16sD
LOAD AtanRatio
STORE d16sN ; Ratio in numerator
CALL A2_div ; divide
LOAD dres16sQ ; get the quotient (remainder ignored)
STORE m16sA ; <= result in radians
LOAD A2cd ; degree conversion factor
STORE m16sB
CALL A2_mult ; convert to degrees
STORE AtanT
SHIFT -7 ; check 7th bit
AND One
JZERO A2_rdwn ; round down
LOAD AtanT
SHIFT -8
ADDI 1 ; round up
RETURN
A2_rdwn:
LOAD AtanT
SHIFT -8 ; round down
RETURN
A2_mult: ; multiply, and return bits 23..8 of result
CALL Mult16s
LOAD mres16sH
SHIFT 8 ; move high word of result up 8 bits
STORE mres16sH
LOAD mres16sL
SHIFT -8 ; move low word of result down 8 bits
AND LowByte
OR mres16sH ; combine high and low words of result
RETURN
A2_div: ; 16-bit division scaled by 256, minimizing error
LOADI 9 ; loop 8 times (256 = 2^8)
STORE AtanT
A2_DL:
LOAD AtanT
ADDI -1
JPOS A2_DN ; not done; continue shifting
CALL Div16s ; do the standard division
RETURN
A2_DN:
STORE AtanT
LOAD d16sN ; start by trying to scale the numerator
SHIFT 1
XOR d16sN ; if the sign changed,
JNEG A2_DD ; switch to scaling the denominator
XOR d16sN ; get back shifted version
STORE d16sN
JUMP A2_DL
A2_DD:
LOAD d16sD
SHIFT -1 ; have to scale denominator
STORE d16sD
JUMP A2_DL
AtanX: DW 0
AtanY: DW 0
AtanRatio: DW 0 ; =y/x
AtanT: DW 0 ; temporary value
A2c: DW 72 ; 72/256=0.28125, with 8 fractional bits
A2cd: DW 14668 ; = 180/pi with 8 fractional bits
;*******************************************************************************
; Mult16s: 16x16 -> 32-bit signed multiplication
; Based on Booth's algorithm.
; Written by Kevin Johnson. No licence or copyright applied.
; Warning: does not work with factor B = -32768 (most-negative number).
; To use:
; - Store factors in m16sA and m16sB.
; - Call Mult16s
; - Result is stored in mres16sH and mres16sL (high and low words).
;*******************************************************************************
Mult16s:
LOADI 0
STORE m16sc ; clear carry
STORE mres16sH ; clear result
LOADI 16 ; load 16 to counter
Mult16s_loop:
STORE mcnt16s
LOAD m16sc ; check the carry (from previous iteration)
JZERO Mult16s_noc ; if no carry, move on
LOAD mres16sH ; if a carry,
ADD m16sA ; add multiplicand to result H
STORE mres16sH
Mult16s_noc: ; no carry
LOAD m16sB
AND One ; check bit 0 of multiplier
STORE m16sc ; save as next carry
JZERO Mult16s_sh ; if no carry, move on to shift
LOAD mres16sH ; if bit 0 set,
SUB m16sA ; subtract multiplicand from result H
STORE mres16sH
Mult16s_sh:
LOAD m16sB
SHIFT -1 ; shift result L >>1
AND c7FFF ; clear msb
STORE m16sB
LOAD mres16sH ; load result H
SHIFT 15 ; move lsb to msb
OR m16sB
STORE m16sB ; result L now includes carry out from H
LOAD mres16sH
SHIFT -1
STORE mres16sH ; shift result H >>1
LOAD mcnt16s
ADDI -1 ; check counter
JPOS Mult16s_loop ; need to iterate 16 times
LOAD m16sB