; Prog8 internal Math library routines - always included by the compiler ; Generic machine independent 6502 code. ; ; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0 ; ; indent format: TABS, size=8 ; some more interesting routines can be found here: ; http://6502org.wikidot.com/software-math ; http://codebase64.org/doku.php?id=base:6502_6510_maths ; math_store_reg .byte 0 ; temporary storage multiply_bytes .proc ; -- multiply 2 bytes A and Y, result as byte in A (signed or unsigned) sta P8ZP_SCRATCH_B1 ; num1 sty P8ZP_SCRATCH_REG ; num2 lda #0 beq _enterloop _doAdd clc adc P8ZP_SCRATCH_B1 _loop asl P8ZP_SCRATCH_B1 _enterloop lsr P8ZP_SCRATCH_REG bcs _doAdd bne _loop rts .pend multiply_bytes_16 .proc ; -- multiply 2 bytes A and Y, result as word in A/Y (unsigned) sta P8ZP_SCRATCH_B1 sty P8ZP_SCRATCH_REG stx math_store_reg lda #0 ldx #8 lsr P8ZP_SCRATCH_B1 - bcc + clc adc P8ZP_SCRATCH_REG + ror a ror P8ZP_SCRATCH_B1 dex bne - tay lda P8ZP_SCRATCH_B1 ldx math_store_reg rts .pend multiply_words .proc ; -- multiply two 16-bit words into a 32-bit result (signed and unsigned) ; input: A/Y = first 16-bit number, P8ZP_SCRATCH_W1 in ZP = second 16-bit number ; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high) ; clobbers: A ; TODO find a faster 16*16 -> 16 bits multiplication routine sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1 stx P8ZP_SCRATCH_REG mult16 lda #0 sta result+2 ; clear upper bits of product sta result+3 ldx #16 ; for all 16 bits... - lsr P8ZP_SCRATCH_W1+1 ; divide multiplier by 2 ror P8ZP_SCRATCH_W1 bcc + lda result+2 ; get upper half of product and add multiplicand clc adc P8ZP_SCRATCH_W2 sta result+2 lda result+3 adc P8ZP_SCRATCH_W2+1 + ror a ; rotate partial product sta result+3 ror result+2 ror result+1 ror result dex bne - ldx P8ZP_SCRATCH_REG rts result .byte 0,0,0,0 .pend divmod_b_asm .proc ; signed byte division: make everything positive and fix sign afterwards sta P8ZP_SCRATCH_B1 tya eor P8ZP_SCRATCH_B1 php ; save sign lda P8ZP_SCRATCH_B1 bpl + eor #$ff sec adc #0 ; make it positive + pha tya bpl + eor #$ff sec adc #0 ; make it positive tay + pla jsr divmod_ub_asm sta _remainder plp bpl + tya eor #$ff sec adc #0 ; negate result tay + rts _remainder .byte 0 .pend divmod_ub_asm .proc ; -- divide A by Y, result quotient in Y, remainder in A (unsigned) ; division by zero will result in quotient = 255 and remainder = original number sty P8ZP_SCRATCH_REG sta P8ZP_SCRATCH_B1 stx math_store_reg lda #0 ldx #8 asl P8ZP_SCRATCH_B1 - rol a cmp P8ZP_SCRATCH_REG bcc + sbc P8ZP_SCRATCH_REG + rol P8ZP_SCRATCH_B1 dex bne - ldy P8ZP_SCRATCH_B1 ldx math_store_reg rts .pend divmod_w_asm .proc ; signed word division: make everything positive and fix sign afterwards sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1 lda P8ZP_SCRATCH_W1+1 eor P8ZP_SCRATCH_W2+1 php ; save sign lda P8ZP_SCRATCH_W1+1 bpl + lda #0 sec sbc P8ZP_SCRATCH_W1 sta P8ZP_SCRATCH_W1 lda #0 sbc P8ZP_SCRATCH_W1+1 sta P8ZP_SCRATCH_W1+1 + lda P8ZP_SCRATCH_W2+1 bpl + lda #0 sec sbc P8ZP_SCRATCH_W2 sta P8ZP_SCRATCH_W2 lda #0 sbc P8ZP_SCRATCH_W2+1 sta P8ZP_SCRATCH_W2+1 + tay lda P8ZP_SCRATCH_W2 jsr divmod_uw_asm plp ; restore sign bpl + sta P8ZP_SCRATCH_W2 sty P8ZP_SCRATCH_W2+1 lda #0 sec sbc P8ZP_SCRATCH_W2 pha lda #0 sbc P8ZP_SCRATCH_W2+1 tay pla + rts .pend divmod_uw_asm .proc ; -- divide two unsigned words (16 bit each) into 16 bit results ; input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor ; output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result ; division by zero will result in quotient = 65535 and remainder = divident dividend = P8ZP_SCRATCH_W1 remainder = P8ZP_SCRATCH_W2 result = dividend ;save memory by reusing divident to store the result sta _divisor sty _divisor+1 stx P8ZP_SCRATCH_REG lda #0 ;preset remainder to 0 sta remainder sta remainder+1 ldx #16 ;repeat for each bit: ... - asl dividend ;dividend lb & hb*2, msb -> Carry rol dividend+1 rol remainder ;remainder lb & hb * 2 + msb from carry rol remainder+1 lda remainder sec sbc _divisor ;substract divisor to see if it fits in tay ;lb result -> Y, for we may need it later lda remainder+1 sbc _divisor+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 result ;and INCrement result cause divisor fit in 1 times + dex bne - lda result ldy result+1 ldx P8ZP_SCRATCH_REG rts _divisor .word 0 .pend randseed .proc ; -- reset the random seeds for the byte and word random generators ; arguments: uword seed in A/Y clobbers A ; (default starting values are: A=$2c Y=$9e) sta randword._seed sty randword._seed+1 clc adc #14 sta randbyte._seed rts .pend randbyte .proc ; -- 8-bit pseudo random number generator into A lda _seed beq _eor asl a beq _done ; if the input was $80, skip the EOR bcc _done _eor eor #$1d ; xor with magic value see below for possible values _done sta _seed rts _seed .byte $3a ; possible 'magic' eor bytes are: ; $1d, $2b, $2d, $4d, $5f, $63, $65, $69 ; $71, $87, $8d, $a9, $c3, $cf, $e7, $f5 .pend randword .proc ; -- 16 bit pseudo random number generator into AY magic_eor = $3f1d ; possible magic eor words are: ; $3f1d, $3f81, $3fa5, $3fc5, $4075, $409d, $40cd, $4109 ; $413f, $414b, $4153, $4159, $4193, $4199, $41af, $41bb 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_eor sta _seed+1 lda _seed eor #