diff --git a/fix16.tal b/fix16.tal index e468183..c15ee1f 100644 --- a/fix16.tal +++ b/fix16.tal @@ -67,6 +67,26 @@ ( - log() is supported ) ( - log(0) throws an error ) ( - log values farther from zero may be a bit inaccurate ) +( ) +( rounding, truncation, conversion: ) +( ) +( - x16-to-s16: converts the whole to signed 16-bit, ) +( discarding the fractional part. the ) +( magnitude of numbers is never increased. ) +( ) +( - x16-to-s8: same as x16-to-s16 but returns one byte. ) +( ) +( - x16-ceil: returns a x16 value with any fractional ) +( part rounded up. ) +( ) +( - x16-floor: returns a x16 value with any fractional ) +( part rounded down. ) +( ) +( - x16-round: returns a x16 value with any fractional ) +( part rounded up or down depending on the ) +( fractional part. in the case of numbers ) +( with a fractional part of 0.5, they will ) +( round towards the nearest even value. ) ( useful macros ) %x16-is-non-neg { x16-minimum LTH2 } @@ -199,6 +219,35 @@ @x16-remainder ( x* y* -> x%y* ) DIV2k MUL2 SUB2 JMP2r +( 1.5 -> 1, 0.5 -> 0, -1.5 -> -1 ) +@x16-to-s16 ( x* -> whole* ) + DUP2 #7fff GTH2 ,&neg JCN ( x0 x1 ) + DUP EOR ( x0 00 ) + SWP JMP2r ( 0 x0 ) + &neg #00ff STHk ( x0 x1 00 ff [ff] ) + ADD2 POP ( x0' [ff] ) + DUP #07 SFT STHr ( x0' s' ff ) + MUL SWP JMP2r ( 00-or-ff x0' ) + +( 1.5 -> 1, 0.5 -> 0, -1.5 -> -1 ) +@x16-to-s8 ( x* -> whole^ ) + DUP2 #0f SFT2 #00ff MUL2 ADD2 POP JMP2r + +( e.g. #0812 -> #0800 ) +@x16-floor ( x* -> floor(x)* ) + DUP EOR JMP2r + +( e.g. #0812 -> #0900 ) +@x16-ceil ( x* -> floor(x)* ) + #00ff ADD2 DUP EOR JMP2r + +( round half-even, #0080 -> #0000, #0180 -> #0200 ) +@x16-round ( x* -> round(x)* ) + OVR #01 AND ,&odd JCN + ( even ) #007f ,&rest JMP + &odd #0080 + &rest ADD2 DUP EOR JMP2r + @x16-cos ( x* -> cos(x)* ) x16-pi/2 ADD2 ,x16-sin JMP diff --git a/test-fix16.py b/test-fix16.py index c47d3ca..e76e25e 100644 --- a/test-fix16.py +++ b/test-fix16.py @@ -1,6 +1,6 @@ #!/usr/bin/python -from math import floor, log, sin, cos, tan, copysign +from math import ceil, copysign, cos, floor, log, sin, tan from os import environ from random import randint from subprocess import Popen, PIPE, run @@ -126,9 +126,26 @@ def x16_tan(x): def x16_log(x): z = round(log(x / 256) * 256) return z if z >= 0 else 65536 + z +def x16_floor(x): + return floor(x / 256) * 256 +def x16_ceil(x): + return (ceil(x / 256) * 256) % 65536 +def x16_round(x): + return (round(x / 256) * 256) % 65536 +def x16_trunc16(x): + r = tosigned(x) / 256 + if r < 0: + return (65536 + ceil(r)) % 65536 + else: + return floor(x / 256) +def x16_trunc8(x): + if tosigned(x) < 0: + return ceil(x / 256) % 256 + else: + return floor(x / 256) def main(): - trials = 10000 + trials = 1000 run(['uxnasm', 'test-fix16.tal', 'run.rom']) p = pipe() test(p, trials, b'+', [('x', u16), ('y', u16)], u16, x16_add) @@ -149,6 +166,11 @@ def main(): test(p, trials, b'c', [('x', p16)], u16, x16_cos, eq=abseq) test(p, trials, b't', [('x', t16)], u16, x16_tan, eq=taneq) test(p, trials, b'l', [('x', p16)], u16, x16_log, eq=releq) + test(p, trials, b'F', [('x', u16)], u16, x16_floor) + test(p, trials, b'C', [('x', u16)], u16, x16_ceil) + test(p, trials, b'R', [('x', u16)], u16, x16_round) + test(p, trials, b'8', [('x', u16)], u16, x16_trunc8) + test(p, trials, b'T', [('x', u16)], u16, x16_trunc16) p.stdin.write(b'\n\n') p.stdin.flush() p.stdin.close() diff --git a/test-fix16.tal b/test-fix16.tal index e91e652..d6ca8b8 100644 --- a/test-fix16.tal +++ b/test-fix16.tal @@ -68,6 +68,11 @@ ;buf LDA LIT "c EQU ;test-x16-cos JCN2 ;buf LDA LIT "t EQU ;test-x16-tan JCN2 ;buf LDA LIT "l EQU ;test-x16-log JCN2 + ;buf LDA LIT "F EQU ;test-x16-floor JCN2 + ;buf LDA LIT "C EQU ;test-x16-ceil JCN2 + ;buf LDA LIT "R EQU ;test-x16-round JCN2 + ;buf LDA LIT "T EQU ;test-x16-to-s16 JCN2 + ;buf LDA LIT "8 EQU ;test-x16-to-s8 JCN2 LIT "? #18 DEO #0a #18 DEO ;reset JSR2 BRK ( set the interpreter to exit now ) @@ -93,25 +98,30 @@ ( different test executors ) ( ) -( TEST-NAME #IN #OUT WORD-TO-TEST HARNESS ) -@test-x16-add #04 #02 ;x16-add ;test JMP2 -@test-x16-mul #04 #02 ;x16-mul ;test JMP2 -@test-x16-sub #04 #02 ;x16-sub ;test JMP2 -@test-x16-div #04 #02 ;x16-div ;test JMP2 -@test-x16-quotient #04 #02 ;x16-quotient ;test JMP2 -@test-x16-remainder #04 #02 ;x16-remainder ;test JMP2 -@test-x16-is-whole #02 #01 ;x16-is-whole ;test JMP2 -@test-x16-negate #02 #02 ;x16-negate ;test JMP2 -@test-x16-eq #04 #01 ;x16-eq ;test JMP2 -@test-x16-ne #04 #01 ;x16-ne ;test JMP2 -@test-x16-lt #04 #01 ;x16-lt ;test JMP2 -@test-x16-lteq #04 #01 ;x16-lteq ;test JMP2 -@test-x16-gt #04 #01 ;x16-gt ;test JMP2 -@test-x16-gteq #04 #01 ;x16-gteq ;test JMP2 -@test-x16-sin #02 #02 ;x16-sin ;test JMP2 -@test-x16-cos #02 #02 ;x16-cos ;test JMP2 -@test-x16-tan #02 #02 ;x16-tan ;test JMP2 -@test-x16-log #02 #02 ;x16-log ;test JMP2 +( TEST-NAME #IN #OUT WORD-TO-TEST HARNESS ) +@test-x16-add #04 #02 ;x16-add ;test JMP2 +@test-x16-mul #04 #02 ;x16-mul ;test JMP2 +@test-x16-sub #04 #02 ;x16-sub ;test JMP2 +@test-x16-div #04 #02 ;x16-div ;test JMP2 +@test-x16-quotient #04 #02 ;x16-quotient ;test JMP2 +@test-x16-remainder #04 #02 ;x16-remainder ;test JMP2 +@test-x16-is-whole #02 #01 ;x16-is-whole ;test JMP2 +@test-x16-negate #02 #02 ;x16-negate ;test JMP2 +@test-x16-eq #04 #01 ;x16-eq ;test JMP2 +@test-x16-ne #04 #01 ;x16-ne ;test JMP2 +@test-x16-lt #04 #01 ;x16-lt ;test JMP2 +@test-x16-lteq #04 #01 ;x16-lteq ;test JMP2 +@test-x16-gt #04 #01 ;x16-gt ;test JMP2 +@test-x16-gteq #04 #01 ;x16-gteq ;test JMP2 +@test-x16-sin #02 #02 ;x16-sin ;test JMP2 +@test-x16-cos #02 #02 ;x16-cos ;test JMP2 +@test-x16-tan #02 #02 ;x16-tan ;test JMP2 +@test-x16-log #02 #02 ;x16-log ;test JMP2 +@test-x16-floor #02 #02 ;x16-floor ;test JMP2 +@test-x16-ceil #02 #02 ;x16-ceil ;test JMP2 +@test-x16-round #02 #02 ;x16-round ;test JMP2 +@test-x16-to-s16 #02 #02 ;x16-to-s16 ;test JMP2 +@test-x16-to-s8 #02 #01 ;x16-to-s8 ;test JMP2 ( reads one byte from ASCII: "13" -> 0x13 ) @read-byte ( c0^ c1^ -> n^ )