diff --git a/NES Emulator/bus.c b/NES Emulator/bus.c index cbce8d5..9144e5a 100644 --- a/NES Emulator/bus.c +++ b/NES Emulator/bus.c @@ -1,6 +1,7 @@ #include "bus.h" #include #include +#include #include "cpu.h" #include "cartridge.h" @@ -21,6 +22,15 @@ struct Bus* createBus() fprintf(stderr, "Failed to allocate memory for NES RAM, aborting."); exit(1); } + memset(bus->ram, 0x00, 0x800); + + bus->io = (Byte*)malloc(0x18); + if (bus->io == NULL) + { + fprintf(stderr, "Failed to allocate memory for NES I/O, aborting."); + exit(1); + } + memset(bus->ram, 0x00, 0x18); // Create CPU and attach it bus->cpu = createCPU(bus); @@ -38,6 +48,7 @@ void destroyBus(struct Bus* bus) destroyCartridge(bus->cartridge); destroyCPU(bus->cpu); + free(bus->io); free(bus->ram); free(bus); } @@ -51,6 +62,10 @@ Byte readBus(struct Bus* bus, Word addr) { val = bus->ram[addr & 0x7FF]; } + else if (0x4000 <= addr && addr <= 0x4017) // I/O space + { + val = bus->io[addr - 0x4000]; + } else if (0x4020 <= addr && addr <= 0xFFFF) // Cartridge space { val = readCartridge(bus->cartridge, addr); @@ -71,6 +86,10 @@ void writeBus(struct Bus* bus, Word addr, Byte val) { bus->ram[addr & 0x7FF] = val; } + else if (0x4000 <= addr && addr <= 0x4017) // I/O space + { + bus->io[addr - 0x4000] = val; + } else if (0x4020 <= addr && addr <= 0xFFFF) // Cartridge space { writeCartridge(bus->cartridge, addr, val); diff --git a/NES Emulator/bus.h b/NES Emulator/bus.h index 93cc9ad..ceafd6a 100644 --- a/NES Emulator/bus.h +++ b/NES Emulator/bus.h @@ -10,6 +10,7 @@ struct Cartridge; struct Bus { Byte* ram; + Byte* io; struct CPU* cpu; struct Cartridge* cartridge; diff --git a/NES Emulator/cartridge.c b/NES Emulator/cartridge.c index 5cc1f40..828a837 100644 --- a/NES Emulator/cartridge.c +++ b/NES Emulator/cartridge.c @@ -85,7 +85,7 @@ Byte readCartridge(struct Cartridge* cartridge, Word addr) } else if (0x8000 <= addr && addr <= 0xFFFF) // PRG ROM { - val = cartridge->prg_rom[addr & 0x1FFF]; + val = cartridge->prg_rom[addr & 0x3FFF]; } return val; diff --git a/NES Emulator/cpu.c b/NES Emulator/cpu.c index 0e5e191..1202b3f 100644 --- a/NES Emulator/cpu.c +++ b/NES Emulator/cpu.c @@ -5,6 +5,16 @@ #include "log.h" #include "bus.h" +inline void Push(struct Bus* bus, Byte val) +{ + writeBus(bus, 0x0100 + (bus->cpu->sp--), val); +} + +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)); @@ -15,12 +25,13 @@ struct CPU* createCPU(struct Bus* parent) } // TODO: THIS IS JUST FOR THE TEST ROM - cpu->pc = 0xC000; + cpu->pc.word = 0xC000; cpu->status.raw = 0x34; cpu->acc = 0; cpu->x = 0; cpu->y = 0; + cpu->sp = 0xFD; cpu->remainingCycles = 7; cpu->totalCycles = 0; @@ -56,34 +67,35 @@ void tickInstr(struct CPU* cpu) void fetch(struct CPU* cpu) { - Byte opcodeVal = readBus(cpu->bus, cpu->pc++); + Byte opcodeVal = readBus(cpu->bus, cpu->pc.word); cpu->currentOpcode = OPCODE_TABLE + opcodeVal; if (cpu->currentOpcode->op == XXX) { - fprintf(stderr, "Unknown opcode: %x", opcodeVal); + 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; - break; + return; case ABS: { - Byte lo = readBus(cpu->bus, cpu->pc++); - Byte hi = readBus(cpu->bus, cpu->pc++); + 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++); - Byte hi = readBus(cpu->bus, cpu->pc++); + 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; @@ -95,8 +107,8 @@ void fetch(struct CPU* cpu) case ABY: { - Byte lo = readBus(cpu->bus, cpu->pc++); - Byte hi = readBus(cpu->bus, cpu->pc++); + 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; @@ -107,40 +119,38 @@ void fetch(struct CPU* cpu) } break; case IMM: - cpu->fetchedVal = readBus(cpu->bus, cpu->pc++); - break; + cpu->fetchedVal = readBus(cpu->bus, cpu->pc.word++); + return; case IMP: - break; + return; case IND: { - Byte lo = readBus(cpu->bus, cpu->pc++); - Byte hi = readBus(cpu->bus, cpu->pc++); - Word addr = ((Word)hi << 8) | lo; + Byte lo = readBus(cpu->bus, cpu->pc.word++); + Byte hi = readBus(cpu->bus, cpu->pc.word++); + Word addr = ((Word)hi << 8); - cpu->fetchedAddress = readBus(cpu->bus, addr); + 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++); - Byte lo = readBus(cpu->bus, op + cpu->x); - Byte hi = readBus(cpu->bus, op + cpu->x + 1); - Word addr = ((Word)hi << 8) | lo; + 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 = readBus(cpu->bus, addr); + cpu->fetchedAddress = ((Word)hi << 8) | lo; } break; case INDY: { - Byte op = readBus(cpu->bus, cpu->pc++); + Byte op = readBus(cpu->bus, cpu->pc.word++); Byte lo = readBus(cpu->bus, op); - Byte hi = readBus(cpu->bus, op + 1); + Byte hi = readBus(cpu->bus, (op + 1) & 0xFF); Word addr = ((Word)hi << 8) | lo; - addr = readBus(cpu->bus, addr); cpu->fetchedAddress = addr + cpu->y; @@ -151,26 +161,26 @@ void fetch(struct CPU* cpu) case REL: { - cpu->fetchedRelAddress = readBus(cpu->bus, cpu->pc++); + cpu->fetchedRelAddress = readBus(cpu->bus, cpu->pc.word++); } break; case ZPG: - cpu->fetchedAddress = readBus(cpu->bus, cpu->pc++); + cpu->fetchedAddress = readBus(cpu->bus, cpu->pc.word++); break; case ZPX: { - cpu->fetchedAddress = readBus(cpu->bus, cpu->pc++) + cpu->x; - cpu->fetchedAddress |= 0xFF; + cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->x) & 0xFF; } break; case ZPY: { - cpu->fetchedAddress = readBus(cpu->bus, cpu->pc++) + cpu->y; - cpu->fetchedAddress |= 0xFF; + cpu->fetchedAddress = (readBus(cpu->bus, cpu->pc.word++) + cpu->y) & 0xFF; } break; } + + cpu->fetchedVal = readBus(cpu->bus, cpu->fetchedAddress); } void execute(struct CPU* cpu) @@ -179,9 +189,489 @@ void execute(struct CPU* cpu) switch (cpu->currentOpcode->op) { + case ADC: + { + 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: + { + cpu->acc &= cpu->fetchedVal; + + cpu->status.negative = (cpu->acc >> 7); + cpu->status.zero = (cpu->acc == 0x00); + } break; + + case ASL: + { + 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 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: + { + 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: + { + 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: + { + 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: + { + 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 DEC: + { + 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 == 0x80); + } break; + + case DEY: + { + cpu->y--; + + cpu->status.negative = ((cpu->y & 0x80) == 0x80); + cpu->status.zero = (cpu->y == 0x80); + } break; + + case EOR: + { + cpu->acc ^= cpu->fetchedVal; + + cpu->status.negative = (cpu->acc >> 7); + cpu->status.zero = (cpu->acc == 0x00); + } break; + + case INC: + { + 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 JMP: { - cpu->pc = cpu->fetchedAddress; + 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 LDA: + { + cpu->acc = cpu->fetchedVal; + + cpu->status.negative = (cpu->acc >> 7); + cpu->status.zero = (cpu->acc == 0x00); + } break; + + case LDX: + { + cpu->x = cpu->fetchedVal; + + cpu->status.negative = (cpu->x >> 7); + cpu->status.zero = (cpu->x == 0x00); + } break; + + case LDY: + { + cpu->y = cpu->fetchedVal; + + cpu->status.negative = (cpu->y >> 7); + cpu->status.zero = (cpu->y == 0x00); + } break; + + case LSR: + { + 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: + { + 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 ROL: + { + 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: + { + 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 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 SBC: + { + 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 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: diff --git a/NES Emulator/cpu.h b/NES Emulator/cpu.h index ac92e4f..4996b0c 100644 --- a/NES Emulator/cpu.h +++ b/NES Emulator/cpu.h @@ -57,7 +57,16 @@ struct CPU Byte raw; } status; - Word pc; + union + { + struct + { + Byte lo; + Byte hi; + }; + + Word word; + } pc; Byte remainingCycles; size_t totalCycles; diff --git a/NES Emulator/log.c b/NES Emulator/log.c index d91304c..f923fd5 100644 --- a/NES Emulator/log.c +++ b/NES Emulator/log.c @@ -8,7 +8,7 @@ void logBusState(struct Bus* bus) { const char buffer[32]; - Word oldPC = bus->cpu->pc - bus->cpu->currentOpcode->length; + Word oldPC = bus->cpu->pc.word - bus->cpu->currentOpcode->length; printf("%04X ", oldPC); @@ -27,11 +27,11 @@ void logBusState(struct Bus* bus) case ABS: sprintf(buffer, "$%04X", bus->cpu->fetchedAddress); break; case ABX: sprintf(buffer, "$%04X, X -> $%04X", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; case ABY: sprintf(buffer, "$%04X, Y -> $%04X", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; - case IMM: sprintf(buffer, "#$%04X", bus->cpu->fetchedVal); break; + case IMM: sprintf(buffer, "#$%02X", bus->cpu->fetchedVal); break; case IMP: sprintf(buffer, ""); break; - case IND: sprintf(buffer, "($%04X) -> $%04x", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; - case INDX: sprintf(buffer, "($%04X, X) -> $%04x", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; - case INDY: sprintf(buffer, "($%04X), Y -> $%04x", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; + case IND: sprintf(buffer, "($%04X) -> $%04X", (instructionBytes[2] << 8) | instructionBytes[1], bus->cpu->fetchedAddress); break; + case INDX: sprintf(buffer, "($%02X, X) -> $%04X", instructionBytes[1], bus->cpu->fetchedAddress); break; + case INDY: sprintf(buffer, "($%02X), Y -> $%04X", instructionBytes[1], bus->cpu->fetchedAddress); break; case REL: sprintf(buffer, "$%02X", bus->cpu->fetchedRelAddress); break; case ZPG: sprintf(buffer, "$%02X", bus->cpu->fetchedAddress); break; case ZPX: sprintf(buffer, "$%02X, X -> $%02X", instructionBytes[1], bus->cpu->fetchedAddress); break;