diff --git a/src/cpu.c b/src/cpu.c index e69de29..7d72a7d 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -0,0 +1,27 @@ +#include "cpu.h" +#include "memory.h" + +// CPU Functions +void cpu_init(CPU* cpu) { + // Initialize registers + cpu->A = 0; + cpu->X = 0; + cpu->Y = 0; + cpu->SP = 0xFF; // Stack pointer starts at 0xFF + cpu->P = FLAG_UNUSED; // Status flags initialized with unused bit set + cpu->PC = 0; // Program counter starts at 0 + cpu->cycles = 0; +} + +// Status flag operations +bool cpu_get_flag(CPU* cpu, uint8_t flag) { + return (cpu->P & flag) != 0; +} + +void cpu_set_flag(CPU* cpu, uint8_t flag, bool value) { + if (value) { + cpu->P |= flag; + } else { + cpu->P &= ~flag; + } +} diff --git a/src/cpu.h b/src/cpu.h new file mode 100644 index 0000000..f3a51fa --- /dev/null +++ b/src/cpu.h @@ -0,0 +1,31 @@ +#ifndef CPU_H +#define CPU_H + +#include +#include + +// CPU Status Flags +#define FLAG_CARRY (1 << 0) +#define FLAG_ZERO (1 << 1) +#define FLAG_INTERRUPT (1 << 2) +#define FLAG_DECIMAL (1 << 3) +#define FLAG_BREAK (1 << 4) +#define FLAG_UNUSED (1 << 5) +#define FLAG_OVERFLOW (1 << 6) +#define FLAG_NEGATIVE (1 << 7) + +typedef struct { + uint8_t A; // Accumulator + uint8_t X; // X Index Register + uint8_t Y; // Y Index Register + uint8_t SP; // Stack Pointer + uint8_t P; // Status Register + uint16_t PC; // Program Counter + uint64_t cycles; +} CPU; + +void cpu_init(CPU* cpu); +bool cpu_get_flag(CPU* cpu, uint8_t flag); +void cpu_set_flag(CPU* cpu, uint8_t flag, bool value); + +#endif // CPU_H \ No newline at end of file diff --git a/src/emulator.c b/src/emulator.c index e69de29..fd996a4 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -0,0 +1,116 @@ +#include +#include +#include "cpu.h" +#include "memory.h" +#include "instructions.h" + +typedef struct { + CPU cpu; + Memory memory; + bool running; +} Emulator; + +void emulator_init(Emulator* emu) { + cpu_init(&emu->cpu); + memory_init(&emu->memory); + emu->running = false; +} + +void emulator_reset(Emulator* emu) { + // Reset CPU + cpu_init(&emu->cpu); + + // Set Program Counter to the reset vector location (0xFFFC-0xFFFD) + uint16_t reset_vector = (uint16_t)emu->memory.data[0xFFFD] << 8 | emu->memory.data[0xFFFC]; + emu->cpu.PC = reset_vector; + + emu->running = true; +} + +void emulator_step(Emulator* emu) { + if (!emu->running) { + return; + } + + // Fetch + uint8_t opcode = memory_read(&emu->memory, emu->cpu.PC); + emu->cpu.PC++; + + // Decode and Execute + switch (opcode) { + // LDA + case 0xA9: // LDA Immediate + ins_lda(&emu->cpu, &emu->memory, addr_immediate(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 2; + break; + case 0xA5: // LDA Zero Page + ins_lda(&emu->cpu, &emu->memory, addr_zeropage(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 3; + break; + case 0xB5: // LDA Zero Page,X + ins_lda(&emu->cpu, &emu->memory, addr_zeropage_x(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 4; + break; + case 0xAD: // LDA Absolute + ins_lda(&emu->cpu, &emu->memory, addr_absolute(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 4; + break; + + // STA + case 0x85: // STA Zero Page + ins_sta(&emu->cpu, &emu->memory, addr_zeropage(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 3; + break; + case 0x95: // STA Zero Page,X + ins_sta(&emu->cpu, &emu->memory, addr_zeropage_x(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 4; + break; + case 0x8D: // STA Absolute + ins_sta(&emu->cpu, &emu->memory, addr_absolute(&emu->cpu, &emu->memory)); + emu->cpu.cycles += 4; + break; + + // Register Transfers + case 0xAA: // TAX + ins_tax(&emu->cpu); + emu->cpu.cycles += 2; + break; + case 0xA8: // TAY + ins_tay(&emu->cpu); + emu->cpu.cycles += 2; + break; + case 0x8A: // TXA + ins_txa(&emu->cpu); + emu->cpu.cycles += 2; + break; + case 0x98: // TYA + ins_tya(&emu->cpu); + emu->cpu.cycles += 2; + break; + + // Stack Operations + case 0x48: // PHA + ins_pha(&emu->cpu, &emu->memory); + emu->cpu.cycles += 3; + break; + case 0x08: // PHP + ins_php(&emu->cpu, &emu->memory); + emu->cpu.cycles += 3; + break; + case 0x68: // PLA + ins_pla(&emu->cpu, &emu->memory); + emu->cpu.cycles += 4; + break; + case 0x28: // PLP + ins_plp(&emu->cpu, &emu->memory); + emu->cpu.cycles += 4; + break; + } +} + +void emulator_run(Emulator* emu) { + emu->running = true; + while (emu->running) { + emulator_step(emu); + } +} diff --git a/src/instructions.c b/src/instructions.c new file mode 100644 index 0000000..41e32e1 --- /dev/null +++ b/src/instructions.c @@ -0,0 +1,136 @@ +#include "instructions.h" + +// Addressing Modes Implementation +uint16_t addr_immediate(CPU* cpu, Memory* memory) { + return cpu->PC++; +} + +uint16_t addr_zeropage(CPU* cpu, Memory* memory) { + return memory_read(memory, cpu->PC++); +} + +uint16_t addr_zeropage_x(CPU* cpu, Memory* memory) { + uint8_t addr = memory_read(memory, cpu->PC++); + return (uint8_t)(addr + cpu->X); +} + +uint16_t addr_zeropage_y(CPU* cpu, Memory* memory) { + uint8_t addr = memory_read(memory, cpu->PC++); + return (uint8_t)(addr + cpu->Y); +} + +uint16_t addr_absolute(CPU* cpu, Memory* memory) { + uint16_t addr_low = memory_read(memory, cpu->PC++); + uint16_t addr_high = memory_read(memory, cpu->PC++); + return (addr_high << 8) | addr_low; +} + +uint16_t addr_absolute_x(CPU* cpu, Memory* memory) { + uint16_t base = addr_absolute(cpu, memory); + return base + cpu->X; +} + +uint16_t addr_absolute_y(CPU* cpu, Memory* memory) { + uint16_t base = addr_absolute(cpu, memory); + return base + cpu->Y; +} + +uint16_t addr_indirect(CPU* cpu, Memory* memory) { + uint16_t ptr = addr_absolute(cpu, memory); + // Simulate the 6502 page boundary bug + uint16_t page = ptr & 0xFF00; + uint8_t low = memory_read(memory, ptr); + uint8_t high = memory_read(memory, (ptr & 0xFF00) | ((ptr + 1) & 0xFF)); + return (high << 8) | low; +} + +uint16_t addr_indirect_x(CPU* cpu, Memory* memory) { + uint8_t zero_addr = memory_read(memory, cpu->PC++); + uint8_t ptr = (uint8_t)(zero_addr + cpu->X); + uint16_t low = memory_read(memory, ptr); + uint16_t high = memory_read(memory, (uint8_t)(ptr + 1)); + return (high << 8) | low; +} + +uint16_t addr_indirect_y(CPU* cpu, Memory* memory) { + uint8_t zero_addr = memory_read(memory, cpu->PC++); + uint16_t low = memory_read(memory, zero_addr); + uint16_t high = memory_read(memory, (uint8_t)(zero_addr + 1)); + uint16_t base = (high << 8) | low; + return base + cpu->Y; +} + +// Load/Store Instructions +void ins_lda(CPU* cpu, Memory* memory, uint16_t addr) { + cpu->A = memory_read(memory, addr); + cpu_set_flag(cpu, FLAG_ZERO, cpu->A == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->A & 0x80) != 0); +} + +void ins_ldx(CPU* cpu, Memory* memory, uint16_t addr) { + cpu->X = memory_read(memory, addr); + cpu_set_flag(cpu, FLAG_ZERO, cpu->X == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->X & 0x80) != 0); +} + +void ins_ldy(CPU* cpu, Memory* memory, uint16_t addr) { + cpu->Y = memory_read(memory, addr); + cpu_set_flag(cpu, FLAG_ZERO, cpu->Y == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->Y & 0x80) != 0); +} + +void ins_sta(CPU* cpu, Memory* memory, uint16_t addr) { + memory_write(memory, addr, cpu->A); +} + +void ins_stx(CPU* cpu, Memory* memory, uint16_t addr) { + memory_write(memory, addr, cpu->X); +} + +void ins_sty(CPU* cpu, Memory* memory, uint16_t addr) { + memory_write(memory, addr, cpu->Y); +} + +// Register Transfer Instructions +void ins_tax(CPU* cpu) { + cpu->X = cpu->A; + cpu_set_flag(cpu, FLAG_ZERO, cpu->X == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->X & 0x80) != 0); +} + +void ins_tay(CPU* cpu) { + cpu->Y = cpu->A; + cpu_set_flag(cpu, FLAG_ZERO, cpu->Y == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->Y & 0x80) != 0); +} + +void ins_txa(CPU* cpu) { + cpu->A = cpu->X; + cpu_set_flag(cpu, FLAG_ZERO, cpu->A == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->A & 0x80) != 0); +} + +void ins_tya(CPU* cpu) { + cpu->A = cpu->Y; + cpu_set_flag(cpu, FLAG_ZERO, cpu->A == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->A & 0x80) != 0); +} + +// Stack Operations +void ins_pha(CPU* cpu, Memory* memory) { + memory_stack_push(memory, &cpu->SP, cpu->A); +} + +void ins_php(CPU* cpu, Memory* memory) { + memory_stack_push(memory, &cpu->SP, cpu->P | FLAG_BREAK | FLAG_UNUSED); +} + +void ins_pla(CPU* cpu, Memory* memory) { + cpu->A = memory_stack_pull(memory, &cpu->SP); + cpu_set_flag(cpu, FLAG_ZERO, cpu->A == 0); + cpu_set_flag(cpu, FLAG_NEGATIVE, (cpu->A & 0x80) != 0); +} + +void ins_plp(CPU* cpu, Memory* memory) { + cpu->P = (memory_stack_pull(memory, &cpu->SP) & ~FLAG_BREAK) | FLAG_UNUSED; +} \ No newline at end of file diff --git a/src/instructions.h b/src/instructions.h new file mode 100644 index 0000000..25951b0 --- /dev/null +++ b/src/instructions.h @@ -0,0 +1,37 @@ +#ifndef INSTRUCTIONS_H +#define INSTRUCTIONS_H + +#include "cpu.h" +#include "memory.h" + +// Addressing Modes +uint16_t addr_immediate(CPU* cpu, Memory* memory); +uint16_t addr_zeropage(CPU* cpu, Memory* memory); +uint16_t addr_zeropage_x(CPU* cpu, Memory* memory); +uint16_t addr_zeropage_y(CPU* cpu, Memory* memory); +uint16_t addr_absolute(CPU* cpu, Memory* memory); +uint16_t addr_absolute_x(CPU* cpu, Memory* memory); +uint16_t addr_absolute_y(CPU* cpu, Memory* memory); +uint16_t addr_indirect(CPU* cpu, Memory* memory); +uint16_t addr_indirect_x(CPU* cpu, Memory* memory); +uint16_t addr_indirect_y(CPU* cpu, Memory* memory); + +// Instructions +void ins_lda(CPU* cpu, Memory* memory, uint16_t addr); +void ins_ldx(CPU* cpu, Memory* memory, uint16_t addr); +void ins_ldy(CPU* cpu, Memory* memory, uint16_t addr); +void ins_sta(CPU* cpu, Memory* memory, uint16_t addr); +void ins_stx(CPU* cpu, Memory* memory, uint16_t addr); +void ins_sty(CPU* cpu, Memory* memory, uint16_t addr); +void ins_tax(CPU* cpu); +void ins_tay(CPU* cpu); +void ins_txa(CPU* cpu); +void ins_tya(CPU* cpu); + +// Stack operations +void ins_pha(CPU* cpu, Memory* memory); +void ins_php(CPU* cpu, Memory* memory); +void ins_pla(CPU* cpu, Memory* memory); +void ins_plp(CPU* cpu, Memory* memory); + +#endif // INSTRUCTIONS_H \ No newline at end of file diff --git a/src/memory.c b/src/memory.c index e69de29..cc06955 100644 --- a/src/memory.c +++ b/src/memory.c @@ -0,0 +1,39 @@ +#include +#include + +#define MEMORY_SIZE 65536 // 64K memory space + +typedef struct { + uint8_t data[MEMORY_SIZE]; +} Memory; + +void memory_init(Memory* memory) { + // Initialize all memory to 0 + memset(memory->data, 0, MEMORY_SIZE); +} + +uint8_t memory_read(Memory* memory, uint16_t address) { + return memory->data[address]; +} + +void memory_write(Memory* memory, uint16_t address, uint8_t value) { + memory->data[address] = value; +} + +// Helper function to write multiple bytes to memory +void memory_write_bytes(Memory* memory, uint16_t address, const uint8_t* data, uint16_t length) { + for (uint16_t i = 0; i < length; i++) { + memory_write(memory, address + i, data[i]); + } +} + +// Stack operations (stack is located at 0x0100-0x01FF) +uint8_t memory_stack_pull(Memory* memory, uint8_t* sp) { + (*sp)++; + return memory_read(memory, 0x0100 | *sp); +} + +void memory_stack_push(Memory* memory, uint8_t* sp, uint8_t value) { + memory_write(memory, 0x0100 | *sp, value); + (*sp)--; +} diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..9114539 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,19 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#include + +#define MEMORY_SIZE 65536 + +typedef struct { + uint8_t data[MEMORY_SIZE]; +} Memory; + +void memory_init(Memory* memory); +uint8_t memory_read(Memory* memory, uint16_t address); +void memory_write(Memory* memory, uint16_t address, uint8_t value); +void memory_write_bytes(Memory* memory, uint16_t address, const uint8_t* data, uint16_t length); +uint8_t memory_stack_pull(Memory* memory, uint8_t* sp); +void memory_stack_push(Memory* memory, uint8_t* sp, uint8_t value); + +#endif // MEMORY_H \ No newline at end of file