; Prog8 integer math library for 6502 ; (floating point math is done via the C-64's BASIC ROM routines) ; ; some more interesting routines can be found here: ; http://6502org.wikidot.com/software-math ; http://codebase64.org/doku.php?id=base:6502_6510_maths ; ; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 ; ; indent format: TABS, size=8 ~ math { ; note: the following ZP scratch registers must be the same as in c64lib memory ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP memory ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP memory uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc) memory uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe) asmsub multiply_bytes (ubyte byte1 @ A, ubyte byte2 @ Y) -> clobbers() -> (ubyte @ A) { ; ---- multiply 2 bytes, result as byte in A (signed or unsigned) %asm {{ sta SCRATCH_ZPB1 ; num1 sty SCRATCH_ZPREG ; num2 lda #0 beq _enterloop _doAdd clc adc SCRATCH_ZPB1 _loop asl SCRATCH_ZPB1 _enterloop lsr SCRATCH_ZPREG bcs _doAdd bne _loop rts }} } asmsub multiply_bytes_16 (ubyte byte1 @ A, ubyte byte2 @ Y) -> clobbers(X) -> (uword @ AY) { ; ---- multiply 2 bytes, result as word in A/Y (unsigned) %asm {{ sta SCRATCH_ZPB1 sty SCRATCH_ZPREG lda #0 ldx #8 lsr SCRATCH_ZPB1 - bcc + clc adc SCRATCH_ZPREG + ror a ror SCRATCH_ZPB1 dex bne - tay lda SCRATCH_ZPB1 rts }} } asmsub multiply_words (uword number @ AY) -> clobbers(A,X) -> () { ; ---- multiply two 16-bit words into a 32-bit result (signed and unsigned) ; input: A/Y = first 16-bit number, SCRATCH_ZPWORD1 in ZP = second 16-bit number ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) %asm {{ sta SCRATCH_ZPWORD2 sty SCRATCH_ZPWORD2+1 mult16 lda #$00 sta multiply_words_result+2 ; clear upper bits of product sta multiply_words_result+3 ldx #16 ; for all 16 bits... - lsr SCRATCH_ZPWORD1+1 ; divide multiplier by 2 ror SCRATCH_ZPWORD1 bcc + lda multiply_words_result+2 ; get upper half of product and add multiplicand clc adc SCRATCH_ZPWORD2 sta multiply_words_result+2 lda multiply_words_result+3 adc SCRATCH_ZPWORD2+1 + ror a ; rotate partial product sta multiply_words_result+3 ror multiply_words_result+2 ror multiply_words_result+1 ror multiply_words_result dex bne - rts multiply_words_result .byte 0,0,0,0 }} } asmsub divmod_bytes (ubyte number @ X, ubyte divisor @ Y) -> clobbers() -> (ubyte @ X, ubyte @ A) { ; ---- divide X by Y, result quotient in X, remainder in A (unsigned) ; division by zero will result in quotient = 255 and remainder = original number %asm {{ stx SCRATCH_ZPB1 sty SCRATCH_ZPREG lda #0 ldx #8 asl SCRATCH_ZPB1 - rol a cmp SCRATCH_ZPREG bcc + sbc SCRATCH_ZPREG + rol SCRATCH_ZPB1 dex bne - ldx SCRATCH_ZPB1 rts }} } asmsub divmod_words (uword divisor @ AY) -> clobbers(X) -> (uword @ AY) { ; ---- divide two words (16 bit each) into 16 bit results ; input: SCRATCH_ZPWORD1 in ZP: 16 bit number, A/Y: 16 bit divisor ; output: SCRATCH_ZPWORD1 in ZP: 16 bit result, A/Y: 16 bit remainder ; division by zero will result in quotient = 65535 and remainder = divident %asm {{ remainder = SCRATCH_ZPB1 sta SCRATCH_ZPWORD2 sty SCRATCH_ZPWORD2+1 lda #0 ;preset remainder to 0 sta remainder sta remainder+1 ldx #16 ;repeat for each bit: ... - asl SCRATCH_ZPWORD1 ;number lb & hb*2, msb -> Carry rol SCRATCH_ZPWORD1+1 rol remainder ;remainder lb & hb * 2 + msb from carry rol remainder+1 lda remainder sec sbc SCRATCH_ZPWORD2 ;substract divisor to see if it fits in tay ;lb result -> Y, for we may need it later lda remainder+1 sbc SCRATCH_ZPWORD2+1 bcc + ;if carry=0 then divisor didn't fit in yet sta remainder+1 ;else save substraction result as new remainder, sty remainder inc SCRATCH_ZPWORD1 ;and INCrement result cause divisor fit in 1 times + dex bne - lda remainder ; copy remainder to ZPWORD2 result register sta SCRATCH_ZPWORD2 lda remainder+1 sta SCRATCH_ZPWORD2+1 lda SCRATCH_ZPWORD1 ; load division result in A/Y ldy SCRATCH_ZPWORD1+1 rts }} } asmsub randseed (uword seed @ AY) -> clobbers(A, Y) -> () { ; ---- reset the random seeds for the byte and word random generators ; default starting values are: A=$2c Y=$9e %asm {{ sta randword._seed sty randword._seed+1 stx SCRATCH_ZPREG clc adc #14 sta randbyte._seed ora #$80 ; make negative jsr c64.FREADSA jsr c64.RND ; reseed the float rng using the (negative) number in A ldx SCRATCH_ZPREG rts }} } asmsub randbyte () -> clobbers() -> (ubyte @ A) { ; ---- 8-bit pseudo random number generator into A %asm {{ lda _seed beq + asl a beq ++ ;if the input was $80, skip the EOR bcc ++ + eor _magic ; #$1d ; could be self-modifying code to set new magic + sta _seed rts _seed .byte $3a _magic .byte $1d _magiceors .byte $1d, $2b, $2d, $4d, $5f, $63, $65, $69 .byte $71, $87, $8d, $a9, $c3, $cf, $e7, $f5 }} } asmsub randword () -> clobbers() -> (uword @ AY) { ; ---- 16 bit pseudo random number generator into AY %asm {{ lda _seed beq _lowZero ; $0000 and $8000 are special values to test for ; Do a normal shift asl _seed lda _seed+1 rol a bcc _noEor _doEor ; high byte is in A eor _magic+1 ; #>magic ; could be self-modifying code to set new magic sta _seed+1 lda _seed eor _magic ; #