Skip to content

Instantly share code, notes, and snippets.

@gucchan22
Last active August 29, 2015 14:07
Show Gist options
  • Save gucchan22/20a7d772492eaba830c7 to your computer and use it in GitHub Desktop.
Save gucchan22/20a7d772492eaba830c7 to your computer and use it in GitHub Desktop.
#include <string.h>
#include <stdint.h>
#include <iostream>
#include <fstream>
#include "SDL.h"
class RAM;
class CPU;
class PPU;
class RAM {
VM& vm;
uint8_t mem[2048]; // 2KB RAM
public:
explicit RAM(VM &nes) : vm(nes) {}
~RAM() {}
void hardwareReset() {
// ref: http://wiki.nesdev.com/w/index.php/CPU_power_up_state
memset(mem, 0xff, 2048);
mem[0x0008] = 0xf7;
mem[0x0009] = 0xef;
mem[0x000a] = 0xdf;
mem[0x000f] = 0xbf;
}
void softwareReset() {}
// addr & 0x7ff = addr mod 2048
uint8_t read(const uint16_t addr) const { return mem[addr & 0x7ff]; }
void write(const uint16_t addr, const uint8_t v) { mem[addr & 0x7ff] = v; }
};
/*
0x0000: RAM
0x0800: RAM Mirroring
<- 4回ループ (RAMには0x0000 ~ 0x1ffffまで割当てられてミラーリングされている) ->
0x2000: I/O Port (for PPU)
0x2008: I/O Port (for PPU) Mirroring
<- 1024回ループ (RAMには0x2000 ~ 0x3fffまで割当てられてミラーリングされている) ->
0x4000: I/O Port (for APU)
0x4020: Extend-RAM
0x6000: Savedata
0x8000~0xffff: Cartridge
*/
class VM {
IO &io;
RAM &ram;
CPU &cpu;
PPU &ppu;
ROMController *mmc;
int32_t clock_d;
const int32_t cpu_clock_divisor = 12;
public:
class VirtualMachineAddressException : public std::exception {};
explicit VM(IO& iop, CPU& nes_cpu, RAM& nes_ram, PPU& nes_ppu ROMController *nrom) :
io(iop), cpu(nes_cpu), ram(nes_ram), ppu(nes_ppu), mmc(nrom), clock_d(0) {}
~VM() {}
void addClock(uint32_t op_clock) { clock_d += op_clock; }
void run() {
cpu.run();
//ppu.run(); (Graphics)
//apu.run(); (Audio)
}
void connectChrToPPU() {} // CHR-ROM -> PPU
uint8_t read(uint16_t addr) {
switch(addr & 0xe000) {
case 0x0000: return ram.read(addr);
case 0x2000: return ppu.read(addr);
// for Cartridge (0x8000 ~ 0xa000: Low, 0xc000 ~ 0xe000: High)
case 0x8000:
case 0xa000:
case 0xc000:
case 0xe000:
return mmc->readRom(addr);
default: throw VirtualMachineAdressException;
}
}
void write(uint16_t addr, const uint8_t v) {
switch(addr & 0xe000) {
case 0x0000: ram.write(addr, v); break;
case 0x2000: ppu.write(addr, v); break;
// 0x8000 ~ 0xe000 -> mmc->writeL/H (Mapper-N)
default: throw VirtualMachineAddressException;
}
}
};
/*
I/O Port (Joypad, APU Sound etc.,)
ref: http://hp.vector.co.jp/authors/VA042397/nes/ioport.html
ref: http://wiki.nesdev.com/w/index.php/Input_devices
*/
class IO {
VM& vm;
public:
IO(VM& nes) : vm(nes) {}
~IO() {}
};
/*
iNES (*.nes) ROM File format.
ref: http://wiki.nesdev.com/w/index.php/INES
*/
class INES {
const char *rom_name;
uint8_t *prg_dat = NULL;
uint8_t *chr_dat = NULL;
int prg_page, chr_page, prg_rom_size, chr_rom_size, mapper;
enum MirrorType {
HORIZONTAL, VERTICAL
};
MirrorType mirroring;
bool is_enable_sram, is_enable_trainer, is_enable_four_screen;
bool loaded = false;
public:
explicit INES(const std::string& fname) : rom_name(fname.c_str()) {}
~INES() {
delete [] prg_dat;
delete [] chr_dat;
}
bool loadROM() {
std::ifstream rom(rom_name, std::ios::binary | std::ios::in); // rb
char magic_byte[4];
uint8_t load_size;
int tt, tu;
rom.read(magic_byte, 4);
if(strncmp(magic_byte, "NES\x1A", 4) != 0) {
rom.read(reinterpret_cast<char*>(&tt),1);
rom.read(reinterpret_cast<char*>(&tu),1);
prg_page = tt;
chr_page = tu;
prg_rom_size = prg_page * 0x4000;
chr_rom_size = chr_page * 0x2000;
rom.read(reinterpret_cast<char*>(&tt), 1);
rom.read(reinterpret_cast<char*>(&tu), 1);
mirroring = ((tt & 1) == 0 ? HORIZONTAL : VERTICAL);
is_enable_sram = (tt & (1 << 1)) != 0;
is_enable_trainer = (tt & (1 << 2)) != 0;
is_enable_four_screen = (tt & (1 << 3)) != 0;
mapper = (tt >> 4) | (tu & 0xf0);
loaded = true;
rom.seekg(16, std::ios_base::beg); // skip iNES-header.
if(is_enable_trainer || mapper != 0) return false; // trainer if present. header(16byte) -> trainer patch(0 or 512byte) || mapper-0
// load PRG/CHR-ROM
prg_dat = new uint8_t[prg_rom_size];
chr_dat = new uint8_t[chr_rom_size];
rom.read(prg_dat, prg_rom_size);
rom.read(chr_dat, chr_rom_size);
return true;
}
return false;
}
void inspect() const {
if(loaded) {
std::cout << "ROM Information (iNES(*.nes) Format):\n";
std::cout << "\t\tProgram ROM Size\t\t: " << (prg_rom_size / 1024) << "KB(" << prg_rom_size << "Bytes.," << prg_page << "Pages)\n";
std::cout << "\t\tCharacter ROM Size: " << (chr_rom_size / 1024) << "KB(" << chr_rom_size << "Bytes.," << chr_page << "Pages.)\n";
std::cout << "Flags:\n";
std::cout << "\t\tMirroring: " << (mirroring == VERTICAL : "VERTICAL" : "HORIZONTAL") << ".\n";
std::cout << "\t\tSRAM: " << (is_enable_sram == 1 ? "Enabled" : "Disabled") << ".\n";
std::cout << "\t\tFour Screen:" << (is_enable_four_screen == 1 ? "Enabled" : "Disabled") << std::endl;
}
}
uint8_t* getChrRom() { return chr_rom; }
uint8_t* getPrgRom() { return prg_rom; }
bool isEnableSram() const { return is_enable_sram; }
bool isEnableMapper() const { return is_enable_trainer; }
int getPrgRomSize() const { return prg_rom_size; }
int getChrRomSize() const { return chr_rom_size; }
int getPrgPage() const { return prg_page; }
int getChrPage() const { return chr_page; }
MirrorType getMirrorType() const { return mirroring; }
};
/*
NES Cartridge
for Nintendo Mapper-0 (16KB-ROM, 32KB-ROM) ONLY
ref: http://wiki.nesdev.com/w/index.php/INES_Mapper_000
*/
class ROMController {
INES *rom;
uint16_t mirror_addr;
uint8_t chr_mem[0x2000]; // 1024byte
bool chr_zflag = true;
public:
explicit ROMController(INES* ines) : vm(nes), rom(ines) {
if(rom->getChrPage() == 0) {
memset(chr_mem, 0, 0x2000);
chr_zflag = true;
}
mirror_addr = (rom->getPrgPage() > 1 ? 0x7fff : 0x3fff);
}
~ROMController() { delete rom; }
// addr mod 8192 (mirroring.)
void writeChrPatt(uint16_t addr, uint8_t v) {
if(chr_zflag) chr_mem[addr & 0x1fff] = v;
}
uint8_t readChrPatt(uint16_t addr) { return chr_zflag ? chr_mem[addr & 0x1fff] : rom->readChrRom()[addr & 0x1fff]; }
uint8_t readRom(uint16_t addr) { return rom->getPrgRom()[addr & mirror_addr]; }
};
/*
PPU (Picture Processing Unit)
1 CPU-CLK = 3 PPU-CLK (cpu_clock * 3 == ppu_clock)
PPU Memory: VRAM + OAM.
Character:
16pixel = 16byte
8*8 pixel (Background=Character-pixel*0xff, Sprite=Character-pixel*0xff) -> Pattern-table.
1pixel = 2bit (64*n=k(bit), k*1/8=16(byte), n=2(bit). 2^n=4color (color=(C1,C2,C3,Tr.)))
Attribute-Table:
Palette-Table -> select 4-colors. -> Character.
Palette-Table:
NES: 64color
background=16colors, splite=16(12colors + 4Trs.)
Background:
SCREEN_SIZE = 256 * 240
*/
class PPU {
VM& vm;
/*
ref: http://wiki.nesdev.com/w/index.php/OAM
PPU OAM(Object-Attribute RAM) Memory Map:
byte0: pos-y
byte1: Character-num
byte2: Settings (bit-flag)
byte2-(0~1): palette-table (2bit h)
byte2-(2~4): *reserved*
byte2-5: 0=back, 1=front
byte2-6: 1:左右反転
byte2-7: 1:上下反転
byte3: pos-x
*/
class OAM {
public:
OAM() {}
~OAM() {}
};
/*
ref: http://wiki.nesdev.com/w/index.php/PPU_palettes
ref: http://wiki.nesdev.com/w/index.php/PPU_memory_map
+-------+-------+
+ A + B +
+-------+-------+
+ C + D +
+-------+-------+
PPU VRAM(Video-RAM) Memory Map:
0x0000 ~ 0x0fff: Pattern-table1 -> Cartridge (pixel)
0x1000 ~ 0x1fff: Pattern-table2 -> Cartridge (pixel)
0x2000 ~ 0x23bf: Name-table A ->
0x23c0 ~ 0x23ff: Attribute-table A ->
0x2400 ~ 0x27bf: Name-table B ->
0x27c0 ~ 0x27ff: Attribute-table B ->
0x2800 ~ 0x2bbf: Name-table C ->
0x2bc0 ~ 0x2bff: Attribute-table C ->
0x2c00 ~ 0x2fbf: Name-table D ->
0x2fc0 ~ 0x2fff: Attribute-table D ->
0x3000 ~ 0x3eff: Name-table (0x2000 ~ 0x2eff) mirror
0x3f00 ~ 0x3f1f: palette-table (0x3f00 ~ 0x3f0f: bg, 0x3f10~0x3f1f: sprite)
0x3d20 ~ 0x3fff: palette-table (0x3f00 ~ 0x3f1f) mirror
*/
class VRAM {
PPU *ppu;
uint8_t video_mem[0x3fff + 1]; // 0x0000 ~ 0x3ffff
public:
VRAM(PPU *nes_ppu) : ppu(nes_ppu) {}
~VRAM() {}
};
uint8_t readSpriteData(uint16_t addr) const { return /*OAM[addr]*/; }
void writeSpriteData(uint16_t addr, uint8_t v) { /* OAM[addr] = v; */ }
public:
explicit PPU(VM& nes) : vm(nes) {}
~PPU() {}
// VM -> PPU I/O-Port (0x2000 ~ 0x3ffff)
// ref: http://wiki.nesdev.com/w/index.php/PPU_registers
uint8_t read(const uint16_t addr) {
switch(addr & 0xf) {
case 0x02: return getPPURegister();
case 0x04: return getSpriteRegister();
case 0x07: return getVRAMRegister():
}
}
void write(const uint16_t addr, uint8_t v) {
switch(addr & 0xf) {
case 0x00: setUpPPURegister(v); break;
case 0x01: setUpMaskConfig(v); break;
case 0x03: setSpriteDataAddr(v); break; // OAM address
case 0x04: setSpriteData(v); break; // OAM data
case 0x05: setUpHardwareScroll(v); break;
case 0x06: setVRAMDataAddr(v); break;
case 0x07: setVRAMData(v); break;
}
}
// Read
uint8_t getPPURegister() {
vramAddrRegisterWritten = false;
scrollRegisterWritten = false;
uint8_t ppu_reg = ((VBLANKHasStarted ? 256 : 0) | (sprite0Hit ? 64 : 0) | (spriteOverflow ? 32 : 0));
VBLANKHasStarted = false;
return ppu_reg;
}
uint8_t getSpriteRegister() { return readSpriteData(spliteAddr); }
uint8_t getVRAMRegister() {}
// Write
void setUpPPURegister() {}
void setUpMaskConfig() {}
void setSpriteDataAddr() {}
void setSpriteData() {}
void setUpHardwareScroll() {}
void setVRAMDataAddr() {}
void setVRAMData() {}
};
/*
6502-Processor(MPU, RP2A03 RICOH)
Registers:
(8bit): A, X, Y, SP, P
(16bit): PC
Details of P(Processor Status Register):
N .. Nagative flag
V .. oVerflow flag
R .. Reserved flag (always P[R]=1)
B .. Break flag
D .. Decimal flag
I .. Interrupt flag
Z .. Zero flag
C .. Carry flag
ref: http://pgate1.at-ninja.jp/NES_on_FPGA/nes_cpu.htm
ref: http://hp.vector.co.jp/authors/VA042397/nes/6502.html
*/
class CPU {
VM& vm;
uint8_t reg_a, reg_x, reg_y, sp, psr;
uint16_t pc = 0x0000;
bool nmi_flag = false;
bool irq_flag = false;
enum PSRFlags {
CARRY_FLAG = 1,
ZERO_FLAG = 2,
INTERRUPT_FLAG = 4,
DECIMAL_FLAG = 8,
BREAK_FLAG = 16,
INIT_FLAG = 32, // P[R]=1のみ立っている初期フラグ(0b00100000)
OVERFLOW_FLAG = 64,
NEGATIVE_FLAG = 128
};
uint8_t psr_table[0x100];
enum RegType { RX, RY };
// Stack (0x0100 ~ 0x01ff (256byte fixed))
class Stack {
CPU* cpu_ptr;
const uint16_t base_addr = 0x0100;
public:
Stack(CPU* cpu) : cpu_ptr(cpu) {}
~Stack() {}
void push(uint8_t v) {
cpu_ptr->write((base_addr | cpu_ptr->sp), v);
cpu_ptr->sp--;
}
uint8_t pop() {
cpu_ptr->sp++;
return cpu_ptr->read(base_addr | cpu_ptr->sp);
}
};
Stack stack = this;
/* オペコード毎の実行クロック数
uint8_t opcode = this->read(this->pc++);
printf("%d¥n",clock_table[opcode]);
(ref): http://pgate1.at-ninja.jp/NES_on_FPGA/nes_cpu.htm
(ref): http://wiki.nesdev.com/w/index.php/Clock_rate
(ref): http://wiki.nesdev.com/w/index.php/CPU
*/
const int clock_table[0x100] = {
7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
2, 5, 2, 6, 4, 4, 4, 4, 2, 4, 2, 5, 5, 4, 5, 5,
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4,
2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
2, 6, 3, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7
};
uint8_t read(uint16_t addr) { return vm.read(addr); }
void write(uint16_t addr, uint8_t v) { vm.write(addr, v); }
void update_psr(const uint8_t reg) { psr = ((psr & 0x7d) | psr_table[reg]); }
void load(uint8_t &reg, const uint16_t addr) {
reg = read(addr);
update_psr(reg);
}
public:
explicit CPU(VM& nes) : vm(nes) {
for(int i = 0; i <= 0xff; i++) {
psr_table[i] = (i == 0 ? 0x02 : i <= 0x7f ? 0x00 : 0x80);
}
}
~CPU() {}
// ref: http://wiki.nesdev.com/w/index.php/CPU_power_up_state (At power up)
void hardwareReset() {
psr = 0x34; // 0b00110100 = IRQ disabled.
reg_a = reg_x = reg_y = 0;
sp = 0xfd;
vm.write(0x0417, 0);
vm.write(0x4015, 0);
for(uint16_t addr = 0x4000; addr < 0x400f; ++addr) vm.write(addr, 0);
pc = ((read(0xfffd) << 8) | read(0xfffc));
}
// ref: http://wiki.nesdev.com/w/index.php/CPU_power_up_state (After reset)
void softwareReset() {
vm.addClock(6);
sp -= 3;
psr |= INTERRUPT_FLAG; // IRQ disable.
vm.write(0x4015, 0);
pc = ((read(0xfffd) << 8) | read(0xfffc));
}
// Debug functions.
void debugPSR() const {
printf("Negative Flag(N)=%d\n", (psr & static_cast<int>(NEGATIVE_FLAG)) >> 7);
printf("oVerflow Flag(V)=%d\n", (psr & static_cast<int>(OVERFLOW_FLAG)) >> 6);
printf("Reserved Flag(R)=1\n");
printf("Break Flag(B)=%d\n", (psr & static_cast<int>(BREAK_FLAG)) >> 4);
printf("Decimal Flag(D)=%d\n", (psr & static_cast<int>(DECIMAL_FLAG)) >> 3);
printf("InterruptI Flag(I)=%d\n", (psr & static_cast<int>(INTERRUPT_FLAG)) >> 2);
printf("Zero Flag(Z)=%d\n", (psr & static_cast<int>(ZERO_FLAG)) >> 1);
printf("Carry Flag(C)=%d\n", (psr & static_cast<int>(CARRY_FLAG)));
}
void debugRegisters() const {
std::cout << "PC(Program Counter) =" << std::hex << pc << "\n";
std::cout << "SP(Stack Pointer) =" << std::hex << sp << "\n";
std::cout << "A(Accumlator) =" << std::hex << reg_a << "\n";
std::cout << "X(Index-X) =" << std::hex << reg_x << "\n";
std::cout << "Y(Index-Y) =" << std::hex << reg_y << "\n" << std::endl;
}
/* Hardware-Interrupt
Interrupt-vector
0xfffa: NMI (P[I]=1,P[B]=0)
0xfffc: RESET (P[I]=1)
0xfffe: IRQ/BRK (P[I]=1, P[B]=0(IRQ), P[B]=1(BRK))
*/
void enableNMI() { nmi_flag = true; }
void reserveIRQ() { irq_flag = true; }
void releaseIRQ() { irq_flag = false; }
void onNMI() {
vm.addClock(7);
psr &= ~BREAK_FLAG; // ~BREAK_FLAG = 0b11110111 (P[B]=0)
stack.push(static_cast<uint8_t>((pc >> 8) & 0xff)); // pc(high) ->stack
stack.push(static_cast<uint8_t>(pc & 0xff)); // pc(low) -> stack
stack.push(psr);
psr |= INTERRUPT_FLAG; // P[I]=1
pc = ((read(0xfffb) << 8) | read(0xfffa)); // interrupt-vector
}
void onIRQ() {
if((psr & INTERRUPT_FLAG) == INTERRUPT_FLAG) return; // P[I]=1ならIRQを拒否る
vm.addClock(7);
psr &= ~BREAK_FLAG; // P[B]=0
stack.push(static_cast<uint8_t>((pc >> 8) & 0xff));
stack.push(static_cast<uint8_t>(pc & 0xff));
stack.push(psr);
psr |= INTERRUPT_FLAG; // P[I]=1
pc = ((read(0xfffd) << 8) | read(0xfffc));
}
// Adressing-Mode
uint16_t immediate() { return pc++; }
uint16_t zeroPage() { return static_cast<uint16_t>(vm.read(pc++)); }
uint16_t indexedZeroPage(const RegType r) { return static_cast<uint16_t>(vm.read(pc++) + (r == RX ? reg_x : reg_y)); }
uint16_t absolute() { // (Op Rl Rh) .. Op Rh*16+Rl
uint16_t addr = vm.read(pc++);
return addr | (vm.read(pc++) << 8);
}
uint16_t indexedAbsolute(const RegType r) {
uint16_t org = vm.read(pc++);
org = org | (vm.read(pc++) << 8);
const uint16_t addr = org + (r == RX ? reg_x : reg_y);
if(((addr ^ org) & 0x0100) != 0) vm.addClock(1);
return addr;
}
uint16_t absoluteIndirect() {
uint16_t src = vm.read(pc++);
src = src | (vm.read(pc++) << 8);
return vm.read(src) + (vm.read(src & 0xff00) | (((src + 1) & 0x00ff)) << 8);
}
uint16_t relative() {
int8_t tmp = static_cast<int8_t>(vm.read(pc++));
return tmp + pc;
}
uint16_t indirectX() {
uint8_t idx = vm.read(pc++) + reg_x;
uint16_t addr = vm.read(idx++);
addr = addr | (vm.read(idx) << 8);
return addr;
}
uint16_t indirectY() {
uint8_t idx = vm.read(pc++);
uint16_t org = vm.read(idx);
org = org | (vm.read(pc) << 8);
const uint16_t addr = org + reg_y;
if(((addr ^ org) & 0x0100) != 0) vm.addClock(1);
return addr;
}
// Operations (ref: http://nesdev.com/opcodes.txt)
void opLda(uint16_t addr) { load(reg_a, vm.read(addr)); }
void opLdx(uint16_t addr) { load(reg_x, vm.read(addr)); }
void opLdy(uint16_t addr) { load(reg_y, vm.read(addr)); }
void opSta(uint16_t addr) { vm.write(addr, reg_a); }
void opStx(uint16_t addr) { vm.write(addr, reg_x); }
void opSty(uint16_t addr) { vm.write(addr, reg_y); }
// Add M to A with C (A+M+C->A) (Flags:N Z V C)
void opAdc(uint16_t addr) {
const uint8_t v = vm.read(addr);
const uint8_t res = reg_a + v + (psr & CARRY_FLAG);
const uint16_t a = static_cast<uint8_t>(res & 0xff);
psr = (psr & ~(OVERFLOW_FLAG | CARRY_FLAG))
| ((((reg_a ^ v) & 0x80) ^ 0x80) & ((reg_a ^ a) & 0x80)) >> 1
| ((res >> 8) & CARRY_FLAG);
reg_a = a;
update_psr(reg_a);
}
void opSbc(uint16_t addr) {}
// JuMP (JMP) (Flags:None)
void opJmp(uint16_t addr) { pc = addr; }
// Jump SubRoutine (JSR) (Flags:None)
void opJsr(uint16_t addr) {
pc--;
stack.push(static_cast<uint8_t>((pc >> 8) & 0xff));
stack.push(static_cast<uint8_t>(pc & 0xff));
pc = addr;
}
void opCli() { psr &= ~INTERRUPT_FLAG; } // CLear Interrupt-flag (CLI) (Flag:I)
void opCld() { psr &= ~DECIMAL_FLAG; } // CLear Decimal-flag (CLD) (Flag:D),. BCD-Mode is not implemented on NES.
void opClc() { psr &= ~CARRY_FLAG; } // CLear Carry-flag (CLC) (Flag:C)
void opClv() { psr &= ~OVERFLOW_FLAG; } // CLear oVerflow-flag (CLV) (Flag:V)
void opSec() { psr |= CARRY_FLAG; } // SEt Carry-flag (SEC) (Flag:C)
void opSed() { psr |= DECIMAL_FLAG; } // SEt Decimal-flag (SED) (Flag:D),. BCD-Mode is not implemented on NES.
void opSei() { psr |= INTERRUPT_FLAG; } // SEt Interrupt-flag (SEI) (Flag:I)
void opNop() {}
// ReTurn Subroutine (RTS) (Flags:None)
void opRts() {
pc = stack.pop();
pc = ((stack.pop() << 8) | pc) + 1;
}
// ReTurn Interrupt (RTI) (Flags:None)
void opRti() {
psr = stack.pop();
pc = stack.pop();
pc = ((stack.pop() << 8) | pc);
}
void run() {
const uint8_t opcode = vm.read(pc++);
switch(opcode) {
// lda
case 0xa9: opLda(immediate()); break;
case 0xa5: opLda(zeroPage()); break;
case 0xad: opLda(absolute()); break;
case 0xb5: opLda(indexedZeroPage(RX)); break;
case 0xbd: opLda(indexedAbsolute(RX)); break;
case 0xb9: opLda(indexedAbsolute(RY)); break;
case 0xa1: opLda(indirectX()); break;
case 0xb1: opLda(indirectY()); break;
// ldx
case 0xa2: opLdx(immediate()); break;
case 0xa6: opLdx(zeroPage()); break;
case 0xae: opLdx(absolute()); break;
case 0xb6: opLdx(indexedZeroPage(RY)); break;
case 0xbe: opLdx(indexedAbsolute(RY)); break;
// ldy
case 0xa0: opLdy(immediate()); break;
case 0xa4: opLdy(zeroPage()); break;
case 0xac: opLdy(absolute()); break;
case 0xb4: opLdy(indexedZeroPage(RY)); break;
case 0xbc: opLdy(indexedAbsolute(RX)); break;
// sta
case 0x85: opSta(zeroPage()); break;
case 0x8d: opSta(absolute()); break;
case 0x95: opSta(indexedZeroPage(RX)); break;
case 0x9d: opSta(indexedAbsolute(RX)); break;
case 0x99: opSta(indexedAbsolute(RY)); break;
case 0x81: opSta(indirectX()); break;
case 0x91: opSta(indirectY()); break;
// stx
case 0x86: opStx(zeroPage()); break;
case 0x8e: opStx(absolute()); break;
case 0x96: opStx(indexedZeroPage(RY)); break;
// sty
case 0x84: opSty(zeroPage()); break;
case 0x8c: opSty(absolute()); break;
case 0x94: opSty(indexedZeroPage(RX)); break;
case 0x8a: opTxa(); break; // txa
case 0x98: opTya(); break; // tya
case 0x9a: opTxs(); break; // txs
case 0xa8: opTay(); break; // tay
case 0xaa: opTax(); break; // tax
case 0xba: opTsx(); break; // tsx
case 0x08: opPhp(); break; // php
case 0x28: opPlp(); break; // plp
case 0x48: opPha(); break; // pha
case 0x68: opPla(); break; // pla
// adc
case 0x69: opAdc(immediate()); break;
case 0x65: opAdc(zeroPage()); break;
case 0x6d: opAdc(absolute()); break;
case 0x75: opAdc(indexedZeroPage(RX)); break;
case 0x7d: opAdc(indexedAbsolute(RX)); break;
case 0x79: opAdc(indexedAbsolute(RY)); break;
case 0x61: opAdc(indirectX()); break;
case 0x71: opAdc(indirectY()); break;
// sbc
case 0xe9: opSbc(immediate()); break;
case 0xe5: opSbc(zeroPage()); break;
case 0xed: opSbc(absolute()); break;
case 0xf5: opSbc(indexedZeroPage(RX)); break;
case 0xfd: opSbc(indexedAbsolute(RX)); break;
case 0xf9: opSbc(indexedAbsolute(RY)); break;
case 0xe1: opSbc(indirectX()); break;
case 0xf1: opSbc(indirectY()); break;
// cpx
case 0xe0: opCpx(immediate()); break;
case 0xe4: opCpx(zeroPage()); break;
case 0xec: opCpx(absolute()); break;
// cpy
case 0xc0: opCpy(immediate()); break;
case 0xc4: opCpy(zeroPage()); break;
case 0xcc: opCpy(absolute()); break;
// cmp
case 0xc9: opCmp(immediate()); break;
case 0xc5: opCmp(zeroPage()); break;
case 0xcd: opCmp(absolute()); break;
case 0xd5: opCmp(indexedZeroPage(RX)); break;
case 0xdd: opCmp(indexedAbsolute(RX)); break;
case 0xd9: opCmp(indexedAbsolute(RY)); break;
case 0xe1: opCmp(indirectX()); break;
case 0xf1: opCmp(indirectY()); break;
// and
case 0x29: opAnd(immediate()); break;
case 0x25: opAnd(zeroPage()); break;
case 0x2d: opAnd(absolute()); break;
case 0x35: opAnd(indexedZeroPage(RX)); break;
case 0x3d: opAnd(indexedAbsolute(RX)); break;
case 0x39: opAnd(indexedAbsolute(RY)); break;
case 0x21: opAnd(indirectX()); break;
case 0x31: opAnd(indirectY()); break;
// eor
case 0x49: opEor(immediate()); break;
case 0x45: opEor(zeroPage()); break;
case 0x4d: opEor(absolute()); break;
case 0x55: opEor(indexedZeroPage(RX)); break;
case 0x5d: opEor(indexedAbsolute(RX)); break;
case 0x59: opEor(indexedAbsolute(RY)); break;
case 0x41: opEor(indirectX()); break;
case 0x51: opEor(indirectY()); break;
// ora
case 0x09: opCmp(immediate()); break;
case 0x05: opCmp(zeroPage()); break;
case 0x0d: opCmp(absolute()); break;
case 0x15: opCmp(indexedZeroPage(RX)); break;
case 0x1d: opCmp(indexedAbsolute(RX)); break;
case 0x19: opCmp(indexedAbsolute(RY)); break;
case 0x01: opCmp(indirectX()); break;
case 0x11: opCmp(indirectY()); break;
// bit
case 0x24: opBit(zeroPage()); break;
case 0x2c: opBit(absolute()); break;
// asl
case 0x0a: opAsl(); break; // none
case 0x06: opAsl(zeroPage()); break;
case 0x0e: opAsl(absolute()); break;
case 0x16: opAsl(indexedZeroPage(RX)); break;
case 0x1e: opAsl(indexedAbsolute(RX)); break;
// lsr
case 0x4a: opLsr(); break; // none
case 0x06: opLsr(zeroPage()); break;
case 0x0e: opLsr(absolute()); break;
case 0x16: opLsr(indexedZeroPage(RX)); break;
case 0x5e: opLsr(indexedAbsolute(RX)); break;
// rol
case 0x2a: opRol(); break; // none
case 0x26: opRol(zeroPage()); break;
case 0x2e: opRol(absolute()); break;
case 0x36: opRol(indexedZeroPage(RX)); break;
case 0x3e: opRol(indexedAbsolute(RX)); break;
// ror
case 0x6a: opRor(); break; // none
case 0x66: opRor(zeroPage()); break;
case 0x6e: opRor(absolute()); break;
case 0x76: opRor(indexedZeroPage(RX)); break;
case 0x7e: opRor(indexedAbsolute(RX)); break;
case 0xe8: opInx(); break; // inx
case 0xc8: opIny(); break; // iny
case 0xca: opDex(); break; // dex
case 0x88: opDec(); break; // dec
// inc
case 0xe6: opInc(zeroPage()); break;
case 0xee: opInc(absolute()); break;
case 0xf6: opInc(indexedZeroPage(RX)); break;
case 0xfe: opInc(indexedAbsolute(RX)); break;
case 0x18: opClc(); break; // clc
case 0x58: opCli(); break; // cli
case 0xb8: opClv(); break; // clv
case 0xd8: opCld(); break; // cld
case 0x38: opSec(); break; // sec
case 0x78: opSei(); break; // sei
case 0xf8: opSed(); break; // sed
case 0xea: opNop(); break; // nop
case 0x00: opBrk(); break; // brk
case 0x20: opJsr(absolute()); break; // jsr
case 0x4c: opJmp(absolute()); break;// jmp
case 0x40: opRti(); break; // rti
case 0x60: opRts(); break;// rts
}
vm.addClock(clock_table[opcode]);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment