// // Created by william on 1/9/24. // #include "decoding.h" #include #include "log.h" address decode_operand_addr(AddressingMode addr_mode, bool *page_crossing) { CPU *registers = cpu_get_state(); address operand_addr; if (addr_mode == ADDR_MODE_ZERO_PAGE) { operand_addr = cpu_get_next_byte(); } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_X) { operand_addr = (cpu_get_next_byte() + registers->x) & 0xff; } else if (addr_mode == ADDR_MODE_ZERO_PAGE_INDEXED_Y) { operand_addr = (cpu_get_next_byte() + registers->y) & 0xff; } else if (addr_mode == ADDR_MODE_ABSOLUTE) { operand_addr = cpu_get_next_word(); } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_X) { word addr = cpu_get_next_word(); word new_addr = addr + registers->x; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); operand_addr = new_addr; } else if (addr_mode == ADDR_MODE_ABSOLUTE_INDEXED_Y) { word addr = cpu_get_next_word(); word new_addr = addr + registers->y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); operand_addr = new_addr; } else if (addr_mode == ADDR_MODE_INDIRECT_JUMP) { word addr = cpu_get_next_word(); if ((addr & 0xff) == 0xff) { // Error in NES CPU for JMP op word result = mem_get_byte(addr); result += mem_get_byte(addr & 0xff00) << 8; operand_addr = result; } else { operand_addr = mem_get_word(addr); } } else if (addr_mode == ADDR_MODE_INDIRECT_X) { byte arg_addr = cpu_get_next_byte(); word addr = mem_get_byte((arg_addr + registers->x) & 0xff); addr += mem_get_byte((arg_addr + registers->x + 1) & 0xff) << 8; operand_addr = addr; } else if (addr_mode == ADDR_MODE_INDIRECT_Y) { byte arg_addr = cpu_get_next_byte(); word addr = mem_get_byte(arg_addr) + (mem_get_byte((arg_addr + 1) & 0xff) << 8); word new_addr = addr + registers->y; *page_crossing = (addr & 0xff00) != (new_addr & 0xff00); operand_addr = new_addr; } else { assert(false); } log_trace("Operand address: %#02x, Addressing mode: %s", operand_addr, get_addr_mode_name(addr_mode)); return operand_addr; } Operand decode_operand(AddressingMode addr_mode) { Operand operand; if (addr_mode == ADDR_MODE_ACCUMULATOR) { operand.type = OPERAND_TYPE_ACCUMULATOR; operand.value = 0; operand.is_page_crossing = false; } else if (addr_mode == ADDR_MODE_IMMEDIATE) { operand.type = OPERAND_TYPE_IMMEDIATE; operand.value = cpu_get_next_byte(); operand.is_page_crossing = false; } else { operand.type = OPERAND_TYPE_ADDRESS; operand.value = decode_operand_addr(addr_mode, &operand.is_page_crossing); } log_trace("Operand type: %s, value: %#02x", operand_name(&operand), operand.value); return operand; } byte read_operand(Operand operand) { switch (operand.type) { case OPERAND_TYPE_ACCUMULATOR: return cpu_get_state()->accumulator; case OPERAND_TYPE_IMMEDIATE: return (byte) operand.value; case OPERAND_TYPE_ADDRESS: return mem_get_byte(operand.value); default: assert(false); } } char *get_addr_mode_name(AddressingMode addr_mode) { switch (addr_mode) { case ADDR_MODE_ABSOLUTE: return "a"; case ADDR_MODE_ABSOLUTE_INDEXED_X: return "a,x"; case ADDR_MODE_ABSOLUTE_INDEXED_Y: return "a,y"; case ADDR_MODE_ACCUMULATOR: return "A"; case ADDR_MODE_IMMEDIATE: return "#"; case ADDR_MODE_IMPLICIT: return " "; case ADDR_MODE_INDIRECT_X: return "(d,x)"; case ADDR_MODE_INDIRECT_Y: return "(d,y)"; case ADDR_MODE_INDIRECT_JUMP: return "(a)"; case ADDR_MODE_RELATIVE: return "label"; case ADDR_MODE_ZERO_PAGE: return "d"; case ADDR_MODE_ZERO_PAGE_INDEXED_X: return "d,x"; case ADDR_MODE_ZERO_PAGE_INDEXED_Y: return "d,y"; } } char *operand_name(Operand *operand) { switch (operand->type) { case OPERAND_TYPE_ACCUMULATOR: return "Accumulator"; case OPERAND_TYPE_IMMEDIATE: return "Immediate"; case OPERAND_TYPE_ADDRESS: return "Address"; } } char *get_op_code_name(byte op) { switch (op) { case 0x00: return "BRK"; case 0x08: return "PHP"; case 0x0b: case 0x2b: return "ANC"; case 0x10: return "BPL"; case 0x18: return "CLC"; case 0x20: return "JSR"; case 0x24: case 0x2c: return "BIT"; case 0x28: return "PLP"; case 0x30: return "BMI"; case 0x38: return "SEC"; case 0x40: return "RTI"; case 0x48: return "PHA"; case 0x4c: case 0x6c: return "JMP"; case 0x50: return "BVC"; case 0x58: return "CLI"; case 0x60: return "RTS"; case 0x68: return "PLA"; case 0x70: return "BVS"; case 0x78: return "SEI"; case 0x84: case 0x8c: case 0x94: return "STY"; case 0x88: return "DEY"; case 0x90: return "BCC"; case 0x98: return "TYA"; case 0x9c: return "SHY"; case 0xa0: case 0xa4: case 0xac: case 0xb4: case 0xbc: return "LDY"; case 0xa8: return "TAY"; case 0xb0: return "BCS"; case 0xb8: return "CLV"; case 0xc0: case 0xc4: case 0xcc: return "CPY"; case 0xc8: return "INY"; case 0xd0: return "BNE"; case 0xd8: return "CLD"; case 0xe0: case 0xe4: case 0xec: return "CPX"; case 0xe8: return "INX"; case 0xf0: return "BEQ"; case 0xf8: return "SED"; case 0x01: case 0x05: case 0x09: case 0x0d: case 0x11: case 0x15: case 0x19: case 0x1d: return "ORA"; case 0x21: case 0x25: case 0x29: case 0x2d: case 0x31: case 0x35: case 0x39: case 0x3d: return "AND"; case 0x41: case 0x45: case 0x49: case 0x4d: case 0x51: case 0x55: case 0x59: case 0x5d: return "EOR"; case 0x61: case 0x65: case 0x69: case 0x6d: case 0x71: case 0x75: case 0x79: case 0x7d: return "ADC"; case 0x81: case 0x85: case 0x8d: case 0x91: case 0x95: case 0x99: case 0x9d: return "STA"; case 0xa1: case 0xa5: case 0xa9: case 0xad: case 0xb1: case 0xb5: case 0xb9: case 0xbd: return "LDA"; case 0xc1: case 0xc5: case 0xc9: case 0xcd: case 0xd1: case 0xd5: case 0xd9: case 0xdd: return "CMP"; case 0xe1: case 0xe5: case 0xe9: case 0xed: case 0xf1: case 0xf5: case 0xf9: case 0xfd: return "SBC"; case 0x03: case 0x07: case 0x0f: case 0x13: case 0x17: case 0x1b: case 0x1f: return "SLO"; case 0x23: case 0x27: case 0x2f: case 0x33: case 0x37: case 0x3b: case 0x3f: return "RLA"; case 0x43: case 0x47: case 0x4f: case 0x53: case 0x57: case 0x5b: case 0x5f: return "SRE"; case 0x4b: return "ALR"; case 0x63: case 0x67: case 0x6f: case 0x73: case 0x77: case 0x7b: case 0x7f: return "RRA"; case 0x6b: return "ARR"; case 0x83: case 0x87: case 0x8f: case 0x97: return "SAX"; case 0x8b: return "XAA"; case 0x93: case 0x9f: return "AHX"; case 0x9b: return "TAS"; case 0xa3: case 0xa7: case 0xab: case 0xaf: case 0xb3: case 0xb7: case 0xbb: case 0xbf: return "LAX"; case 0xc3: case 0xc7: case 0xcf: case 0xd3: case 0xd7: case 0xdb: case 0xdf: return "DCP"; case 0xcb: return "AXS"; case 0xe3: case 0xe7: case 0xef: case 0xf3: case 0xf7: case 0xfb: case 0xff: return "ISC"; case 0xeb: return "SBC"; case 0x06: case 0x0a: case 0x0e: case 0x16: case 0x1e: return "ASL"; case 0x26: case 0x2a: case 0x2e: case 0x36: case 0x3e: return "ROL"; case 0x46: case 0x4a: case 0x4e: case 0x56: case 0x5e: return "LSR"; case 0x66: case 0x6a: case 0x6e: case 0x76: case 0x7e: return "ROR"; case 0x86: case 0x8e: case 0x96: return "STX"; case 0x8a: return "TXA"; case 0x9a: case 0xba: return "TSX"; case 0x9e: return "SHX"; case 0xa2: case 0xa6: case 0xae: case 0xb6: case 0xbe: return "LDX"; case 0xaa: return "TAX"; case 0xc6: case 0xce: case 0xd6: case 0xde: return "DEC"; case 0xca: return "DEX"; case 0xe6: case 0xee: case 0xf6: case 0xfe: return "INC"; case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2: return "STP"; case 0x04: case 0x0c: case 0x14: case 0x1c: case 0x1a: case 0x34: case 0x3a: case 0x3c: case 0x44: case 0x54: case 0x5a: case 0x5c: case 0x64: case 0x74: case 0x7a: case 0x7c: case 0x80: case 0x82: case 0x89: case 0xc2: case 0xd4: case 0xda: case 0xdc: case 0xe2: case 0xea: case 0xf4: case 0xfa: case 0xfc: return "NOP"; default: assert(false); } }