nxu/fix16.tal

239 lines
9.1 KiB
Tal
Raw Normal View History

2022-03-13 23:23:20 -04:00
( fix16.tal )
( )
( use a signed 16-bit short as a fixed point number. )
( )
( numbers are interpreted as fractions with an implicit )
( 256 denominator. the upper byte is signed and )
( represents the "whole" part of the number, and the )
( lower byte is unsigned and represents the )
( "fractional" part of the number. )
( )
( 16-bit fixed point can represent fractional values )
( in the range -128 <= x < 128. the smallest fraction it )
( can represent is 1/256, which is about 0.004. )
( )
( SHORT FRACTION DECIMAL )
( #0000 0/256 0.000 )
( #0001 1/256 0.004 )
( #0002 2/256 0.008 )
( #0040 64/256 0.250 )
( #0080 128/256 0.500 )
( #0100 256/256 1.000 )
( #0700 1792/256 7.000 )
( #7f00 32512/256 127.000 )
( #7fff 32767/256 127.996 )
( #8000 -32768/256 -128.000 )
( #8001 -32767/256 -127.996 )
( #8100 -32767/256 -127.000 )
( #ff00 -256/256 -1.000 )
( #ffff -1/256 -0.004 )
( )
2022-03-13 23:28:13 -04:00
( many 8.8 operations are equivalent to uin16: )
2022-03-13 23:23:20 -04:00
( * addition/subtraction )
( * division )
2022-03-13 23:28:13 -04:00
( or signed int16: )
( * comparisons/equality )
2022-03-13 23:23:20 -04:00
( )
( but due to 16-bit truncation multiplication differs... )
( )
( x*y = x0*y0 + x0*y1/256 + x1*y0/256 + x1*y1/65536 )
( )
( since we only have 16-bits: )
( 1. we need to drop the 8 high bits from x0*y0 )
( 2. we need to drop the 8 low bits from x1*y1 )
( 3. we need to use all the bits from x0*y1 and x1*y0 )
( )
( that said, if either x or y is whole (i.e. ends in 00) )
( then we can just shift that argument right by 8 and use )
2022-03-13 23:28:13 -04:00
( MUL2. )
2022-03-13 23:23:20 -04:00
2022-03-17 23:04:40 -04:00
%xyz { ;x16-emit JSR2 #0a #18 DEO }
2022-03-13 23:23:20 -04:00
( useful constants )
( )
( to generate your own: )
( 1. take true value, e.g. 3.14159... )
( 2. multiply by 256 )
( 3. round to nearest whole number )
( 4. emit hex output )
( )
( in python: hex(round(x * 256)) )
%x16-zero { #0000 } ( 0.0 )
%x16-one { #0100 } ( 1.0 )
%x16-two { #0200 } ( 2.0 )
%x16-ten { #0a00 } ( 10.0 )
%x16-hundred { #6400 } ( 100.0 )
%x16-minus-one { #7f00 } ( -1.0 )
%x16-minus-two { #7e00 } ( -2.0 )
%x16-pi/2 { #0192 } ( 1.57079... )
%x16-pi { #0324 } ( 3.14159... )
%x16-pi*2 { #0648 } ( 6.28318... )
%x16-e { #02b8 } ( 2.71828... )
%x16-phi { #019e } ( 1.61803... )
%x16-sqrt-2 { #016a } ( 1.41421... )
%x16-sqrt-3 { #01bb } ( 1.73205... )
%x16-epsilon { #0001 } ( 0.00390625 )
%x16-minimum { #8000 } ( -128.0 )
%x16-maximum { #7fff } ( 127.99609375 )
( useful macros )
%x16-is-non-neg { x16-minimum LTH2 }
%x16-is-neg { x16-maximum GTH2 }
2022-03-17 23:04:40 -04:00
%x16-emit-dec { #30 ADD #18 DEO }
( |0100
x16-zero xyz
x16-one xyz
x16-two xyz
x16-ten xyz
x16-hundred xyz
x16-pi/2 xyz
x16-pi xyz
x16-pi*2 xyz
x16-e xyz
x16-phi xyz
x16-sqrt-2 xyz
x16-sqrt-3 xyz
x16-epsilon xyz
#1234 xyz
BRK )
@x16-emit ( x* -> )
SWP DUP #64 LTH ,&<100 JCN
#64 DIVk DUP x16-emit-dec MUL SUB ,&>=10 JMP
&<100 DUP #0a LTH ,&<10 JCN
&>=10 #0a DIVk DUP x16-emit-dec MUL SUB
&<10 x16-emit-dec
LIT '. #18 DEO
( emit fractional part )
#00 SWP ( lo* )
#000a MUL2 #0100 DIV2k DUP2 NIP x16-emit-dec MUL2 SUB2
#000a MUL2 #0100 DIV2k DUP2 NIP x16-emit-dec MUL2 SUB2
#000a MUL2 #0100 DIV2k STH2k MUL2 SUB2 #0080 LTH2 ,&no-round JCN INC2r
&no-round STH2r NIP x16-emit-dec JMP2r
2022-03-13 23:23:20 -04:00
( comparison between x and y. )
( - ff: x < y )
( - 00: x = y )
( - 01: x > y )
@x16-cmp ( x* y* -> c^ )
STH2k x16-is-neg ,&yn JMP
x16-is-non-neg ,&ypxp ( y>=0 )
POP2 POP2r #ff JMP2r ( x<0 y>=0 )
&ypxp ;x16-ucmp JMP2 ( x>=0 y>=0 )
&yn x16-is-neg ,&ynxn ( y<0 )
POP2 POP2r #01 JMP2r ( x>=0 y<0 )
&ynxn SWP2 ;x16-ucmp JMP2 ( x<0 y<0 )
( unsigned comparison between x and y. )
( - ff: x < y )
( - 00: x = y )
( - 01: x > y )
@x16-ucmp ( x* y* -> c^ )
LTH2k ,&lt JCN GTH2 JMP2r
&lt POP2 POP2 #ff JMP2r
2022-03-17 23:04:40 -04:00
@x16-eq ( x* y* -> x=y^ ) EQU2 JMP2r
@x16-ne ( x* y* -> x!=0^ ) NEQ2 JMP2r
@x16-lt ( x* y* -> x<y^ ) ;x16-cmp JSR2 #ff EQU JMP2r
@x16-lteq ( x* y* -> x<y^ ) ;x16-cmp JSR2 #01 NEQ JMP2r
@x16-gt ( x* y* -> x<y^ ) ;x16-cmp JSR2 #01 EQU JMP2r
@x16-gteq ( x* y* -> x<y^ ) ;x16-cmp JSR2 #ff NEQ JMP2r
2022-03-13 23:23:20 -04:00
@x16-is-whole ( x* -> bool^ )
NIP #00 EQU JMP2r
@x16-add ( x* y* -> x+y* )
ADD2 JMP2r
@x16-sub ( x* y* -> x-y* )
SUB2 JMP2r
@x16-negate ( x* -> -x* )
#0000 SWP2 SUB2 JMP2r
@x16-mul ( x* y* -> xy* )
DUP #00 EQU ,&rhs-whole JCN
SWP2 DUP #00 EQU ,&rhs-whole JCN
,&y3 STR ,&y1 STR ,&x3 STR ,&x1 STR
LIT2 &x2 00 &x3 00 LIT2 &y2 00 &y3 00 MUL2 #08 SFT2
LIT2 &x0 00 &x1 00 ,&y2 LDR2 MUL2 ADD2
,&x2 LDR2 LIT2 &y0 00 &y1 00 MUL2 ADD2
,&x0 LDR2 ,&x0 LDR2 MUL2 #80 SFT2 ADD2 JMP2r
&rhs-whole #08 SFT2 MUL2 JMP2r
@x16-div ( x* y* -> x/y* )
SWP2 DUP2 x16-is-non-neg ,&non-negative ( y x )
;x16-negate JSR2 SWP2 DIV2 ;x16-negate JMP2
&non-negative
SWP2 DIV2 JMP2r
@x16-mod ( x* y* -> x%y* )
;x16-div JSR2 ;x16-mul JSR2 SUB2 JMP2r
@x16-mod-div ( x* y* -> x%y* x/y* )
;x16-div JSR2 STH2k ;x16-mul JSR2 SUB2 STH2r JMP2r
@x16-div-mod ( x* y* -> x/y* x%y* )
;x16-mod-div JSR2 SWP2 JMP2r
2022-03-14 00:00:01 -04:00
( trigonometry )
( )
( this uses there different angle representations: )
( )
( 1. angle representation (#0080 = pi, #0100 = 2pi) )
( 2. fix16 representation (#0324 = pi, #0648 = 2pi) )
( 3. degrees (#00b4 = pi, #0168 = 2pi) )
( )
( angles are the most precise, but may require some )
( conversion. )
( calculate the sin of the given angle. )
( )
( the input should be an angle and the )
( result will be 16-bit fixed point. )
( )
( cos(x) = sin(x + pi/2) )
@cos-angle ( x* -> cos(x)* )
#0080 ADD2 ;sin-angle JMP2
( calculate the sin of the given angle. )
( )
( the input should be an angle and the )
( result will be 16-bit fixed point. )
( )
( sin table offset math: )
( 0 <= x < 64 -> table[x] )
( 64 <= x < 128 -> table[127 - x] )
( 128 <= x < 192 -> -table[x - 128] )
( 192 <= x < 256 -> -table[255 - x] )
@sin-angle ( x* -> sin(x)* )
NIP DUP #7f GTH ,&d180+ JCN
#0001 STH2
DUP #3f GTH ,&d90 ,&load JMP
&d90 #7f SWP SUB ,&load JMP
&d180+
#ffff STH2
DUP #bf GTH ,&d270 #80 SUB ,&load JMP
&d270 #ff SWP SUB ,&load JMP
&load ,sin-table ADD LDR2 STH2r MUL2 JMP2r
( sin table with 64 entries for 1/4 of a unit circle. )
( )
( there is no need for interpolation when using )
( angle values, since 256 distinct values can be )
( produced using reflection. )
( )
( these table values go from 0 until pi/2 (i.e. from )
( angles #00 until #80). )
@sin-table ( 0 - pi/2: 64 steps )
0000 0006 000d 0013 0019 001f 0026 002c
0032 0038 003e 0044 004a 0050 0056 005c
0062 0068 006d 0073 0079 007e 0084 0089
008e 0093 0098 009d 00a2 00a7 00ac 00b1
00b5 00b9 00be 00c2 00c6 00ca 00ce 00d1
00d5 00d8 00dc 00df 00e2 00e5 00e7 00ea
00ed 00ef 00f1 00f3 00f5 00f7 00f8 00fa
00fb 00fc 00fd 00fe 00ff 00ff 0100 0100