// Test the fast multiplication library
import "print.kc"
import "multiply.kc"

byte* BGCOL = $d021;

void main() {
    *BGCOL = 5;
    print_cls();
    init_multiply();
    init_multiply_asm();
    multiply_tables_compare();
    multiply_results_compare();
    signed_multiply_results_compare();
}

// Calculate an unsigned multiplication by repeated addition
word slow_multiply(byte a, byte b) {
    word m = 0;
    if(a!=0) {
        for(byte i = 0; i!=a; i++) {
            m = m + b;
        }
    }
    return m;
}

// Perform a signed multiplication by repeated addition/
signed word slow_signed_multiply(signed byte a, signed byte b) {
    signed word m = 0;
    if(a<0) {
        for(signed byte i = 0; i!=a; i--) {
            m = m - b;
        }
    } else if (a>0) {
        for(signed byte j = 0; j!=a; j++) {
            m = m + b;
        }
    }
    return m;
}

// ASM based multiplication tables
// <(( x * x )/4)
byte[512] align($100) asm_mul_sqr1_lo;
// >(( x * x )/4)
byte[512] align($100) asm_mul_sqr1_hi;
// <((( x - 255) * ( x - 255 ))/4)
byte[512] align($100) asm_mul_sqr2_lo;
// >((( x - 255) * ( x - 255 ))/4)
byte[512] align($100) asm_mul_sqr2_hi;
// Initialize the multiplication tables using ASM code from
// http://codebase64.org/doku.php?id=base:seriously_fast_multiplication
void init_multiply_asm() {
    asm{
        ldx #$00
        txa
        .byte $c9
    lb1:
        tya
        adc #$00
    ml1:
        sta asm_mul_sqr1_hi,x
        tay
        cmp #$40
        txa
        ror
    ml9:
        adc #$00
        sta ml9+1
        inx
    ml0:
        sta asm_mul_sqr1_lo,x
        bne lb1
        inc ml0+2
        inc ml1+2
        clc
        iny
        bne lb1
        ldx #$00
        ldy #$ff
    !:
        lda asm_mul_sqr1_hi+1,x
        sta asm_mul_sqr2_hi+$100,x
        lda asm_mul_sqr1_hi,x
        sta asm_mul_sqr2_hi,y
        lda asm_mul_sqr1_lo+1,x
        sta asm_mul_sqr2_lo+$100,x
        lda asm_mul_sqr1_lo,x
        sta asm_mul_sqr2_lo,y
        dey
        inx
        bne !-
    }
    // Ensure the ASM tables are not detected as unused by the optimizer
    byte* mem = $ff;
    *mem = *asm_mul_sqr1_lo;
    *mem = *asm_mul_sqr1_hi;
    *mem = *asm_mul_sqr2_lo;
    *mem = *asm_mul_sqr2_hi;
}

// Compare the ASM-based mul tables with the KC-based mul tables
// Red screen on failure - green on success
void multiply_tables_compare() {
    byte* asm_sqr = asm_mul_sqr1_lo;
    for( byte* kc_sqr=mul_sqr1_lo; kc_sqr<mul_sqr1_lo+512*4; kc_sqr++) {
        if(*kc_sqr != *asm_sqr) {
            *BGCOL = 2;
            print_str("multiply table mismatch at @");
            print_word((word)asm_sqr);
            print_str(" / @");
            print_word((word)kc_sqr);
            return;
        }
        asm_sqr++;
    }
    print_str("multiply tables match!@");
    print_ln();
}

// Perform all possible byte multiplications (slow and fast) and compare the results
void multiply_results_compare() {
    for(byte a: 0..255) {
        for(byte b: 0..255) {
            word ms = slow_multiply(a, b);
            word ma = multiply(a,b);
            if(ms!=ma) {
                *BGCOL = 2;
                multiply_error(a,b, ms, ma);
                return;
            }
        }
    }
    print_str("multiply results match!@");
    print_ln();
}

void multiply_error(byte a, byte b, word ms, word ma) {
  print_str("multiply mismatch @");
  print_byte(a);
  print_str("*@");
  print_byte(b);
  print_str(" slow:@");
  print_word(ms);
  print_str(" / fast asm:@");
  print_word(ma);
  print_ln();
}

// Perform all possible signed byte multiplications (slow and fast) and compare the results
void signed_multiply_results_compare() {
    for(signed byte a = -128; a!=-128; a++) {
        for(signed byte b = -128; b!=-128; b++) {
            signed word ms = slow_signed_multiply(a, b);
            signed word ma = signed_multiply(a,b);
            if(ms!=ma) {
                *BGCOL = 2;
                signed_multiply_error(a,b, ms, ma);
                return;
            }
        }
    }
    print_str("signed multiply results match!@");
    print_ln();
}

void signed_multiply_error(signed byte a, signed byte b, signed word ms, signed word ma) {
  print_str("signed multiply mismatch @");
  print_sbyte(a);
  print_str("*@");
  print_sbyte(b);
  print_str(" slow:@");
  print_sword(ms);
  print_str(" / fast asm:@");
  print_sword(ma);
  print_ln();
}
