#include "cpu.h" #include #include #include "log.h" #include "bus.h" static inline void Push(struct Bus* bus, Byte val) { writeBus(bus, 0x0100 + (bus->cpu->sp--), val); } static inline Byte Pop(struct Bus* bus) { return readBus(bus, 0x0100 + (++bus->cpu->sp)); } struct CPU* createCPU(struct Bus* parent) { struct CPU* cpu = (struct CPU*)malloc(sizeof(struct CPU)); if (cpu == NULL) { fprintf(stderr, "Failed to create CPU, aborting.\n"); exit(1); } // TODO: THIS IS JUST FOR THE TEST ROM cpu->pc.word = 0xC000; cpu->pc.word = ((Word)readBus(parent, 0xFFFD) << 8) | readBus(parent, 0xFFFC); cpu->status.raw = 0x34; cpu->acc = 0; cpu->x = 0; cpu->y = 0; cpu->sp = 0xFD; cpu->remainingCycles = 7; cpu->totalCycles = 0; cpu->irq = 0; cpu->nmi = 0; cpu->bus = parent; return cpu; } void destroyCPU(struct CPU* cpu) { free(cpu); } int tickCPU(struct CPU* cpu) { if (cpu->remainingCycles == -1) // Jammed return 1; cpu->remainingCycles--; cpu->totalCycles += 1; if (cpu->remainingCycles == 1) { // Handle interrupts if (cpu->nmi) { Push(cpu->bus, cpu->pc.hi); Push(cpu->bus, cpu->pc.lo); Push(cpu->bus, cpu->status.raw & 0b11101111); cpu->nmi = 0; // printf("NMI TRIGGERED FROM $%04x\n", cpu->pc.word); cpu->pc.lo = readBus(cpu->bus, 0xFFFA); cpu->pc.hi = readBus(cpu->bus, 0xFFFB); return 0; } if (!cpu->status.id) { if (cpu->irq) { Push(cpu->bus, cpu->pc.hi); Push(cpu->bus, cpu->pc.lo); Push(cpu->bus, cpu->status.raw & 0b11101111); cpu->status.id = 1; cpu->pc.lo = readBus(cpu->bus, 0xFFFE); cpu->pc.hi = readBus(cpu->bus, 0xFFFF); cpu->irq = 0; } } } if (cpu->remainingCycles == 0) { prepareFetch(cpu); execute(cpu); return 1; } return 0; } void tickInstr(struct CPU* cpu) { while (!tickCPU(cpu)); } void prepareFetch(struct CPU* cpu) { Byte opcodeVal = readBus(cpu->bus, cpu->pc.word); cpu->currentOpcode = OPCODE_TABLE + opcodeVal; if (cpu->currentOpcode->op == XXX) { fprintf(stderr, "Unknown opcode: %02X", opcodeVal); exit(1); } cpu->pc.word++; cpu->remainingCycles = cpu->currentOpcode->cycles; switch (cpu->currentOpcode->addr) { case ACC: cpu->fetchedVal = cpu->acc; return; case ABS: { Byte lo = readBus(cpu->bus, cpu->pc.word++); Byte hi = readBus(cpu->bus, cpu->pc.word++); cpu->fetchedAddress = ((Word)hi << 8) | lo; } break; case ABX: { Byte lo = readBus(cpu->bus, cpu->pc.word++); Byte hi = readBus(cpu->bus, cpu->pc.word++); Word addr = ((Word)hi << 8) | lo; cpu->fetchedAddress = addr + cpu->x; if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00)) cpu->remainingCycles++; } break; case ABY: { Byte lo = readBus(cpu->bus, cpu->pc.word++); Byte hi = readBus(cpu->bus, cpu->pc.word++); Word addr = ((Word)hi << 8) | lo; cpu->fetchedAddress = addr + cpu->y; if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00)) cpu->remainingCycles++; } break; case IMM: cpu->fetchedAddress = cpu->pc.word++; return; case IMP: return; case IND: { Byte lo = readBus(cpu->bus, cpu->pc.word++); Byte hi = readBus(cpu->bus, cpu->pc.word++); Word addr = ((Word)hi << 8); cpu->fetchedAddress = ((Word)readBus(cpu->bus, addr | ((lo + 1) & 0xFF)) << 8) | readBus(cpu->bus, addr | lo); } break; case INDX: { Byte op = readBus(cpu->bus, cpu->pc.word++); Byte lo = readBus(cpu->bus, (op + cpu->x) & 0xFF); Byte hi = readBus(cpu->bus, (op + cpu->x + 1) & 0xFF); cpu->fetchedAddress = ((Word)hi << 8) | lo; } break; case INDY: { Byte op = readBus(cpu->bus, cpu->pc.word++); Byte lo = readBus(cpu->bus, op); Byte hi = readBus(cpu->bus, (op + 1) & 0xFF); Word addr = ((Word)hi << 8) | lo; cpu->fetchedAddress = addr + cpu->y; if ((cpu->fetchedAddress & 0xFF00) != (addr & 0xFF00)) cpu->remainingCycles++; } break; case REL: { cpu->fetchedRelAddress = readBus(cpu->bus, cpu->pc.word++); } break; case ZPG: cpu->fetchedAddress = readBus(cpu->bus, cpu->pc.word++); break; case ZPX: { cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->x) & 0xFF; } break; case ZPY: { cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->y) & 0xFF; } break; } } void fetch(struct CPU* cpu) { if(cpu->currentOpcode->addr != IMP && cpu->currentOpcode->addr != ACC) cpu->fetchedVal = readBus(cpu->bus, cpu->fetchedAddress); } void execute(struct CPU* cpu) { // LOG_BUS(cpu->bus); switch (cpu->currentOpcode->op) { case ADC: { fetch(cpu); Word result = cpu->acc + cpu->fetchedVal + cpu->status.carry; cpu->status.carry = (result > 0xFF); cpu->status.overflow = ((~(cpu->acc ^ cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80); cpu->status.negative = (result >> 7); cpu->status.zero = ((result & 0xFF) == 0x00); cpu->acc = result & 0xFF; } break; case AND: { fetch(cpu); cpu->acc &= cpu->fetchedVal; cpu->status.negative = (cpu->acc >> 7); cpu->status.zero = (cpu->acc == 0x00); } break; case ASL: { fetch(cpu); cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80); cpu->fetchedVal <<= 1; cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80); cpu->status.zero = (cpu->fetchedVal == 0x00); if (!cpu->currentOpcode->addr == ACC) writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); else cpu->acc = cpu->fetchedVal; } break; case BCC: { if (!cpu->status.carry) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BCS: { if (cpu->status.carry) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BEQ: { if (cpu->status.zero) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BMI: { if (cpu->status.negative) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BPL: { if (!cpu->status.negative) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BRK: { cpu->pc.word++; Push(cpu->bus, cpu->pc.hi); Push(cpu->bus, cpu->pc.lo); Push(cpu->bus, cpu->status.raw | 0b00110000); cpu->status.id = 1; cpu->pc.hi = readBus(cpu->bus, 0xFFFF); cpu->pc.lo = readBus(cpu->bus, 0xFFFE); } break; case BVC: { if (!cpu->status.overflow) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BVS: { if (cpu->status.overflow) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case BIT: { fetch(cpu); cpu->status.negative = (cpu->fetchedVal >> 7); cpu->status.overflow = (cpu->fetchedVal >> 6); cpu->status.zero = ((cpu->acc & cpu->fetchedVal) == 0x00); } break; case BNE: { if (!cpu->status.zero) { Word target = cpu->pc.word + cpu->fetchedRelAddress; cpu->remainingCycles++; if ((target & 0xFF00) != (cpu->pc.word & 0xFF00)) cpu->remainingCycles++; cpu->pc.word = target; } } break; case CLC: { cpu->status.carry = 0; } break; case CLD: { cpu->status.decimal = 0; } break; case CLV: { cpu->status.overflow = 0; } break; case CMP: { fetch(cpu); Byte result = cpu->acc - cpu->fetchedVal; cpu->status.negative = (result >> 7); cpu->status.zero = (result == 0x00); cpu->status.carry = (cpu->fetchedVal <= cpu->acc); } break; case CPX: { fetch(cpu); Byte result = cpu->x - cpu->fetchedVal; cpu->status.negative = (result >> 7); cpu->status.zero = (result == 0x00); cpu->status.carry = (cpu->fetchedVal <= cpu->x); } break; case CPY: { fetch(cpu); Byte result = cpu->y - cpu->fetchedVal; cpu->status.negative = (result >> 7); cpu->status.zero = (result == 0x00); cpu->status.carry = (cpu->fetchedVal <= cpu->y); } break; case DCP: { fetch(cpu); cpu->fetchedVal--; writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); Byte result = cpu->acc - cpu->fetchedVal; cpu->status.negative = (result >> 7); cpu->status.zero = (result == 0x00); cpu->status.carry = (cpu->fetchedVal <= cpu->acc); cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case DEC: { fetch(cpu); cpu->fetchedVal--; cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80); cpu->status.zero = (cpu->fetchedVal == 0x00); writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); } break; case DEX: { cpu->x--; cpu->status.negative = ((cpu->x & 0x80) == 0x80); cpu->status.zero = (cpu->x == 0x00); } break; case DEY: { cpu->y--; cpu->status.negative = ((cpu->y & 0x80) == 0x80); cpu->status.zero = (cpu->y == 0x00); } break; case EOR: { fetch(cpu); cpu->acc ^= cpu->fetchedVal; cpu->status.negative = (cpu->acc >> 7); cpu->status.zero = (cpu->acc == 0x00); } break; case INC: { fetch(cpu); cpu->fetchedVal++; cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80); cpu->status.zero = (cpu->fetchedVal == 0x00); writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); } break; case INX: { cpu->x++; cpu->status.negative = ((cpu->x & 0x80) == 0x80); cpu->status.zero = (cpu->x == 0x00); } break; case INY: { cpu->y++; cpu->status.negative = ((cpu->y & 0x80) == 0x80); cpu->status.zero = (cpu->y == 0x00); } break; case ISC: { fetch(cpu); cpu->fetchedVal++; writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); Word result = cpu->acc + ~cpu->fetchedVal + cpu->status.carry; cpu->status.carry = ((result & 0x8000) != 0x8000); cpu->status.overflow = ((~(cpu->acc ^ ~cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80); cpu->status.negative = ((result & 0x80) == 0x80); cpu->status.zero = ((result & 0xFF) == 0x00); cpu->acc = result & 0xFF; cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case JMP: { cpu->pc.word = cpu->fetchedAddress; } break; case JSR: { cpu->pc.word--; Push(cpu->bus, cpu->pc.hi); Push(cpu->bus, cpu->pc.lo); cpu->pc.word = cpu->fetchedAddress; } break; case LAX: { fetch(cpu); cpu->acc = cpu->fetchedVal; cpu->x = cpu->fetchedVal; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); } break; case LDA: { fetch(cpu); cpu->acc = cpu->fetchedVal; cpu->status.negative = (cpu->acc >> 7); cpu->status.zero = (cpu->acc == 0x00); } break; case LDX: { fetch(cpu); cpu->x = cpu->fetchedVal; cpu->status.negative = (cpu->x >> 7); cpu->status.zero = (cpu->x == 0x00); } break; case LDY: { fetch(cpu); cpu->y = cpu->fetchedVal; cpu->status.negative = (cpu->y >> 7); cpu->status.zero = (cpu->y == 0x00); } break; case LSR: { fetch(cpu); cpu->status.negative = 0; cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01); cpu->fetchedVal >>= 1; cpu->status.zero = (cpu->fetchedVal == 0x00); if (!cpu->currentOpcode->addr == ACC) writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); else cpu->acc = cpu->fetchedVal; } break; case NOP: { } break; case ORA: { fetch(cpu); cpu->acc |= cpu->fetchedVal; cpu->status.negative = (cpu->acc >> 7); cpu->status.zero = (cpu->acc == 0x00); } break; case PHA: { Push(cpu->bus, cpu->acc); } break; case PHP: { Push(cpu->bus, cpu->status.raw | 0b00110000); } break; case PLA: { cpu->acc = Pop(cpu->bus); cpu->status.negative = (cpu->acc >> 7); cpu->status.zero = (cpu->acc == 0x00); } break; case PLP: { cpu->status.raw = Pop(cpu->bus) | 0b00110000; } break; case RLA: { fetch(cpu); Byte oldCarry = cpu->status.carry; cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80); cpu->fetchedVal <<= 1; cpu->fetchedVal |= oldCarry; writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); cpu->acc &= cpu->fetchedVal; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case ROL: { fetch(cpu); Byte oldCarry = cpu->status.carry; cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80); cpu->fetchedVal <<= 1; cpu->fetchedVal |= oldCarry; cpu->status.negative = ((cpu->fetchedVal & 0x80) == 0x80); cpu->status.zero = (cpu->fetchedVal == 0x00); if (!cpu->currentOpcode->addr == ACC) writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); else cpu->acc = cpu->fetchedVal; } break; case ROR: { fetch(cpu); Byte oldCarry = cpu->status.carry; cpu->status.negative = oldCarry; cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01); cpu->fetchedVal >>= 1; cpu->fetchedVal |= (oldCarry << 7); cpu->status.zero = (cpu->fetchedVal == 0x00); if (!cpu->currentOpcode->addr == ACC) writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); else cpu->acc = cpu->fetchedVal; } break; case RRA: { fetch(cpu); Byte oldCarry = cpu->status.carry; cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01); cpu->fetchedVal >>= 1; cpu->fetchedVal |= (oldCarry << 7); writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); Word result = cpu->acc + cpu->fetchedVal + cpu->status.carry; cpu->status.carry = (result > 0xFF); cpu->status.overflow = ((~(cpu->acc ^ cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80); cpu->status.negative = (result >> 7); cpu->status.zero = ((result & 0xFF) == 0x00); cpu->acc = result & 0xFF; cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case RTI: { cpu->status.raw = Pop(cpu->bus) | 0b00110000; cpu->pc.lo = Pop(cpu->bus); cpu->pc.hi = Pop(cpu->bus); } break; case RTS: { cpu->pc.lo = Pop(cpu->bus); cpu->pc.hi = Pop(cpu->bus); cpu->pc.word++; } break; case SAX: { writeBus(cpu->bus, cpu->fetchedAddress, cpu->acc & cpu->x); } break; case SBC: { fetch(cpu); Word result = cpu->acc + ~cpu->fetchedVal + cpu->status.carry; cpu->status.carry = ((result & 0x8000) != 0x8000); cpu->status.overflow = ((~(cpu->acc ^ ~cpu->fetchedVal) & (cpu->acc ^ result) & 0x80) == 0x80); cpu->status.negative = ((result & 0x80) == 0x80); cpu->status.zero = ((result & 0xFF) == 0x00); cpu->acc = result & 0xFF; } break; case SEC: { cpu->status.carry = 1; } break; case SED: { cpu->status.decimal = 1; } break; case SEI: { cpu->status.id = 1; } break; case SLO: { fetch(cpu); cpu->status.carry = ((cpu->fetchedVal & 0x80) == 0x80); cpu->fetchedVal <<= 1; writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); cpu->acc |= cpu->fetchedVal; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case SRE: { fetch(cpu); cpu->status.carry = ((cpu->fetchedVal & 0x01) == 0x01); cpu->fetchedVal >>= 1; writeBus(cpu->bus, cpu->fetchedAddress, cpu->fetchedVal); cpu->acc ^= cpu->fetchedVal; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); cpu->remainingCycles = cpu->currentOpcode->cycles; } break; case STA: { writeBus(cpu->bus, cpu->fetchedAddress, cpu->acc); cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it) } break; case STX: { writeBus(cpu->bus, cpu->fetchedAddress, cpu->x); cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it) } break; case STY: { writeBus(cpu->bus, cpu->fetchedAddress, cpu->y); cpu->remainingCycles = cpu->currentOpcode->cycles; // Correct "oops" cycle (store instr. always have it) } break; case TAX: { cpu->x = cpu->acc; cpu->status.negative = ((cpu->x & 0x80) == 0x80); cpu->status.zero = (cpu->x == 0x00); } break; case TAY: { cpu->y = cpu->acc; cpu->status.negative = ((cpu->y & 0x80) == 0x80); cpu->status.zero = (cpu->y == 0x00); } break; case TSX: { cpu->x = cpu->sp; cpu->status.negative = ((cpu->x & 0x80) == 0x80); cpu->status.zero = (cpu->x == 0x00); } break; case TXA: { cpu->acc = cpu->x; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); } break; case TXS: { cpu->sp = cpu->x; } break; case TYA: { cpu->acc = cpu->y; cpu->status.negative = ((cpu->acc & 0x80) == 0x80); cpu->status.zero = (cpu->acc == 0x00); } break; default: fprintf(stderr, "Unknown instruction: %s", cpu->currentOpcode->str); exit(1); break; } } void IRQ(struct CPU* cpu) { cpu->irq = 1; } void NMI(struct CPU* cpu) { cpu->nmi = 1; }