![]() |
![]() |
#1 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Atan2 in assembler for "integer" / screen-ish coordinates
Hi all , I'm needing an atan2 function in assembler. I have a 2D delta between objects , and want to determine the angle between then to rotate one towards the other. So far the best I can do is to decide on a which quadrant (aka delta signs) the line lines in; take the abs of both dx and dy whilst clamping each to a max value. Then scaling the dx and dy down and combining to form an index into a lookup table , modifying the result based on the original quadrant. So I have 2 main compromises , (1) the clamp will distort the line and (2) the scale down (to reduce the overall table size) will "pixelate" the results.
Any thoughts on how to do this better , thanks for any help. Paul. AtanTable: dc.w 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 dc.w 256,128,75,52,39,32,26,23,20,18,16,14,13,12,11,10 dc.w 256,180,128,95,75,62,52,45,39,35,32,29,26,24,23,21 dc.w 256,203,160,128,104,88,75,65,58,52,47,43,39,36,34,32 dc.w 256,216,180,151,128,109,95,84,75,68,62,56,52,48,45,42 dc.w 256,223,193,167,146,128,113,101,91,82,75,69,64,59,55,52 dc.w 256,229,203,180,160,142,128,115,104,95,88,81,75,70,65,62 dc.w 256,232,210,190,171,154,140,128,117,107,99,92,86,80,75,71 dc.w 256,235,216,197,180,164,151,138,128,118,109,102,95,89,84,79 dc.w 256,237,220,203,187,173,160,148,137,128,119,111,104,98,93,88 dc.w 256,239,223,208,193,180,167,156,146,136,128,120,113,106,101,95 dc.w 256,241,226,212,199,186,174,163,153,144,135,128,120,114,108,103 dc.w 256,242,229,216,203,191,180,169,160,151,142,135,128,121,115,109 dc.w 256,243,231,219,207,196,185,175,166,157,149,141,134,128,121,116 dc.w 256,244,232,221,210,200,190,180,171,162,154,147,140,134,128,122 dc.w 256,245,234,223,213,203,193,184,176,167,160,152,146,139,133,128 GetATan2: ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 tst.w d0 bpl.s .notnegY bset #0,d2 neg.w d0 .notnegY tst.w d1 bpl.s .notnegX bset #1,d2 neg.w d1 .notnegX .clamp cmp.w #256,d0 blt.s .noyclamp move.w #255,d0 .noyclamp cmp.w #256,d1 blt.s .noxclamp move.w #255,d1 .noxclamp ;16 entries to span 0..255 lsr.w #4,d1 add.w d1,d1 and.w #$F0,d0 add.w d0,d0 add.w d1,d0 lea.l AtanTable(pc),a4 move.w 0(a4,d0),d3 btst #0,d2 beq .noybias neg.w d3 add.w #1024,d3 .noybias btst #1,d2 beq .noxbias neg.w d3 add.w #512,d3 .noxbias move.w d3,d2 add.w d2,d2 and.w #$3FF<<1,d2 rts Last edited by PaulF; 19 August 2024 at 12:49. Reason: added (first pass!) code & table |
![]() |
![]() |
#2 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
So , tried this (forgive the first pass code) , but its got a juicy divide in there.
Atan table is now spans a 1/8th of a circle . Do quadrant stuff, now consider dy/dx , if > 1 , flip to dx/dy , look up angle and correct for >1 (45 degress) and quadrant. No clmaping or loss or resolution. AtanTable: dc.w 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 dc.w 10,10,11,12,12,13,13,14,15,15,16,17,17,18,19,19 dc.w 20,20,21,22,22,23,24,24,25,25,26,27,27,28,28,29 dc.w 30,30,31,32,32,33,33,34,35,35,36,36,37,38,38,39 dc.w 39,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48 dc.w 49,49,50,51,51,52,52,53,53,54,55,55,56,56,57,57 dc.w 58,59,59,60,60,61,61,62,62,63,63,64,65,65,66,66 dc.w 67,67,68,68,69,69,70,70,71,71,72,72,73,74,74,75 dc.w 75,76,76,77,77,78,78,79,79,80,80,81,81,82,82,83 dc.w 83,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90 dc.w 91,91,91,92,92,93,93,94,94,95,95,95,96,96,97,97 dc.w 98,98,99,99,99,100,100,101,101,101,102,102,103,103,104,104 dc.w 104,105,105,106,106,106,107,107,108,108,108,109,109,110,110,110 dc.w 111,111,111,112,112,113,113,113,114,114,114,115,115,116,116,116 dc.w 117,117,117,118,118,118,119,119,119,120,120,121,121,121,122,122 dc.w 122,123,123,123,124,124,124,125,125,125,126,126,126,127,127,127 dc.w 128 GetATan2: ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 tst.w d0 bpl.s .notnegY bset #0,d2 neg.w d0 .notnegY tst.w d1 bpl.s .notnegX bset #1,d2 neg.w d1 .notnegX cmp.w d1,d0 blt.s .noswap exg d0,d1 bset #2,d2 .noswap ext.l d0 lsl.w #8,d0 tst.w d1 beq.s .div0 divu d1,d0 add.w d0,d0 lea.l AtanTable(pc),a4 move.w 0(a4,d0),d3 btst #2,d2 beq .wasntsteep neg.w d3 add.w #256,d3 .wasntsteep btst #0,d2 beq .noybias neg.w d3 add.w #1024,d3 .noybias btst #1,d2 beq .noxbias neg.w d3 add.w #512,d3 .noxbias move.w d3,d2 add.w d2,d2 and.w #$3FF<<1,d2 rts .div0 move.w #256<<1,d2 rts |
![]() |
![]() |
#3 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
tidy of post fixes...
CorrectionTable: ;8 zones , b2 = steep , b1 = neg x , b0 = neg y dc.w 0 ;0 , 0..128 (shallow) base case 0..45 deg , 0...128 in my "angles" dc.w -1024 ;1, 896..1024 (neg y , shallow) dc.w -512 ;2, 384..512 dc.w 512 ;3, 512..640 dc.W -256 ;4, 128..256 dc.w 768 ;5, 768..896 dc.w 256 ;6, 256..384 dc.w -768 ;7, 640..768 AtanTable: dc.w 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 dc.w 10,10,11,12,12,13,13,14,15,15,16,17,17,18,19,19 dc.w 20,20,21,22,22,23,24,24,25,25,26,27,27,28,28,29 dc.w 30,30,31,32,32,33,33,34,35,35,36,36,37,38,38,39 dc.w 39,40,41,41,42,42,43,44,44,45,45,46,47,47,48,48 dc.w 49,49,50,51,51,52,52,53,53,54,55,55,56,56,57,57 dc.w 58,59,59,60,60,61,61,62,62,63,63,64,65,65,66,66 dc.w 67,67,68,68,69,69,70,70,71,71,72,72,73,74,74,75 dc.w 75,76,76,77,77,78,78,79,79,80,80,81,81,82,82,83 dc.w 83,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90 dc.w 91,91,91,92,92,93,93,94,94,95,95,95,96,96,97,97 dc.w 98,98,99,99,99,100,100,101,101,101,102,102,103,103,104,104 dc.w 104,105,105,106,106,106,107,107,108,108,108,109,109,110,110,110 dc.w 111,111,111,112,112,113,113,113,114,114,114,115,115,116,116,116 dc.w 117,117,117,118,118,118,119,119,119,120,120,121,121,121,122,122 dc.w 122,123,123,123,124,124,124,125,125,125,126,126,126,127,127,127 dc.w 128 GetATan2: ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 ;zone code tst.w d0 ;dy negative ? bpl.s .notnegY or.w #2,d2 ;b0<<1 neg.w d0 .notnegY tst.w d1 bpl.s .notnegX ;dx negative or.w #4,d2 ;b1<<1 neg.w d1 .notnegX cmp.w d1,d0 ;dy > dx blt.s .noswap exg d0,d1 ;swap so using dx/dy or.w #8,d2 ;b2<<1 .noswap ext.l d0 ;dont know what in high word of dx/dy lsl.w #8,d0 ;table is 256 deep tst.w d1 ;quick div by 0 check beq.s .div0 divu d1,d0 ;quotient is table index add.w d0,d0 ;which is words sized , so *2 lea.l AtanTable(pc),a4 move.w 0(a4,d0),d3 ;look up angle move.w (CorrectionTable-AtanTable)(a4,d2),d2 ;zone to correction values bpl.s .nocorrection neg.w d3 ;if neg , then negate retrieved angle , becomes -45...0 neg.w d2 ;remove neg-ness of correction basis .nocorrection add.w d3,d2 ;add correction add.w d2,d2 ;double result for word access and.w #$3FF<<1,d2 ;redundant...? rts .div0 move.w #256<<1,d2 rts |
![]() |
![]() |
#4 |
Registered User
Join Date: Jun 2015
Location: Germany
Posts: 1,961
|
Do you absolutely need to know the angle or just the left/right information? And what processor/hardware configuration are you aiming at?
|
![]() |
![]() |
#5 |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 56
Posts: 2,129
|
Test for div by 0 check can be at begining of code.
AtanTable can be byte table. And you can use move.w AtanTable(PC,D0),D3 After moving both tables at end of code. BTW. Still dont know why some coders prefers to use "lea.l" when exist only "lea", 2 bytes shortest. |
![]() |
![]() |
#6 | |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Quote:
|
|
![]() |
![]() |
#7 | |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Quote:
Targeting an A500 / OCS. |
|
![]() |
![]() |
#8 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
GetATan2:
;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 ;zone code tst.w d0 ;dy negative ? bpl.s .notnegY or.w #2,d2 ;b0<<1 neg.w d0 .notnegY tst.w d1 bpl.s .notnegX ;dx negative or.w #4,d2 ;b1<<1 neg.w d1 .notnegX cmp.w d1,d0 ;dy > dx blt.s .noswap exg d0,d1 ;swap so using dx/dy or.w #8,d2 ;b2<<1 .noswap ext.l d0 ;dont know what in high word of dx/dy lsl.w #8,d0 ;table is 256 deep tst.w d1 ;quick div by 0 check beq.s .div0 divu d1,d0 ;quotient is table index moveq #0,d3 move.b AtanTable(pc,d0),d3 ;look up angle move.w CorrectionTable(pc,d2),d2 ;zone to correction values bpl.s .nocorrection neg.w d3 ;if neg , then negate retrieved angle , becomes -45...0 neg.w d2 ;remove neg-ness of correction basis .nocorrection add.w d3,d2 ;add correction add.w d2,d2 ;double result for word access and.w #$3FF<<1,d2 ;redundant...? rts .div0 move.w #256<<1,d2 rts CorrectionTable: ;8 zones , b2 = steep , b1 = neg x , b0 = neg y dc.w 0 ;0 , 0..128 (shallow) base case 0..45 deg , 0...128 in my "angles" dc.w -1024 ;1, 896..1024 (neg y , shallow) dc.w -512 ;2, 384..512 dc.w 512 ;3, 512..640 dc.W -256 ;4, 128..256 dc.w 768 ;5, 768..896 dc.w 256 ;6, 256..384 dc.w -768 ;7, 640..768 AtanTable: dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 |
![]() |
![]() |
#9 |
ex. demoscener "Bigmama"
Join Date: Jun 2012
Location: Fyn / Denmark
Posts: 1,664
|
your or's can be exchanged to addq's - not that it matters much in the shadow of that divu.
|
![]() |
![]() |
#10 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
|
![]() |
![]() |
#11 |
Computer Nerd
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,964
|
Use code tags and formatting:
Code:
GetATan2: ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 ;zone code tst.w d0 ;dy negative ? bpl.s .notnegY or.w #2,d2 ;b0<<1 neg.w d0 .notnegY tst.w d1 bpl.s .notnegX ;dx negative or.w #4,d2 ;b1<<1 neg.w d1 .notnegX cmp.w d1,d0 ;dy > dx blt.s .noswap exg d0,d1 ;swap so using dx/dy or.w #8,d2 ;b2<<1 .noswap ext.l d0 ;dont know what in high word of dx/dy lsl.w #8,d0 ;table is 256 deep tst.w d1 ;quick div by 0 check beq.s .div0 divu d1,d0 ;quotient is table index moveq #0,d3 move.b AtanTable(pc,d0),d3 ;look up angle move.w CorrectionTable(pc,d2),d2 ;zone to correction values bpl.s .nocorrection neg.w d3 ;if neg , then negate retrieved angle , becomes -45...0 neg.w d2 ;remove neg-ness of correction basis .nocorrection add.w d3,d2 ;add correction add.w d2,d2 ;double result for word access and.w #$3FF<<1,d2 ;redundant...? rts .div0 move.w #256<<1,d2 rts CorrectionTable: ;8 zones , b2 = steep , b1 = neg x , b0 = neg y dc.w 0 ;0 , 0..128 (shallow) base case 0..45 deg , 0...128 in my "angles" dc.w -1024 ;1, 896..1024 (neg y , shallow) dc.w -512 ;2, 384..512 dc.w 512 ;3, 512..640 dc.W -256 ;4, 128..256 dc.w 768 ;5, 768..896 dc.w 256 ;6, 256..384 dc.w -768 ;7, 640..768 AtanTable: dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 ![]() |
![]() |
![]() |
#12 |
Registered User
Join Date: Jun 2016
Location: europe
Posts: 1,096
|
Start with:
Code:
clr.w d2 ;zone code tst.w d1 beq.s .div0 bpl.s .notnegX ;dx negative ... |
![]() |
![]() |
#13 | |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 56
Posts: 2,129
|
Quote:
Code:
GetATan2 ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 ;zone code tst.w d0 ;dy negative ? beq.b .div0 bpl.s .notnegY or.w #2,d2 ;b0<<1 neg.w d0 .notnegY tst.w d1 beq.b .div0 bpl.s .notnegX ;dx negative or.w #4,d2 ;b1<<1 neg.w d1 .notnegX cmp.w d1,d0 ;dy > dx blt.s .noswap exg d0,d1 ;swap so using dx/dy or.w #8,d2 ;b2<<1 .noswap ext.l d0 ;dont know what in high word of dx/dy lsl.w #8,d0 ;table is 256 deep ; tst.w d1 ;quick div by 0 check ; beq.s .div0 divu d1,d0 ;quotient is table index moveq #0,d3 move.b AtanTable(pc,d0),d3 ;look up angle move.w CorrectionTable(pc,d2),d2 ;zone to correction values bpl.s .nocorrection neg.w d3 ;if neg , then negate retrieved angle , becomes -45...0 neg.w d2 ;remove neg-ness of correction basis .nocorrection add.w d3,d2 ;add correction add.w d2,d2 ;double result for word access and.w #$3FF<<1,d2 ;redundant...? rts .div0 move.w #256<<1,d2 rts CorrectionTable: ;8 zones , b2 = steep , b1 = neg x , b0 = neg y dc.w 0 ;0 , 0..128 (shallow) base case 0..45 deg , 0...128 in my "angles" dc.w -1024 ;1, 896..1024 (neg y , shallow) dc.w -512 ;2, 384..512 dc.w 512 ;3, 512..640 dc.W -256 ;4, 128..256 dc.w 768 ;5, 768..896 dc.w 256 ;6, 256..384 dc.w -768 ;7, 640..768 AtanTable: dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 |
|
![]() |
![]() |
#14 | |
Registered User
Join Date: Jun 2015
Location: Germany
Posts: 1,961
|
Quote:
https://en.wikipedia.org/wiki/Cross_product Edited to add: this animation shows why the cross product is useful for what you are trying to do: https://de.wikipedia.org/wiki/Kreuzp...allelogram.gif |
|
![]() |
![]() |
#15 | |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Quote:
|
|
![]() |
![]() |
#16 | |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Quote:
Added... Code:
GetATan2: ;of d0= dy ; d1= dx ; returns in d2 , 0..$3FF << 1 clr.w d2 ;zone code tst.w d0 ;dy negative ? beq.s .div0 bpl.s .notnegY addq.w #2,d2 ;b0<<1 neg.w d0 .notnegY tst.w d1 beq.s .div0 bpl.s .notnegX ;dx negative addq.w #4,d2 ;b1<<1 neg.w d1 .notnegX cmp.w d1,d0 ;dy > dx blt.s .noswap exg d0,d1 ;swap so using dx/dy addq.w #8,d2 ;b2<<1 .noswap ext.l d0 ;dont know what in high word of dx/dy lsl.w #8,d0 ;table is 256 deep divu d1,d0 ;quotient is table index moveq #0,d3 move.b AtanTable(pc,d0),d3 ;look up angle move.w CorrectionTable(pc,d2),d2 ;zone to correction values bpl.s .nocorrection neg.w d3 ;if neg , then negate retrieved angle , becomes -45...0 neg.w d2 ;remove neg-ness of correction basis .nocorrection add.w d3,d2 ;add correction add.w d2,d2 ;double result for word access and.w #$3FF<<1,d2 ;redundant...? rts .div0 move.w #256<<1,d2 rts CorrectionTable: |
|
![]() |
![]() |
#17 |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
I'll take that early zero away , it's incorrect. dy==0 , could have a + or - dx leading to 0deg or 180deg , so we cant early out. equally dx==0 , could be 90 or 270 depending on the sign of dy.
|
![]() |
![]() |
#18 |
Registered User
Join Date: Jan 2008
Location: Warsaw/Poland
Age: 56
Posts: 2,129
|
Right.
For better registers usage, you can replace D3 with D1. |
![]() |
![]() |
#19 |
Registered User
Join Date: Apr 2023
Location: "Hamcastle"
Posts: 30
|
Another interesting way for determining the angle is for e.g. the CORDIC algorithm. An excellent explanation from Lund's Tekniska Högskolan can be found here:
http://www.eit.lth.se/fileadmin/eit/...or_Dummies.pdf ---- "What would "just left/right" look like ?" If I am not mistaken, you could make use of the cross product for that. You would need three x,y pairs (e.g. object A, object B, and centre of the screen) and depending on the sign of the calculated cross product, you would then know if object A is on the right or left of object B. Oops, just saw it was already covered by grond :-) |
![]() |
![]() |
#20 | |
Registered User
Join Date: Jan 2021
Location: UK
Posts: 13
|
Quote:
|
|
![]() |
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
"Voices8" 8 Channel Soundtracker "DemoSongI" song - "This is the Amiga with 8 Voices" | DemosongIHunter | request.Music | 45 | 23 May 2022 20:07 |
Most "recent" and "modern" IDE to write in C/C++/assembler on the Amiga? | guybrush | Coders. General | 11 | 27 February 2022 22:26 |
[FOUND: PHANTASIE III!] 2D RPG game with map screen, "black background combat screen" | Artifex 28 | Looking for a game name ? | 1 | 22 May 2014 17:42 |
RTG "full-screen" vs. "full windowed" - advantages/disadvantages? | ral-clan | support.WinUAE | 2 | 19 April 2011 01:15 |
The 6502 assembler code in "The Terminator" (1984) | Shoonay | Nostalgia & memories | 2 | 15 May 2009 13:52 |
|
|