From ff7f3484e4687b42c02580e1cb0d4cd639d2dc44 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 17 Jun 2023 15:36:53 +0200 Subject: [PATCH] atan --- compiler/res/prog8lib/math.p8 | 13 ++-- compiler/res/prog8lib/virtual/math.p8 | 69 +++++++++-------- docs/source/libraries.rst | 18 ++--- docs/source/todo.rst | 1 - examples/test.p8 | 75 ++++++++++++------- virtualmachine/src/prog8/vm/SysCalls.kt | 14 ++-- .../src/prog8/vm/VmProgramLoader.kt | 2 +- 7 files changed, 109 insertions(+), 83 deletions(-) diff --git a/compiler/res/prog8lib/math.p8 b/compiler/res/prog8lib/math.p8 index 2784ae29e..84105d0d1 100644 --- a/compiler/res/prog8lib/math.p8 +++ b/compiler/res/prog8lib/math.p8 @@ -97,7 +97,7 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0))) } -sub atan_coarse_sgn(byte x1, byte y1, byte x2, byte y2) -> ubyte { +sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte { ; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A. cx16.r0L = 3 ; quadrant cx16.r1sL = x2-x1 ; xdelta @@ -110,10 +110,10 @@ sub atan_coarse_sgn(byte x1, byte y1, byte x2, byte y2) -> ubyte { cx16.r0L-=2 cx16.r2sL = -cx16.r2sL } - return atan_coarse_qd(cx16.r0L, cx16.r1L, cx16.r2L) + return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L) } -sub atan_coarse(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { +sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { ; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A. cx16.r0L = 3 ; quadrant if x2>=x1 { @@ -128,10 +128,10 @@ sub atan_coarse(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { cx16.r2L = y1-y2 cx16.r0L -= 2 } - return atan_coarse_qd(cx16.r0L, cx16.r1L, cx16.r2L) + return direction_qd(cx16.r0L, cx16.r1L, cx16.r2L) } -asmsub atan_coarse_qd(ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A { +asmsub direction_qd(ubyte quadrant @A, ubyte xdelta @X, ubyte ydelta @Y) -> ubyte @A { ;Arctan https://github.com/dustmop/arctan24 ; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23 into A. ; .reg:a @in quadrant Number 0 to 3. @@ -269,6 +269,7 @@ y2 = cx16.r3L octant = cx16.r4L ;; temporary zeropage variable lda x1 + sec sbc x2 bcs *+4 eor #$ff @@ -276,6 +277,7 @@ octant = cx16.r4L ;; temporary zeropage variable rol octant lda y1 + sec sbc y2 bcs *+4 eor #$ff @@ -283,6 +285,7 @@ octant = cx16.r4L ;; temporary zeropage variable rol octant lda log2_tab,x + sec sbc log2_tab,y bcc *+4 eor #$ff diff --git a/compiler/res/prog8lib/virtual/math.p8 b/compiler/res/prog8lib/virtual/math.p8 index cad475f2f..cd64824b5 100644 --- a/compiler/res/prog8lib/virtual/math.p8 +++ b/compiler/res/prog8lib/virtual/math.p8 @@ -184,43 +184,46 @@ math { } -sub atan_coarse_sgn(byte x1, byte y1, byte x2, byte y2) -> ubyte { - ; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A. - cx16.r0L = 3 ; quadrant - cx16.r1sL = x2-x1 ; xdelta - if_neg { - cx16.r0L-- - cx16.r1sL = -cx16.r1sL - } - cx16.r2sL = y2-y1 ; ydelta - if_neg { - cx16.r0L-=2 - cx16.r2sL = -cx16.r2sL - } - return atan_coarse_qd(cx16.r0L, cx16.r1L, cx16.r2L) -} - -sub atan_coarse(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { +sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { ; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A. - cx16.r0L = 3 ; quadrant - if x2>=x1 { - cx16.r1L = x2-x1 - } else { - cx16.r1L = x1-x2 - cx16.r0L-- - } - if y2>=y1 { - cx16.r2L = y2-y1 - } else { - cx16.r2L = y1-y2 - cx16.r0L -= 2 - } - return atan_coarse_qd(cx16.r0L, cx16.r1L, cx16.r2L) + ; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary + ubyte angle = atan(x1, y1, x2, y2) - 256/48 + return 23-lsb(mkword(angle,0) / 2730) } -sub atan_coarse_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte { +sub direction_sc(byte x1, byte y1, byte x2, byte y2) -> ubyte { + ; From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23 into A. + ; shift the points into the positive quadrant + ubyte px1 + ubyte py1 + ubyte px2 + ubyte py2 + if x1<0 or x2<0 { + px1 = x1 as ubyte + 128 + px2 = x2 as ubyte + 128 + } else { + px1 = x1 as ubyte + px2 = x2 as ubyte + } + if y1<0 or y2<0 { + py1 = y1 as ubyte + 128 + py2 = y2 as ubyte + 128 + } else { + py1 = y1 as ubyte + py2 = y2 as ubyte + } + + return direction(px1, py1, px2, py2) +} + +sub direction_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta) -> ubyte { ; From a pair of X/Y deltas (both >=0), and quadrant 0-3, calculate discrete direction between 0 and 23. - return lsb(mkword(atan(0, 0, xdelta, ydelta), 0) / 2730) + when quadrant { + 3 -> return direction(0, 0, xdelta, ydelta) + 2 -> return direction(xdelta, 0, 0, ydelta) + 1 -> return direction(0, ydelta, xdelta, 0) + else -> return direction(xdelta, ydelta, 0, 0) + } } sub atan(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index ced1a386f..1e77e0207 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -372,21 +372,21 @@ but perhaps the provided ones can be of service too. Fast 8-bit byte cosine of angle 0..179 (each is a 2 degree step), result is in range -127..127 Angles 180..255 will yield a garbage result! -``atan(ubyte x1, ubyte y1, ubyte x2, ubyte y2)`` +``atan (ubyte x1, ubyte y1, ubyte x2, ubyte y2)`` Fast arctan routine that uses more memory because of large lookup tables. Calculate the angle, in a 256-degree circle, between two points in the positive quadrant. -``atan_coarse_sgn(byte x1, byte y1, byte x2, byte y2)`` - Small and fast, but imprecise, arctan routine - From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23. - -``atan_coarse(ubyte x1, ubyte y1, ubyte x2, ubyte y2)`` - Small and fast, but imprecise, arctan routine +``direction (ubyte x1, ubyte y1, ubyte x2, ubyte y2)`` From a pair of positive coordinates, calculate discrete direction between 0 and 23. + This is a heavily optimized routine (small and fast). -``atan_coarse_qd(ubyte quadrant, ubyte xdelta, ubyte ydelta)`` - Small and fast, but imprecise, arctan routine +``direction_sc (byte x1, byte y1, byte x2, byte y2)`` + From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23. + This is a heavily optimized routine (small and fast). + +``direction_qd (ubyte quadrant, ubyte xdelta, ubyte ydelta)`` If you already know the quadrant and x/y deltas, calculate discrete direction between 0 and 23. + This is a heavily optimized routine (small and fast). diff --git a/docs/source/todo.rst b/docs/source/todo.rst index c0b4ca968..652550ecf 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,7 +1,6 @@ TODO ==== -- vm: fix syscall.ATAN calculation - document some library modules better (diskio, etc) ... diff --git a/examples/test.p8 b/examples/test.p8 index 81ca29a9f..3b04995c9 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,43 +2,66 @@ %import textio %zeropage basicsafe -main { +main { + + const ubyte WIDTH=255 + const ubyte HEIGHT=240 sub start() { + sys.gfx_enable(0) ; enable lo res screen + ;; gfx2.screen_mode(4) + repeat { - const ubyte HEIGHT = 30 ; txt.DEFAULT_HEIGHT-1 - const ubyte WIDTH = 80 ; txt.DEFAULT_WIDTH-1 - const ubyte HALFWIDTH = 40 ; txt.DEFAULT_WIDTH/2 - const ubyte HALFHEIGHT = 15 ; txt.DEFAULT_HEIGHT/2 + ubyte xx + ubyte yy - txt.print_ub(math.atan(0, 0, 10, 20)) + for yy in 0 to HEIGHT-1 { + for xx in 0 to WIDTH-1 { + ubyte value = math.direction(WIDTH/2, HEIGHT/2, xx, yy) + ;; gfx2.plot(xx,yy,value) + sys.gfx_plot(xx, yy, value*10) + } + } + } + } +} + +;main { +; +; sub start() { +; +; const ubyte HEIGHT = txt.DEFAULT_HEIGHT +; const ubyte WIDTH = txt.DEFAULT_WIDTH +; const ubyte HALFWIDTH = txt.DEFAULT_WIDTH/2 +; const ubyte HALFHEIGHT = txt.DEFAULT_HEIGHT/2 +; ; ubyte @zp value ; ubyte xx ; ubyte yy -; for yy in 0 to HEIGHT { -; for xx in 0 to WIDTH { -; value = math.atan(HALFWIDTH, HALFHEIGHT, xx, yy) -; txt.setchr(xx,yy,value) -; } -; } +;; for yy in 0 to HEIGHT-1 { +;; for xx in 0 to WIDTH-1 { +;; value = math.atan(HALFWIDTH, HALFHEIGHT, xx, yy) +;; txt.setchr(xx,yy,value) +;; } +;; } +;; +;; byte sx +;; byte sy +;; for sy in -HEIGHT/2 to HEIGHT/2 { +;; for sx in -WIDTH/2 to WIDTH/2 { +;; value = math.direction_sc(0, 0, sx, sy) +;; txt.setchr(sx+WIDTH/2 as ubyte,sy+HEIGHT/2 as ubyte,value) +;; } +;; } ; -; byte sx -; byte sy -; for sy in 0 to HEIGHT as byte { -; for sx in 0 to WIDTH as byte { -; value = math.atan_coarse_sgn(0, 0, sx-HALFWIDTH, sy-HALFHEIGHT) -; txt.setchr(sx as ubyte,sy as ubyte,value) -; } -; } -; -; for yy in 0 to HEIGHT { -; for xx in 0 to WIDTH { -; value = math.atan_coarse(HALFWIDTH, HALFHEIGHT, xx, yy) +; for yy in 0 to HEIGHT-1 { +; for xx in 0 to WIDTH-1 { +; value = math.direction(HALFWIDTH, HALFHEIGHT, xx, yy) ; txt.setchr(xx,yy,value) ; } ; } ; ; goto start - } -} +; } +;} diff --git a/virtualmachine/src/prog8/vm/SysCalls.kt b/virtualmachine/src/prog8/vm/SysCalls.kt index ab22a4015..11ba19285 100644 --- a/virtualmachine/src/prog8/vm/SysCalls.kt +++ b/virtualmachine/src/prog8/vm/SysCalls.kt @@ -3,8 +3,7 @@ package prog8.vm import prog8.code.core.AssemblyError import prog8.intermediate.FunctionCallArgs import prog8.intermediate.IRDataType -import kotlin.math.max -import kotlin.math.min +import kotlin.math.* /* SYSCALLS: @@ -479,12 +478,11 @@ object SysCalls { val y1f = (y1 as UByte).toDouble() val x2f = (x2 as UByte).toDouble() val y2f = (y2 as UByte).toDouble() - val xd = x2f-x1f - val yd = y2f-y1f - TODO("calculate atan the same way as the 6502 routine does 0-255") -// val radians = atan2(yd, xd) + PI // 0 to 2*PI -// val result = floor(2*PI/radians*256.0) -// returnValue(callspec.returns!!, result, vm) + var radians = atan2(y2f-y1f, x2f-x1f) + if(radians<0) + radians+=2*PI + val result = floor(radians/2.0/PI*256.0) + returnValue(callspec.returns!!, result, vm) } else -> throw AssemblyError("missing syscall ${call.name}") } diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index 8d97f3599..7f3007e6e 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -201,7 +201,7 @@ class VmProgramLoader { program.st.allVariables().forEach { variable -> var addr = allocations.allocations.getValue(variable.name) - // zero out uninitialized variables. + // zero out uninitialized ('bss') variables. if(variable.uninitialized) { if(variable.dt in ArrayDatatypes) { repeat(variable.length!!) {