; Internal Math library routines - always included by the compiler ; note: some functions you might expect here are builtin functions, ; such as abs, sqrt, clamp, min, max for example. math { %option ignore_unused sub sin8u(ubyte angle) -> ubyte { ubyte[256] sintab = [$80, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9, $bc, $be, $c1, $c4, $c6, $c9, $cb, $ce, $d0, $d3, $d5, $d7, $da, $dc, $de, $e0, $e2, $e4, $e6, $e8, $ea, $eb, $ed, $ee, $f0, $f1, $f3, $f4, $f5, $f6, $f8, $f9, $fa, $fa, $fb, $fc, $fd, $fd, $fe, $fe, $fe, $ff, $ff, $ff, $ff, $ff, $ff, $ff, $fe, $fe, $fe, $fd, $fd, $fc, $fb, $fa, $fa, $f9, $f8, $f6, $f5, $f4, $f3, $f1, $f0, $ee, $ed, $eb, $ea, $e8, $e6, $e4, $e2, $e0, $de, $dc, $da, $d7, $d5, $d3, $d0, $ce, $cb, $c9, $c6, $c4, $c1, $be, $bc, $b9, $b6, $b3, $b0, $ad, $aa, $a7, $a5, $a2, $9e, $9b, $98, $95, $92, $8f, $8c, $89, $86, $83, $80, $7c, $79, $76, $73, $70, $6d, $6a, $67, $64, $61, $5d, $5a, $58, $55, $52, $4f, $4c, $49, $46, $43, $41, $3e, $3b, $39, $36, $34, $31, $2f, $2c, $2a, $28, $25, $23, $21, $1f, $1d, $1b, $19, $17, $15, $14, $12, $11, $0f, $0e, $0c, $0b, $0a, $09, $07, $06, $05, $05, $04, $03, $02, $02, $01, $01, $01, $00, $00, $00, $00, $00, $00, $00, $01, $01, $01, $02, $02, $03, $04, $05, $05, $06, $07, $09, $0a, $0b, $0c, $0e, $0f, $11, $12, $14, $15, $17, $19, $1b, $1d, $1f, $21, $23, $25, $28, $2a, $2c, $2f, $31, $34, $36, $39, $3b, $3e, $41, $43, $46, $49, $4c, $4f, $52, $55, $58, $5a, $5d, $61, $64, $67, $6a, $6d, $70, $73, $76, $79, $7c] return sintab[angle] } sub cos8u(ubyte angle) -> ubyte { ubyte[256] costab = [$ff, $ff, $ff, $ff, $fe, $fe, $fe, $fd, $fd, $fc, $fb, $fa, $fa, $f9, $f8, $f6, $f5, $f4, $f3, $f1, $f0, $ee, $ed, $eb, $ea, $e8, $e6, $e4, $e2, $e0, $de, $dc, $da, $d7, $d5, $d3, $d0, $ce, $cb, $c9, $c6, $c4, $c1, $be, $bc, $b9, $b6, $b3, $b0, $ad, $aa, $a7, $a5, $a2, $9e, $9b, $98, $95, $92, $8f, $8c, $89, $86, $83, $80, $7c, $79, $76, $73, $70, $6d, $6a, $67, $64, $61, $5d, $5a, $58, $55, $52, $4f, $4c, $49, $46, $43, $41, $3e, $3b, $39, $36, $34, $31, $2f, $2c, $2a, $28, $25, $23, $21, $1f, $1d, $1b, $19, $17, $15, $14, $12, $11, $0f, $0e, $0c, $0b, $0a, $09, $07, $06, $05, $05, $04, $03, $02, $02, $01, $01, $01, $00, $00, $00, $00, $00, $00, $00, $01, $01, $01, $02, $02, $03, $04, $05, $05, $06, $07, $09, $0a, $0b, $0c, $0e, $0f, $11, $12, $14, $15, $17, $19, $1b, $1d, $1f, $21, $23, $25, $28, $2a, $2c, $2f, $31, $34, $36, $39, $3b, $3e, $41, $43, $46, $49, $4c, $4f, $52, $55, $58, $5a, $5d, $61, $64, $67, $6a, $6d, $70, $73, $76, $79, $7c, $7f, $83, $86, $89, $8c, $8f, $92, $95, $98, $9b, $9e, $a2, $a5, $a7, $aa, $ad, $b0, $b3, $b6, $b9, $bc, $be, $c1, $c4, $c6, $c9, $cb, $ce, $d0, $d3, $d5, $d7, $da, $dc, $de, $e0, $e2, $e4, $e6, $e8, $ea, $eb, $ed, $ee, $f0, $f1, $f3, $f4, $f5, $f6, $f8, $f9, $fa, $fa, $fb, $fc, $fd, $fd, $fe, $fe, $fe, $ff, $ff, $ff ] return costab[angle] } sub sin8(ubyte angle) -> byte { ubyte[256] sintab = [ $00, $03, $06, $09, $0c, $0f, $12, $15, $18, $1b, $1e, $21, $24, $27, $2a, $2d, $30, $33, $36, $39, $3b, $3e, $41, $43, $46, $49, $4b, $4e, $50, $52, $55, $57, $59, $5b, $5e, $60, $62, $64, $66, $67, $69, $6b, $6c, $6e, $70, $71, $72, $74, $75, $76, $77, $78, $79, $7a, $7b, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7e, $7e, $7f, $7e, $7e, $7e, $7e, $7e, $7d, $7d, $7c, $7b, $7b, $7a, $79, $78, $77, $76, $75, $74, $72, $71, $70, $6e, $6c, $6b, $69, $67, $66, $64, $62, $60, $5e, $5b, $59, $57, $55, $52, $50, $4e, $4b, $49, $46, $43, $41, $3e, $3b, $39, $36, $33, $30, $2d, $2a, $27, $24, $21, $1e, $1b, $18, $15, $12, $0f, $0c, $09, $06, $03, $00, $fd, $fa, $f7, $f4, $f1, $ee, $eb, $e8, $e5, $e2, $df, $dc, $d9, $d6, $d3, $d0, $cd, $ca, $c7, $c5, $c2, $bf, $bd, $ba, $b7, $b5, $b2, $b0, $ae, $ab, $a9, $a7, $a5, $a2, $a0, $9e, $9c, $9a, $99, $97, $95, $94, $92, $90, $8f, $8e, $8c, $8b, $8a, $89, $88, $87, $86, $85, $85, $84, $83, $83, $82, $82, $82, $82, $82, $81, $82, $82, $82, $82, $82, $83, $83, $84, $85, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8e, $8f, $90, $92, $94, $95, $97, $99, $9a, $9c, $9e, $a0, $a2, $a5, $a7, $a9, $ab, $ae, $b0, $b2, $b5, $b7, $ba, $bd, $bf, $c2, $c5, $c7, $ca, $cd, $d0, $d3, $d6, $d9, $dc, $df, $e2, $e5, $e8, $eb, $ee, $f1, $f4, $f7, $fa, $fd ] return sintab[angle] as byte } sub cos8(ubyte angle) -> byte { ubyte[256] costab = [ $7f, $7e, $7e, $7e, $7e, $7e, $7d, $7d, $7c, $7b, $7b, $7a, $79, $78, $77, $76, $75, $74, $72, $71, $70, $6e, $6c, $6b, $69, $67, $66, $64, $62, $60, $5e, $5b, $59, $57, $55, $52, $50, $4e, $4b, $49, $46, $43, $41, $3e, $3b, $39, $36, $33, $30, $2d, $2a, $27, $24, $21, $1e, $1b, $18, $15, $12, $0f, $0c, $09, $06, $03, $00, $fd, $fa, $f7, $f4, $f1, $ee, $eb, $e8, $e5, $e2, $df, $dc, $d9, $d6, $d3, $d0, $cd, $ca, $c7, $c5, $c2, $bf, $bd, $ba, $b7, $b5, $b2, $b0, $ae, $ab, $a9, $a7, $a5, $a2, $a0, $9e, $9c, $9a, $99, $97, $95, $94, $92, $90, $8f, $8e, $8c, $8b, $8a, $89, $88, $87, $86, $85, $85, $84, $83, $83, $82, $82, $82, $82, $82, $81, $82, $82, $82, $82, $82, $83, $83, $84, $85, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8e, $8f, $90, $92, $94, $95, $97, $99, $9a, $9c, $9e, $a0, $a2, $a5, $a7, $a9, $ab, $ae, $b0, $b2, $b5, $b7, $ba, $bd, $bf, $c2, $c5, $c7, $ca, $cd, $d0, $d3, $d6, $d9, $dc, $df, $e2, $e5, $e8, $eb, $ee, $f1, $f4, $f7, $fa, $fd, $00, $03, $06, $09, $0c, $0f, $12, $15, $18, $1b, $1e, $21, $24, $27, $2a, $2d, $30, $33, $36, $39, $3b, $3e, $41, $43, $46, $49, $4b, $4e, $50, $52, $55, $57, $59, $5b, $5e, $60, $62, $64, $66, $67, $69, $6b, $6c, $6e, $70, $71, $72, $74, $75, $76, $77, $78, $79, $7a, $7b, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7e, $7e ] return costab[angle] as byte } sub sinr8u(ubyte radians) -> ubyte { ubyte[180] sintab = [ $80, $84, $88, $8d, $91, $96, $9a, $9e, $a3, $a7, $ab, $af, $b3, $b7, $bb, $bf, $c3, $c7, $ca, $ce, $d1, $d5, $d8, $db, $de, $e1, $e4, $e7, $e9, $ec, $ee, $f0, $f2, $f4, $f6, $f7, $f9, $fa, $fb, $fc, $fd, $fe, $fe, $ff, $ff, $ff, $ff, $ff, $fe, $fe, $fd, $fc, $fb, $fa, $f9, $f7, $f6, $f4, $f2, $f0, $ee, $ec, $e9, $e7, $e4, $e1, $de, $db, $d8, $d5, $d1, $ce, $ca, $c7, $c3, $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3, $9e, $9a, $96, $91, $8d, $88, $84, $80, $7b, $77, $72, $6e, $69, $65, $61, $5c, $58, $54, $50, $4c, $48, $44, $40, $3c, $38, $35, $31, $2e, $2a, $27, $24, $21, $1e, $1b, $18, $16, $13, $11, $0f, $0d, $0b, $09, $08, $06, $05, $04, $03, $02, $01, $01, $00, $00, $00, $00, $00, $01, $01, $02, $03, $04, $05, $06, $08, $09, $0b, $0d, $0f, $11, $13, $16, $18, $1b, $1e, $21, $24, $27, $2a, $2e, $31, $35, $38, $3c, $40, $44, $48, $4c, $50, $54, $58, $5c, $61, $65, $69, $6e, $72, $77, $7b] return sintab[radians] } sub cosr8u(ubyte radians) -> ubyte { ubyte[180] costab = [ $ff, $ff, $ff, $fe, $fe, $fd, $fc, $fb, $fa, $f9, $f7, $f6, $f4, $f2, $f0, $ee, $ec, $e9, $e7, $e4, $e1, $de, $db, $d8, $d5, $d1, $ce, $ca, $c7, $c3, $bf, $bb, $b7, $b3, $af, $ab, $a7, $a3, $9e, $9a, $96, $91, $8d, $88, $84, $80, $7b, $77, $72, $6e, $69, $65, $61, $5c, $58, $54, $50, $4c, $48, $44, $40, $3c, $38, $35, $31, $2e, $2a, $27, $24, $21, $1e, $1b, $18, $16, $13, $11, $0f, $0d, $0b, $09, $08, $06, $05, $04, $03, $02, $01, $01, $00, $00, $00, $00, $00, $01, $01, $02, $03, $04, $05, $06, $08, $09, $0b, $0d, $0f, $11, $13, $16, $18, $1b, $1e, $21, $24, $27, $2a, $2e, $31, $35, $38, $3c, $40, $44, $48, $4c, $50, $54, $58, $5c, $61, $65, $69, $6e, $72, $77, $7b, $7f, $84, $88, $8d, $91, $96, $9a, $9e, $a3, $a7, $ab, $af, $b3, $b7, $bb, $bf, $c3, $c7, $ca, $ce, $d1, $d5, $d8, $db, $de, $e1, $e4, $e7, $e9, $ec, $ee, $f0, $f2, $f4, $f6, $f7, $f9, $fa, $fb, $fc, $fd, $fe, $fe, $ff, $ff ] return costab[radians] } sub sinr8(ubyte radians) -> byte { ubyte[180] sintab = [ $00, $04, $08, $0d, $11, $16, $1a, $1e, $23, $27, $2b, $2f, $33, $37, $3b, $3f, $43, $47, $4a, $4e, $51, $54, $58, $5b, $5e, $61, $64, $66, $69, $6b, $6d, $70, $72, $74, $75, $77, $78, $7a, $7b, $7c, $7d, $7d, $7e, $7e, $7e, $7f, $7e, $7e, $7e, $7d, $7d, $7c, $7b, $7a, $78, $77, $75, $74, $72, $70, $6d, $6b, $69, $66, $64, $61, $5e, $5b, $58, $54, $51, $4e, $4a, $47, $43, $3f, $3b, $37, $33, $2f, $2b, $27, $23, $1e, $1a, $16, $11, $0d, $08, $04, $00, $fc, $f8, $f3, $ef, $ea, $e6, $e2, $dd, $d9, $d5, $d1, $cd, $c9, $c5, $c1, $bd, $b9, $b6, $b2, $af, $ac, $a8, $a5, $a2, $9f, $9c, $9a, $97, $95, $93, $90, $8e, $8c, $8b, $89, $88, $86, $85, $84, $83, $83, $82, $82, $82, $81, $82, $82, $82, $83, $83, $84, $85, $86, $88, $89, $8b, $8c, $8e, $90, $93, $95, $97, $9a, $9c, $9f, $a2, $a5, $a8, $ac, $af, $b2, $b6, $b9, $bd, $c1, $c5, $c9, $cd, $d1, $d5, $d9, $dd, $e2, $e6, $ea, $ef, $f3, $f8, $fc ] return sintab[radians] as byte } sub cosr8(ubyte radians) -> byte { ubyte[180] costab = [ $7f, $7e, $7e, $7e, $7d, $7d, $7c, $7b, $7a, $78, $77, $75, $74, $72, $70, $6d, $6b, $69, $66, $64, $61, $5e, $5b, $58, $54, $51, $4e, $4a, $47, $43, $3f, $3b, $37, $33, $2f, $2b, $27, $23, $1e, $1a, $16, $11, $0d, $08, $04, $00, $fc, $f8, $f3, $ef, $ea, $e6, $e2, $dd, $d9, $d5, $d1, $cd, $c9, $c5, $c1, $bd, $b9, $b6, $b2, $af, $ac, $a8, $a5, $a2, $9f, $9c, $9a, $97, $95, $93, $90, $8e, $8c, $8b, $89, $88, $86, $85, $84, $83, $83, $82, $82, $82, $81, $82, $82, $82, $83, $83, $84, $85, $86, $88, $89, $8b, $8c, $8e, $90, $93, $95, $97, $9a, $9c, $9f, $a2, $a5, $a8, $ac, $af, $b2, $b6, $b9, $bd, $c1, $c5, $c9, $cd, $d1, $d5, $d9, $dd, $e2, $e6, $ea, $ef, $f3, $f8, $fc, $00, $04, $08, $0d, $11, $16, $1a, $1e, $23, $27, $2b, $2f, $33, $37, $3b, $3f, $43, $47, $4a, $4e, $51, $54, $58, $5b, $5e, $61, $64, $66, $69, $6b, $6d, $70, $72, $74, $75, $77, $78, $7a, $7b, $7c, $7d, $7d, $7e, $7e, $7e ] return costab[radians] as byte } sub rnd() -> ubyte { %ir {{ syscall 20 (): r0.b returnr.b r0 }} } sub rndw() -> uword { %ir {{ syscall 21 (): r0.w returnr.w r0 }} } sub randrange(ubyte n) -> ubyte { ; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias) cx16.r0H = 255 / n * n do { cx16.r0L = math.rnd() } until cx16.r0L < cx16.r0H return cx16.r0L % n } sub randrangew(uword n) -> uword { ; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias) cx16.r1 = 65535 / n * n do { cx16.r0 = math.rndw() } until cx16.r0 < cx16.r1 return cx16.r0 % n } sub rndseed(uword seed1, uword seed2) { ; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653. %ir {{ loadm.w r65534,math.rndseed.seed1 loadm.w r65535,math.rndseed.seed2 syscall 18 (r65534.w, r65535.w) return }} } sub log2(ubyte value) -> ubyte { ubyte result = 7 ubyte compare = $80 repeat { if value&compare!=0 return result result-- if_z return 0 compare >>= 1 } } sub log2w(uword value) -> ubyte { ubyte result = 15 uword compare = $8000 repeat { if value&compare!=0 return result result-- if_z return 0 compare >>= 1 } } 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. ; 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 = atan2(x1, y1, x2, y2) - (256/48 as ubyte) return 23-lsb(mkword(angle,0) / 2730) } 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. 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 atan2(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte { ;; Calculate the angle, in a 256-degree circle, between two points into A. ;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane! %ir {{ loadm.b r65532,math.atan2.x1 loadm.b r65533,math.atan2.y1 loadm.b r65534,math.atan2.x2 loadm.b r65535,math.atan2.y2 syscall 31 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b returnr.b r0 }} } sub mul16_last_upper() -> uword { ; This routine peeks into the internal 32 bits multiplication result buffer of the ; 16*16 bits multiplication routine, to fetch the upper 16 bits of the last calculation. ; Notes: ; - to avoid interference it's best to fetch and store this value immediately after the multiplication expression. ; for instance, simply printing a number may already result in new multiplication calls being performed ; - not all multiplications in the source code result in an actual multiplication call: ; some simpler multiplications will be optimized away into faster routines. These will not set the upper 16 bits at all! ; - THE RESULT IS ONLY VALID IF THE MULTIPLICATION WAS DONE WITH UWORD ARGUMENTS (or two positive WORD arguments) ; as soon as a negative word value (or 2) was used in the multiplication, these upper 16 bits are not valid!! %ir {{ syscall 33 (): r0.w returnr.w r0 }} } sub diff(ubyte b1, ubyte b2) -> ubyte { if b1>b2 return b1-b2 return b2-b1 } sub diffw(uword w1, uword w2) -> uword { if w1>w2 return w1-w2 return w2-w1 } sub crc16(uword data, uword length) -> uword { ; calculates the CRC16 (XMODEM) checksum of the buffer. ; There are also "streaming" crc16_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block. crc16_start() cx16.r13 = data cx16.r14 = data+length while cx16.r13!=cx16.r14 { crc16_update(@(cx16.r13)) cx16.r13++ } return crc16_end() } sub crc16_start() { ; start the "streaming" crc16 ; note: tracks the crc16 checksum in cx16.r15! ; if your code uses that, it must save/restore it before calling this routine cx16.r15 = 0 } sub crc16_update(ubyte value) { ; update the "streaming" crc16 with next byte value ; note: tracks the crc16 checksum in cx16.r15! ; if your code uses that, it must save/restore it before calling this routine cx16.r15H ^= value repeat 8 { if cx16.r15H & $80 !=0 cx16.r15 = (cx16.r15<<1)^$1021 else cx16.r15<<=1 } } sub crc16_end() -> uword { ; finalize the "streaming" crc16, returns resulting crc16 value return cx16.r15 } sub crc32(uword data, uword length) { ; Calculates the CRC-32 (POSIX) checksum of the buffer. ; because prog8 doesn't have 32 bits integers, we have to split up the calculation over 2 words. ; result stored in cx16.r14 (low word) and cx16.r15 (high word) ; There are also "streaming" crc32_start/update/end routines below, that allow you to calculate crc32 for data that doesn't fit in a single memory block. crc32_start() cx16.r12 = data cx16.r13 = data+length while cx16.r12!=cx16.r13 { crc32_update(@(cx16.r12)) cx16.r12++ } crc32_end() } sub crc32_start() { ; start the "streaming" crc32 ; note: tracks the crc32 checksum in cx16.r14 and cx16.r15! ; if your code uses these, it must save/restore them before calling this routine cx16.r14 = cx16.r15 = 0 } sub crc32_update(ubyte value) { ; update the "streaming" crc32 with next byte value ; note: tracks the crc32 checksum in cx16.r14 and cx16.r15! ; if your code uses these, it must save/restore them before calling this routine cx16.r15H ^= value repeat 8 { if cx16.r15H & $80 !=0 { cx16.r14 <<= 1 rol(cx16.r15) cx16.r15 ^= $04c1 cx16.r14 ^= $1db7 } else { cx16.r14 <<= 1 rol(cx16.r15) } } } sub crc32_end() { ; finalize the "streaming" crc32 ; result stored in cx16.r14 (low word) and cx16.r15 (high word) cx16.r15 ^= $ffff cx16.r14 ^= $ffff } sub lerp(ubyte v0, ubyte v1, ubyte t) -> ubyte { ; Linear interpolation (LERP) ; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255] ; guarantees v = v1 when t = 255 return v0 + msb(t as uword * (v1 - v0) + 255) } sub lerpw(uword v0, uword v1, uword t) -> uword { ; Linear interpolation (LERP) on word values ; returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535] ; guarantees v = v1 when t = 65535 t *= v1-v0 cx16.r0 = math.mul16_last_upper() if t!=0 cx16.r0++ return v0 + cx16.r0 } sub interpolate(ubyte v, ubyte inputMin, ubyte inputMax, ubyte outputMin, ubyte outputMax) -> ubyte { ; Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax] ; There is no version for words because of lack of precision in the fixed point calculation there. cx16.r0 = ((v-inputMin)*256+inputMax) / (inputMax-inputMin) cx16.r0 *= (outputMax-outputMin) return cx16.r0H + outputMin } }