quickerNES/core/Nes_Cpu.h

127 lines
3.1 KiB
C++

// NES 6502 CPU emulator
// Nes_Emu 0.7.0
#ifndef NES_CPU_H
#define NES_CPU_H
#include <stdint.h>
#include "blargg_common.h"
typedef long nes_time_t; // clock cycle count
typedef unsigned nes_addr_t; // 16-bit address
class Nes_Cpu {
public:
// Clear registers, unmap memory, and map code pages to unmapped_page.
void reset( void const* unmapped_page = 0 );
// Map code memory (memory accessed via the program counter). Start and size
// must be multiple of page_size.
enum { page_bits = 11 };
enum { page_count = 0x10000 >> page_bits };
enum { page_size = 1L << page_bits };
void map_code( nes_addr_t start, unsigned size, void const* code );
// Access memory as the emulated CPU does.
int read( nes_addr_t );
void write( nes_addr_t, int data );
uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code
// Push a byte on the stack
void push_byte( int );
// NES 6502 registers. *Not* kept updated during a call to run().
struct registers_t {
long pc; // more than 16 bits to allow overflow detection
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t status;
uint8_t sp;
};
//registers_t r;
// Reasons that run() returns
enum result_t {
result_cycles, // Requested number of cycles (or more) were executed
result_sei, // I flag just set and IRQ time would generate IRQ now
result_cli, // I flag just cleared but IRQ should occur *after* next instr
result_badop // unimplemented/illegal instruction
};
result_t run( nes_time_t end_time );
nes_time_t time() const { return clock_count; }
void reduce_limit( int offset );
void set_end_time_( nes_time_t t );
void set_irq_time_( nes_time_t t );
unsigned long error_count() const { return error_count_; }
// If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped.
enum { page_wrap_opcode = 0xF2 };
// One of the many opcodes that are undefined and stop CPU emulation.
enum { bad_opcode = 0xD2 };
private:
uint8_t const* code_map [page_count + 1];
nes_time_t clock_limit;
nes_time_t clock_count;
nes_time_t irq_time_;
nes_time_t end_time_;
unsigned long error_count_;
enum { irq_inhibit = 0x04 };
void set_code_page( int, uint8_t const* );
void update_clock_limit();
public:
registers_t r;
// low_mem is a full page size so it can be mapped with code_map
uint8_t low_mem [page_size > 0x800 ? page_size : 0x800];
};
inline uint8_t* Nes_Cpu::get_code( nes_addr_t addr )
{
return (uint8_t*) code_map [addr >> page_bits] + addr;
}
inline void Nes_Cpu::update_clock_limit()
{
nes_time_t t = end_time_;
if ( t > irq_time_ && !(r.status & irq_inhibit) )
t = irq_time_;
clock_limit = t;
}
inline void Nes_Cpu::set_end_time_( nes_time_t t )
{
end_time_ = t;
update_clock_limit();
}
inline void Nes_Cpu::set_irq_time_( nes_time_t t )
{
irq_time_ = t;
update_clock_limit();
}
inline void Nes_Cpu::reduce_limit( int offset )
{
clock_limit -= offset;
end_time_ -= offset;
irq_time_ -= offset;
}
inline void Nes_Cpu::push_byte( int data )
{
int sp = r.sp;
r.sp = (sp - 1) & 0xFF;
low_mem [0x100 + sp] = data;
}
#endif