#include "inc/types.h"
#include "inc/memory.h"
#include "inc/opcodes.h"

ocl opcode_in_list;
oct opcode_in_table;

void init()
{
    // pc is set using 0xFFFC
    pc = read_word(0xFFFC);
    
    write_mem(0xD010, 'T');
    write_mem(0xD010, 'E');
    write_mem(0xD010, 'S');
    write_mem(0xD010, 'T');
    write_mem(0xD010, 'S');
}

void fetch()
{
    ir = read_byte(pc);
    pc = pc + 1;
}

void decode()
{
    // instruction pattern is "aaabbbccc" where:
    // aaa cc determines the opcode
    // bbb determines the addressing mode
    
    db aaa = (ir & 0b11100000) >> 5;
    db bbb = (ir & 0b00011100) >> 2;
    db  cc = (ir & 0b00000011);
    
    db aaacc = (aaa << 2) | cc;
    
    opcode_in_list = ir;
    opcode_in_table = aaacc;
    
    address_mode = bbb;
    
    if (cc == 0b01)
    {
        // correct the addressing mode for '01' opcode type
        if (bbb == 0b000)
        {
            address_mode = indirect_x;
        }
        if (bbb == 0b010)
        {
            address_mode = immediate;
        }
    }
    
    if (cc == 0b10)
    {
        // adjust the addressing mode for STX and LDX
        if ((aaacc == STX || aaacc == LDX) && address_mode == zero_page_x)
        {
            address_mode = zero_page_y;
        }
        if (aaacc == LDX && address_mode == absolute_x)
        {
            address_mode = absolute_y;
        }
    }
}

void execute()
{
    switch (opcode_in_list)
    {
        case BCC: return bcc();
        case BCS: return bcs();
        case BEQ: return beq();
        case BMI: return bmi();
        case BNE: return bne();
        case BPL: return bpl();
        case BRK: return brk();
        case BVC: return bvc();
        case BVS: return bvs();
        case CLC: return clc();
        case CLD: return cld();
        case CLI: return cli();
        case CLV: return clv();
        case DEX: return dex();
        case DEY: return dey();
        case INX: return inx();
        case INY: return iny();
        case JSR: return jsr();
        case NOP: return nop();
        case PHA: return pha();
        case PHP: return php();
        case PLA: return pla();
        case PLP: return plp();
        case RTI: return rti();
        case RTS: return rts();
        case SEC: return sec();
        case SED: return sed();
        case SEI: return sei();
        case TAX: return tax();
        case TAY: return tay();
        case TSX: return tsx();
        case TXA: return txa();
        case TXS: return txs();
        case TYA: return tya();
    }
    
    switch (opcode_in_table)
    {
        case ADC: return adc();
        case AND: return and();
        case ASL: return asl();
        case BIT: return bit();
        case CMP: return cmp();
        case CPX: return cpx();
        case CPY: return cpy();
        case DEC: return dec();
        case EOR: return eor();
        case INC: return inc();
        case JMP: return jmp();
        case JPA: return jpa();
        case LDA: return lda();
        case LDX: return ldx();
        case LDY: return ldy();
        case LSR: return lsr();
        case ORA: return ora();
        case ROL: return rol();
        case ROR: return ror();
        case SBC: return sbc();
        case STA: return sta();
        case STX: return stx();
        case STY: return sty();
    }
}

void run()
{
    while (1)
    {
        fetch();
        decode();
        execute();
    }
    
}
