nesemu/cpu/decoding.c

483 lines
11 KiB
C

//
// Created by william on 1/9/24.
//
#include "decoding.h"
#include <assert.h>
#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);
}
}