2024-04-30 12:28:43 -04:00
|
|
|
//#include "log.h"
|
2024-01-06 14:27:09 -05:00
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
2023-11-26 12:11:49 -05:00
|
|
|
#include "../include/cpu.h"
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "memory.h"
|
2023-12-23 16:35:23 -05:00
|
|
|
#include "op.h"
|
2024-01-09 15:56:54 -05:00
|
|
|
#include "decoding.h"
|
2024-04-30 12:28:43 -04:00
|
|
|
#include "log.h"
|
2023-11-26 12:11:49 -05:00
|
|
|
|
2023-09-21 23:53:14 -04:00
|
|
|
/*
|
|
|
|
* =====================================================================================
|
|
|
|
*
|
2024-05-06 20:23:44 -04:00
|
|
|
* Filename: cpu_state.c
|
2023-09-21 23:53:14 -04:00
|
|
|
*
|
|
|
|
* Description: 6502 CPU emulator
|
|
|
|
*
|
|
|
|
* Version: 1.0
|
|
|
|
* Created: 2023-09-21 10:10:26 PM
|
|
|
|
* Revision: none
|
|
|
|
* Compiler: gcc
|
|
|
|
*
|
|
|
|
* Author: William Nolin,
|
|
|
|
* Organization:
|
|
|
|
*
|
|
|
|
* =====================================================================================
|
|
|
|
*/
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
CPU cpu_state;
|
|
|
|
|
|
|
|
void cpu_init() {
|
|
|
|
cpu_state.program_counter = 0x8000;
|
|
|
|
cpu_state.stack_pointer = 0xfd;
|
|
|
|
cpu_state.accumulator = 0x00;
|
|
|
|
cpu_state.x = 0x00;
|
|
|
|
cpu_state.y = 0x00;
|
|
|
|
cpu_state.status = 0x04;
|
|
|
|
cpu_state.oam_dma_triggered = false;
|
|
|
|
cpu_state.nmi_requested = false;
|
2024-01-06 14:27:09 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void print_registers(byte op, unsigned long cycle_count) {
|
|
|
|
log_info("%#02x %#02x %s \t A:%#02x X:%#02x Y:%#02x F:%#02x SP:%#02x \t [%d]",
|
|
|
|
cpu_state.program_counter,
|
2024-01-06 14:27:09 -05:00
|
|
|
op,
|
|
|
|
get_op_code_name(op),
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.accumulator,
|
|
|
|
cpu_state.x,
|
|
|
|
cpu_state.y,
|
|
|
|
cpu_state.status,
|
|
|
|
cpu_state.stack_pointer,
|
2024-01-06 14:27:09 -05:00
|
|
|
cycle_count);
|
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_process_nmi() {
|
|
|
|
cpu_stack_push_context();
|
2024-05-04 22:16:12 -04:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
address handler_addr = mem_get_word(0xfffa);
|
2024-05-04 22:16:12 -04:00
|
|
|
log_debug("NMI %#04x", handler_addr);
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.nmi_requested = false;
|
|
|
|
cpu_state.program_counter = handler_addr;
|
2024-05-04 22:16:12 -04:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void oam_dma_upload() {
|
|
|
|
byte page_high_addr = *ppu_get_state()->oam_dma_register; // TODO
|
2024-01-06 14:27:09 -05:00
|
|
|
address page_addr = ((address) page_high_addr) << 8;
|
|
|
|
byte n = 0xff;
|
2023-11-26 12:11:49 -05:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
byte *ram_source = mem_get_ptr(page_addr);
|
|
|
|
byte *oam_destination = ppu_get_state()->oam;
|
2023-12-23 16:35:23 -05:00
|
|
|
|
2024-01-06 14:27:09 -05:00
|
|
|
memcpy(oam_destination, ram_source, n);
|
|
|
|
|
|
|
|
log_debug("OAM DMA %#04x", page_addr);
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_add_cycles(513); // TODO
|
2023-12-23 16:35:23 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_cycle() {
|
|
|
|
if (cpu_state.nmi_requested) {
|
|
|
|
cpu_process_nmi();
|
2024-05-04 22:16:12 -04:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
if (cpu_state.oam_dma_triggered) {
|
|
|
|
oam_dma_upload();
|
|
|
|
cpu_state.oam_dma_triggered = false;
|
2024-01-06 14:27:09 -05:00
|
|
|
return;
|
2023-12-23 16:35:23 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
CPU cpu = cpu_state;
|
|
|
|
byte op = cpu_get_next_byte();
|
2024-01-06 14:27:09 -05:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
print_registers(op, system_get_cycles());
|
2024-01-06 14:27:09 -05:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
process_op_code(op);
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_add_cycles(unsigned int cycle_count) {
|
|
|
|
system_add_cycles(cycle_count);
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
|
2024-01-06 14:27:09 -05:00
|
|
|
// === Registers ===
|
2024-05-06 20:23:44 -04:00
|
|
|
bool cpu_get_flag(byte mask) {
|
|
|
|
return cpu_state.status & mask;
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_set_flag(byte mask, bool set) {
|
2023-11-26 12:11:49 -05:00
|
|
|
if (set) {
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.status |= mask;
|
2023-11-26 12:11:49 -05:00
|
|
|
} else {
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.status &= ~mask;
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
}
|
2023-09-22 14:39:25 -04:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
byte cpu_get_next_byte() {
|
|
|
|
byte next_byte = mem_get_byte(cpu_state.program_counter);
|
|
|
|
cpu_state.program_counter++;
|
2023-11-26 12:11:49 -05:00
|
|
|
return next_byte;
|
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
word cpu_get_next_word() {
|
|
|
|
word next_word = mem_get_word(cpu_state.program_counter);
|
|
|
|
cpu_state.program_counter += 2;
|
2023-11-26 12:11:49 -05:00
|
|
|
return next_word;
|
|
|
|
}
|
2023-09-21 23:53:14 -04:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_stack_push(byte value) {
|
|
|
|
assert(cpu_state.stack_pointer > 0);
|
2023-09-21 23:53:14 -04:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
|
|
|
|
mem_set_byte(mem_addr, value);
|
|
|
|
cpu_state.stack_pointer--;
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
byte cpu_stack_pop() {
|
|
|
|
assert(cpu_state.stack_pointer < 0xff);
|
2023-11-26 12:11:49 -05:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.stack_pointer++;
|
|
|
|
address mem_addr = CPU_STACK_ADDR | cpu_state.stack_pointer;
|
|
|
|
byte value = mem_get_byte(mem_addr);
|
2023-11-26 12:11:49 -05:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_stack_push_context() {
|
|
|
|
cpu_stack_push(cpu_state.program_counter >> 8);
|
|
|
|
cpu_stack_push(cpu_state.program_counter & 0xff);
|
|
|
|
cpu_stack_push(cpu_state.status);
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
void cpu_stack_pop_context() {
|
|
|
|
byte value = cpu_stack_pop();
|
2024-01-06 14:27:09 -05:00
|
|
|
value &= 0xef; // The B mask cannot be set as it is a CPU signal
|
|
|
|
value |= 0x20; // This value is always set
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.status = value;
|
2023-11-26 12:11:49 -05:00
|
|
|
|
2024-05-06 20:23:44 -04:00
|
|
|
byte lo = cpu_stack_pop();
|
|
|
|
address pc = cpu_stack_pop() << 8;
|
2023-11-26 12:11:49 -05:00
|
|
|
pc += lo;
|
2024-05-06 20:23:44 -04:00
|
|
|
cpu_state.program_counter = pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpu_trigger_oam_dma() {
|
|
|
|
cpu_state.oam_dma_triggered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpu_trigger_nmi() {
|
|
|
|
cpu_state.nmi_requested = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPU *cpu_get_state() {
|
|
|
|
return &cpu_state;
|
2023-11-26 12:11:49 -05:00
|
|
|
}
|