Add CMP, COP, DEC, EOR, update SNES and Memory

This commit is contained in:
scawful
2023-08-20 15:39:22 -04:00
parent 1795f8f3bf
commit 7448f80119
6 changed files with 617 additions and 187 deletions

View File

@@ -67,6 +67,22 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) {
return fetchedByte;
}
void CPU::Run() {
while (true) {
// Fetch the next opcode from memory at the current program counter
uint8_t opcode = memory.ReadByte(PC);
// Increment the program counter to point to the next instruction
PC++;
// Execute the instruction corresponding to the fetched opcode
ExecuteInstruction(opcode);
// Optionally, handle interrupts or other external events
HandleInterrupts();
}
}
void CPU::ExecuteInstruction(uint8_t opcode) {
// Update the PC based on the Program Bank Register
PC |= (static_cast<uint16_t>(PB) << 16);
@@ -262,11 +278,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x50: // BVC Branch if overflow clear
// BVC();
BVC(ReadByte(PC));
break;
case 0x70: // BVS Branch if overflow set
// BVS();
BVS(ReadByte(PC));
break;
case 0x18: // CLC Clear carry
@@ -286,49 +302,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0xC1: // CMP DP Indexed Indirect, X
// CMP();
CMP(DirectPageIndexedIndirectX());
break;
case 0xC3: // CMP Stack Relative
// CMP();
CMP(StackRelative());
break;
case 0xC5: // CMP Direct Page
// CMP();
CMP(DirectPage());
break;
case 0xC7: // CMP DP Indirect Long
// CMP();
CMP(DirectPageIndirectLong());
break;
case 0xC9: // CMP Immediate
// CMP();
CMP(Immediate(), true);
break;
case 0xCD: // CMP Absolute
// CMP();
CMP(Absolute());
break;
case 0xCF: // CMP Absolute Long
// CMP();
CMP(AbsoluteLong());
break;
case 0xD1: // CMP DP Indirect Indexed, Y
// CMP();
CMP(DirectPageIndirectIndexedY());
break;
case 0xD2: // CMP DP Indirect
// CMP();
CMP(DirectPageIndirect());
break;
case 0xD3: // CMP SR Indirect Indexed, Y
// CMP();
CMP(StackRelativeIndirectIndexedY());
break;
case 0xD5: // CMP DP Indexed, X
// CMP();
CMP(DirectPageIndexedX());
break;
case 0xD7: // CMP DP Indirect Long Indexed, Y
// CMP();
CMP(DirectPageIndirectLongIndexedY());
break;
case 0xD9: // CMP Absolute Indexed, Y
// CMP();
CMP(AbsoluteIndexedY());
break;
case 0xDD: // CMP Absolute Indexed, X
// CMP();
CMP(AbsoluteIndexedX());
break;
case 0xDF: // CMP Absolute Long Indexed, X
// CMP();
CMP(AbsoluteLongIndexedX());
break;
case 0x02: // COP Coprocessor
@@ -356,19 +372,19 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x3A: // DEC Accumulator
// DEC();
DEC(A);
break;
case 0xC6: // DEC Direct Page
// DEC();
DEC(DirectPage());
break;
case 0xCE: // DEC Absolute
// DEC();
DEC(Absolute());
break;
case 0xD6: // DEC DP Indexed, X
// DEC();
DEC(DirectPageIndexedX());
break;
case 0xDE: // DEC Absolute Indexed, X
// DEC();
DEC(AbsoluteIndexedX());
break;
case 0xCA: // DEX Decrement X register
@@ -380,49 +396,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x41: // EOR DP Indexed Indirect, X
// EOR();
EOR(DirectPageIndexedIndirectX());
break;
case 0x43: // EOR Stack Relative
// EOR();
EOR(StackRelative());
break;
case 0x45: // EOR Direct Page
// EOR();
EOR(DirectPage());
break;
case 0x47: // EOR DP Indirect Long
// EOR();
EOR(DirectPageIndirectLong());
break;
case 0x49: // EOR Immediate
// EOR();
EOR(Immediate(), true);
break;
case 0x4D: // EOR Absolute
// EOR();
EOR(Absolute());
break;
case 0x4F: // EOR Absolute Long
// EOR();
EOR(AbsoluteLong());
break;
case 0x51: // EOR DP Indirect Indexed, Y
// EOR();
EOR(DirectPageIndirectIndexedY());
break;
case 0x52: // EOR DP Indirect
// EOR();
EOR(DirectPageIndirect());
break;
case 0x53: // EOR SR Indirect Indexed, Y
// EOR();
EOR(StackRelativeIndirectIndexedY());
break;
case 0x55: // EOR DP Indexed, X
// EOR();
EOR(DirectPageIndexedX());
break;
case 0x57: // EOR DP Indirect Long Indexed, Y
// EOR();
EOR(DirectPageIndirectLongIndexedY());
break;
case 0x59: // EOR Absolute Indexed, Y
// EOR();
EOR(AbsoluteIndexedY());
break;
case 0x5D: // EOR Absolute Indexed, X
// EOR();
EOR(AbsoluteIndexedX());
break;
case 0x5F: // EOR Absolute Long Indexed, X
// EOR();
EOR(AbsoluteLongIndexedX());
break;
case 0x1A: // INC Accumulator
@@ -524,51 +540,51 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0xA2: // LDX Immediate
// LDX();
LDX(Immediate(), true);
break;
case 0xA6: // LDX Direct Page
// LDX();
LDX(DirectPage());
break;
case 0xAE: // LDX Absolute
// LDX();
LDX(Absolute());
break;
case 0xB6: // LDX DP Indexed, Y
// LDX();
LDX(DirectPageIndexedY());
break;
case 0xBE: // LDX Absolute Indexed, Y
// LDX();
LDX(AbsoluteIndexedY());
break;
case 0xA0: // LDY Immediate
// LDY();
LDY(Immediate(), true);
break;
case 0xA4: // LDY Direct Page
// LDY();
LDY(DirectPage());
break;
case 0xAC: // LDY Absolute
// LDY();
LDY(Absolute());
break;
case 0xB4: // LDY DP Indexed, X
// LDY();
LDY(DirectPageIndexedX());
break;
case 0xBC: // LDY Absolute Indexed, X
// LDY();
LDY(AbsoluteIndexedX());
break;
case 0x46: // LSR Direct Page
// LSR();
LSR(DirectPage());
break;
case 0x4A: // LSR Accumulator
// LSR();
LSR(A);
break;
case 0x4E: // LSR Absolute
// LSR();
LSR(Absolute());
break;
case 0x56: // LSR DP Indexed, X
// LSR();
LSR(DirectPageIndexedX());
break;
case 0x5E: // LSR Absolute Indexed, X
// LSR();
LSR(AbsoluteIndexedX());
break;
case 0x54: // MVN Block Move Next
@@ -580,61 +596,61 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x01: // ORA DP Indexed Indirect, X
// ORA();
ORA(DirectPageIndexedIndirectX());
break;
case 0x03: // ORA Stack Relative
// ORA();
ORA(StackRelative());
break;
case 0x05: // ORA Direct Page
// ORA();
ORA(DirectPage());
break;
case 0x07: // ORA DP Indirect Long
// ORA();
ORA(DirectPageIndirectLong());
break;
case 0x09: // ORA Immediate
// ORA();
ORA(Immediate(), true);
break;
case 0x0D: // ORA Absolute
// ORA();
ORA(Absolute());
break;
case 0x0F: // ORA Absolute Long
// ORA();
ORA(AbsoluteLong());
break;
case 0x11: // ORA DP Indirect Indexed, Y
// ORA();
ORA(DirectPageIndirectIndexedY());
break;
case 0x12: // ORA DP Indirect
// ORA();
ORA(DirectPageIndirect());
break;
case 0x13: // ORA SR Indirect Indexed, Y
// ORA();
ORA(StackRelativeIndirectIndexedY());
break;
case 0x15: // ORA DP Indexed, X
// ORA();
ORA(DirectPageIndexedX());
break;
case 0x17: // ORA DP Indirect Long Indexed, Y
// ORA();
ORA(DirectPageIndirectLongIndexedY());
break;
case 0x19: // ORA Absolute Indexed, Y
// ORA();
ORA(AbsoluteIndexedY());
break;
case 0x1D: // ORA Absolute Indexed, X
// ORA();
ORA(AbsoluteIndexedX());
break;
case 0x1F: // ORA Absolute Long Indexed, X
// ORA();
ORA(AbsoluteLongIndexedX());
break;
case 0xF4: // PEA Push Effective Absolute address
// PEA();
PEA();
break;
case 0xD4: // PEI Push Effective Indirect address
// PEI();
PEI();
break;
case 0x62: // PER Push Effective PC Relative Indirect address
// PER();
PER();
break;
case 0x48: // PHA Push Accumulator
@@ -694,94 +710,93 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x26: // ROL Direct Page
// ROL();
ROL(DirectPage());
break;
case 0x2A: // ROL Accumulator
// ROL();
ROL(A);
break;
case 0x2E: // ROL Absolute
// ROL();
ROL(Absolute());
break;
case 0x36: // ROL DP Indexed, X
// ROL();
ROL(DirectPageIndexedX());
break;
case 0x3E: // ROL Absolute Indexed, X
// ROL();
ROL(AbsoluteIndexedX());
break;
case 0x66: // ROR Direct Page
// ROR();
ROR(DirectPage());
break;
case 0x6A: // ROR Accumulator
// ROR();
ROR(A);
break;
case 0x6E: // ROR Absolute
// ROR();
ROR(Absolute());
break;
case 0x76: // ROR DP Indexed, X
// ROR();
ROR(DirectPageIndexedX());
break;
case 0x7E: // ROR Absolute Indexed, X
// ROR();
ROR(AbsoluteIndexedX());
break;
case 0x40: // RTI Return from interrupt
// RTI();
RTI();
break;
case 0x6B: // RTL Return from subroutine long
// RTL();
RTL();
break;
case 0x60: // RTS Return from subroutine
// RTS();
RTS();
break;
case 0xE1: // SBC DP Indexed Indirect, X
// SBC();
SBC(DirectPageIndexedIndirectX());
break;
case 0xE3: // SBC Stack Relative
// SBC();
SBC(StackRelative());
break;
case 0xE5: // SBC Direct Page
// SBC();
SBC(DirectPage());
break;
case 0xE7: // SBC DP Indirect Long
// SBC();
SBC(DirectPageIndirectLong());
break;
case 0xE9: // SBC Immediate
// SBC();
SBC(Immediate(), true);
break;
case 0xED: // SBC Absolute
// SBC();
SBC(Absolute());
break;
case 0xEF: // SBC Absolute Long
// SBC();
SBC(AbsoluteLong());
break;
case 0xF1: // SBC DP Indirect Indexed, Y
// SBC();
SBC(DirectPageIndirectIndexedY());
break;
case 0xF2: // SBC DP Indirect
// SBC();
SBC(DirectPageIndirect());
break;
case 0xF3: // SBC SR Indirect Indexed, Y
// SBC();
SBC(StackRelativeIndirectIndexedY());
break;
case 0xF5: // SBC DP Indexed, X
// SBC();
SBC(DirectPageIndexedX());
break;
case 0xF7: // SBC DP Indirect Long Indexed, Y
// SBC();
SBC(DirectPageIndirectLongIndexedY());
break;
case 0xF9: // SBC Absolute Indexed, Y
// SBC();
SBC(AbsoluteIndexedY());
break;
case 0xFD: // SBC Absolute Indexed, X
// SBC();
SBC(AbsoluteIndexedX());
break;
case 0xFF: // SBC Absolute Long Indexed, X
// SBC();
SBC(AbsoluteLongIndexedX());
break;
case 0x38: // SEC Set carry
@@ -801,90 +816,90 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x81: // STA DP Indexed Indirect, X
// STA();
STA(DirectPageIndexedIndirectX());
break;
case 0x83: // STA Stack Relative
// STA();
STA(StackRelative());
break;
case 0x85: // STA Direct Page
// STA();
STA(DirectPage());
break;
case 0x87: // STA DP Indirect Long
// STA();
STA(DirectPageIndirectLong());
break;
case 0x8D: // STA Absolute
// STA();
STA(Absolute());
break;
case 0x8F: // STA Absolute Long
// STA();
STA(AbsoluteLong());
break;
case 0x91: // STA DP Indirect Indexed, Y
// STA();
STA(DirectPageIndirectIndexedY());
break;
case 0x92: // STA DP Indirect
// STA();
STA(DirectPageIndirect());
break;
case 0x93: // STA SR Indirect Indexed, Y
// STA();
STA(StackRelativeIndirectIndexedY());
break;
case 0x95: // STA DP Indexed, X
// STA();
STA(DirectPageIndexedX());
break;
case 0x97: // STA DP Indirect Long Indexed, Y
// STA();
STA(DirectPageIndirectLongIndexedY());
break;
case 0x99: // STA Absolute Indexed, Y
// STA();
STA(AbsoluteIndexedY());
break;
case 0x9D: // STA Absolute Indexed, X
// STA();
STA(AbsoluteIndexedX());
break;
case 0x9F: // STA Absolute Long Indexed, X
// STA();
STA(AbsoluteLongIndexedX());
break;
case 0xDB: // STP Stop the clock
// STP();
STP();
break;
case 0x86: // STX Direct Page
// STX();
STX(DirectPage());
break;
case 0x8E: // STX Absolute
// STX();
STX(Absolute());
break;
case 0x96: // STX DP Indexed, Y
// STX();
STX(DirectPageIndexedY());
break;
case 0x84: // STY Direct Page
// STY();
STY(DirectPage());
break;
case 0x8C: // STY Absolute
// STY();
STY(Absolute());
break;
case 0x94: // STY DP Indexed, X
// STY();
STY(DirectPageIndexedX());
break;
case 0x64: // STZ Direct Page
// STZ();
STZ(DirectPage());
break;
case 0x74: // STZ DP Indexed, X
// STZ();
STZ(DirectPageIndexedX());
break;
case 0x9C: // STZ Absolute
// STZ();
STZ(Absolute());
break;
case 0x9E: // STZ Absolute Indexed, X
// STZ();
STZ(AbsoluteIndexedX());
break;
case 0xAA: // TAX
case 0xAA: // TAX Transfer accumulator to X
TAX();
break;
case 0xA8: // TAY
case 0xA8: // TAY Transfer accumulator to Y
TAY();
break;
@@ -901,58 +916,56 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x14: // TRB Direct Page
// TRB();
TRB(DirectPage());
break;
case 0x1C: // TRB Absolute
// TRB();
TRB(Absolute());
break;
case 0x04: // TSB Direct Page
// TSB();
TSB(DirectPage());
break;
case 0x0C: // TSB Absolute
// TSB();
TSB(Absolute());
break;
case 0x3B: // TSC
TSC();
break;
case 0xBA: // TSX
case 0xBA: // TSX Transfer stack pointer to X
TSX();
break;
case 0x8A: // TXA
case 0x8A: // TXA Transfer X to accumulator
TXA();
break;
case 0x9A: // TXS
case 0x9A: // TXS Transfer X to stack pointer
TXS();
break;
case 0x9B: // TXY
case 0x9B: // TXY Transfer X to Y
TXY();
break;
case 0x98: // TYA
case 0x98: // TYA Transfer Y to accumulator
TYA();
break;
case 0xBB: // TYX
case 0xBB: // TYX Transfer Y to X
TYX();
break;
case 0xCB: // WAI
// WAI();
case 0xCB: // WAI Wait for interrupt
WAI();
break;
case 0xEB: // XBA
// XBA();
case 0xEB: // XBA Exchange B and A
XBA();
break;
case 0xFB: // XCE
case 0xFB: // XCE Exchange carry and emulation bits
XCE();
break;
default:
@@ -961,29 +974,26 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
}
}
void CPU::ADC(uint8_t operand) {
auto C = GetCarryFlag();
if (!E) { // 8-bit mode
uint16_t result = (A & 0xFF) + (operand & 0xFF); // + (C ? 1 : 0);
SetCarryFlag(!(result > 0xFF)); // Update the carry flag
bool C = GetCarryFlag();
if (GetAccumulatorSize()) { // 8-bit mode
uint16_t result = static_cast<uint16_t>(A & 0xFF) +
static_cast<uint16_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFF); // Update the carry flag
// Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0;
if (overflow) {
status |= 0x40; // Set overflow flag
} else {
status &= ~0x40; // Clear overflow flag
}
SetOverflowFlag(overflow);
// Update the accumulator
// Update the accumulator with proper wrap-around
A = (A & 0xFF00) | (result & 0xFF);
SetZeroFlag(A == 0);
SetZeroFlag((A & 0xFF) == 0);
SetNegativeFlag(A & 0x80);
} else {
uint32_t result = A + operand; // + (C ? 1 : 0);
SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag
uint32_t result =
static_cast<uint32_t>(A) + static_cast<uint32_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFFFF); // Update the carry flag
// Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x8000) != 0;
@@ -997,6 +1007,8 @@ void CPU::ADC(uint8_t operand) {
}
}
void CPU::HandleInterrupts() {}
void CPU::AND(uint16_t value, bool isImmediate) {
uint16_t operand;
if (E == 0) { // 16-bit mode

View File

@@ -51,7 +51,9 @@ class CPU : public Memory, public Clock {
uint8_t FetchByteDirectPage(uint8_t operand);
void Run();
void ExecuteInstruction(uint8_t opcode);
void HandleInterrupts();
// ==========================================================================
// Addressing Modes
@@ -274,12 +276,11 @@ class CPU : public Memory, public Clock {
// ==========================================================================
// Registers
uint8_t A = 0; // Accumulator
uint8_t B = 0; // Accumulator (High)
uint16_t A = 0; // Accumulator
uint16_t X = 0; // X index register
uint16_t Y = 0; // Y index register
uint16_t D = 0; // Direct Page register
uint16_t DB = 0; // Data Bank register
uint8_t DB = 0; // Data Bank register
uint8_t PB = 0; // Program Bank register
uint16_t PC = 0; // Program Counter
uint8_t E = 1; // Emulation mode flag
@@ -435,29 +436,76 @@ class CPU : public Memory, public Clock {
// CLV: Clear overflow flag
void CLV() { status &= ~0x40; }
// CMP: Compare ```
// COP: Coprocessor ```
// CMP: Compare TESTME
// n Set if MSB of result is set; else cleared
// z Set if result is zero; else cleared
// c Set if no borrow; else cleared
void CMP(uint8_t value, bool isImmediate = false) {
if (GetAccumulatorSize()) { // 8-bit
uint8_t result = isImmediate ? A - value : A - memory.ReadByte(value);
SetZeroFlag(result == 0);
SetNegativeFlag(result & 0x80);
SetCarryFlag(A >= value);
} else { // 16-bit
uint16_t result = isImmediate ? A - value : A - memory.ReadWord(value);
SetZeroFlag(result == 0);
SetNegativeFlag(result & 0x8000);
SetCarryFlag(A >= value);
}
}
// COP: Coprocessor TESTME
void COP() {
PC += 2; // Increment the program counter by 2
memory.PushWord(PC);
memory.PushByte(status);
SetInterruptFlag(true);
if (E) {
PC = memory.ReadWord(0xFFF4);
} else {
PC = memory.ReadWord(0xFFE4);
}
SetDecimalFlag(false);
}
// CPX: Compare X register
// CPX: Compare X register
void CPX(uint16_t value, bool isImmediate = false) {
uint16_t memory_value = isImmediate
? value
: (GetIndexSize() ? memory.ReadByte(value)
: memory.ReadWord(value));
compare(X, memory_value);
if (GetIndexSize()) { // 8-bit
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
compare(X, memory_value);
} else { // 16-bit
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(X, memory_value);
}
}
// CPY: Compare Y register
void CPY(uint16_t value, bool isImmediate = false) {
uint16_t memory_value = isImmediate
? value
: (GetIndexSize() ? memory.ReadByte(value)
: memory.ReadWord(value));
compare(Y, memory_value);
if (GetIndexSize()) { // 8-bit
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
compare(Y, memory_value);
} else { // 16-bit
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(Y, memory_value);
}
}
// DEC: Decrement ```
// DEC: Decrement TESTME
void DEC(uint16_t address) {
if (GetAccumulatorSize()) {
uint8_t value = memory.ReadByte(address);
value--;
memory.WriteByte(address, value);
SetZeroFlag(value == 0);
SetNegativeFlag(value & 0x80);
} else {
uint16_t value = memory.ReadWord(address);
value--;
memory.WriteWord(address, value);
SetZeroFlag(value == 0);
SetNegativeFlag(value & 0x8000);
}
}
// DEX: Decrement X register
void DEX() {
@@ -485,7 +533,18 @@ class CPU : public Memory, public Clock {
}
}
// EOR: Exclusive OR ```
// EOR: Exclusive OR TESTMEs
void EOR(uint16_t address, bool isImmediate = false) {
if (GetAccumulatorSize()) {
A ^= isImmediate ? address : memory.ReadByte(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else {
A ^= isImmediate ? address : memory.ReadWord(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
// INC: Increment
void INC(uint16_t address) {
@@ -779,7 +838,12 @@ class CPU : public Memory, public Clock {
}
}
// STP: Stop the clock ```
// TODO: Make this work with the Clock class of the CPU
// STP: Stop the clock
void STP() {
// During the next phase 2 clock cycle, stop the processors oscillator input
// The processor is effectively shut down until a reset occurs (RES` pin).
}
// STX: Store X register
void STX(uint16_t address) {
@@ -900,15 +964,20 @@ class CPU : public Memory, public Clock {
SetNegativeFlag(Y & 0x80);
}
// WAI: Wait for interrupt ```
// TODO: Make this communicate with the SNES class
// WAI: Wait for interrupt TESTME
void WAI() {
// Pull the RDY pin low
// Power consumption is reduced(?)
// RDY remains low until an external hardware interupt
// (NMI, IRQ, ABORT, or RESET) is received from the SNES class
}
// XBA: Exchange B and A accumulator
void XBA() {
uint8_t temp = A;
A = B;
B = temp;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
uint8_t lowByte = A & 0xFF;
uint8_t highByte = (A >> 8) & 0xFF;
A = (lowByte << 8) | highByte;
}
// XCE: Exchange Carry and Emulation Flags

View File

@@ -5,6 +5,43 @@
#include <iostream>
#include <vector>
// LoROM (Mode 20):
// Banks Offset Purpose
// 00-3F 0000-1FFF LowRAM (shadowed from 7E)
// 2000-2FFF PPU1, APU
// 3000-3FFF SFX, DSP, etc.
// 4000-41FF Controller
// 4200-5FFF PPU2, DMA, etc.
// 6000-7FFF Expansion RAM (reserved)
// 8000-FFFF 32k ROM Chunk
// 40-7C 0000-7FFF 32k ROM Chunk
// 8000-FFFF 32k ROM Chunk
// 7D 0000-FFFF SRAM
// 7E 0000-1FFF LowRAM
// 2000-FFFF System RAM
// 7F 0000-FFFF System RAM
// HiROM (Mode 21):
// Banks Offset Purpose
// 00-3F 0000-1FFF LowRAM (shadowed from 7E)
// 2000-2FFF PPU1, APU
// 3000-3FFF SFX, DSP, etc.
// 4000-41FF Controller
// 4200-5FFF PPU2, DMA, etc.
// 6000-7FFF SRAM (256KB)
// 8000-FFFF 32k ROM Chunk
// 40-6F 0000-FFFF 64k ROM Chunk
// 70-77 0000-FFFF SRAM (256KB)
// 78-7D 0000-FFFF Never Used
// 7E 0000-1FFF LowRAM
// 2000-7FFF HighRAM
// 8000-FFFF Expanded RAM
// 7F 0000-FFFF More Expanded RAM
// 80-EF 0000-FFFF Mirror of 00-6F
// F0-FF 0000-FFFF 64k ROM Chunk
namespace yaze {
namespace app {
namespace emu {
@@ -103,6 +140,86 @@ class Memory {
class MemoryImpl : public Memory {
public:
void Initialize(const std::vector<uint8_t>& romData) {
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
const size_t SRAM_SIZE = 0x10000; // 64 KB
const size_t SYSTEM_RAM_SIZE = 0x20000; // 128 KB
const size_t EXPANSION_RAM_SIZE = 0x2000; // 8 KB
const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB
// Clear memory
memory_.clear();
memory_.resize(0x1000000, 0); // 24-bit address space
// Load ROM data into memory based on LoROM mapping
size_t romSize = romData.size();
size_t romAddress = 0;
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
if (romAddress < romSize) {
std::copy(romData.begin() + romAddress,
romData.begin() + romAddress + ROM_CHUNK_SIZE,
memory_.begin() + (bank << 16) + offset);
romAddress += ROM_CHUNK_SIZE;
}
}
}
// Initialize SRAM at banks 0x7D and 0xFD
std::fill(memory_.begin() + (0x7D << 16), memory_.begin() + (0x7E << 16),
0);
std::fill(memory_.begin() + (0xFD << 16), memory_.begin() + (0xFE << 16),
0);
// Initialize System RAM at banks 0x7E and 0x7F
std::fill(memory_.begin() + (0x7E << 16),
memory_.begin() + (0x7E << 16) + SYSTEM_RAM_SIZE, 0);
// Initialize Shadow RAM at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(memory_.begin() + (bank << 16),
memory_.begin() + (bank << 16) + 0x2000, 0);
}
// Initialize Hardware Registers at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(
memory_.begin() + (bank << 16) + 0x2000,
memory_.begin() + (bank << 16) + 0x2000 + HARDWARE_REGISTERS_SIZE, 0);
}
// Initialize Expansion RAM at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(memory_.begin() + (bank << 16) + 0x6000,
memory_.begin() + (bank << 16) + 0x6000 + EXPANSION_RAM_SIZE,
0);
}
// Initialize Reset and NMI Vectors at bank 0xFF
std::fill(memory_.begin() + (0xFF << 16) + 0xFF00,
memory_.begin() + (0xFF << 16) + 0xFFFF + 1, 0);
// Copy data into rom_ vector
rom_.resize(kROMSize);
std::copy(memory_.begin() + kROMStart,
memory_.begin() + kROMStart + kROMSize, rom_.begin());
// Copy data into ram_ vector
ram_.resize(kRAMSize);
std::copy(memory_.begin() + kRAMStart,
memory_.begin() + kRAMStart + kRAMSize, ram_.begin());
// Copy data into vram_ vector
vram_.resize(kVRAMSize);
std::copy(memory_.begin() + kVRAMStart,
memory_.begin() + kVRAMStart + kVRAMSize, vram_.begin());
// Copy data into oam_ vector
oam_.resize(kOAMSize);
std::copy(memory_.begin() + kOAMStart,
memory_.begin() + kOAMStart + kOAMSize, oam_.begin());
}
uint8_t ReadByte(uint16_t address) const override {
uint32_t mapped_address = GetMappedAddress(address);
return memory_.at(mapped_address);
@@ -174,6 +291,7 @@ class MemoryImpl : public Memory {
(static_cast<uint32_t>(mid) << 8) | low;
}
// Stack Pointer access.
int16_t SP() const override { return SP_; }
void SetSP(int16_t value) override { SP_ = value; }

View File

@@ -14,6 +14,30 @@ namespace yaze {
namespace app {
namespace emu {
namespace {
uint8_t GetHeaderOffset(const Memory& memory) {
uint8_t mapMode = memory[(0x00 << 16) + 0xFFD5];
uint8_t offset;
switch (mapMode & 0x07) {
case 0: // LoROM
offset = 0x7F;
break;
case 1: // HiROM
offset = 0xFF;
break;
case 5: // ExHiROM
offset = 0x40;
break;
default:
throw std::runtime_error("Unsupported map mode");
}
return offset;
}
} // namespace
void DMA::StartDMATransfer(uint8_t channelMask) {
for (int i = 0; i < 8; ++i) {
if ((channelMask & (1 << i)) != 0) {
@@ -78,7 +102,63 @@ void DMA::EnableHDMATransfers(uint8_t channelMask) {
HDMAEN = channelMask; // Set the HDMAEN register to the channel mask
}
ROMInfo SNES::ReadRomHeader(uint32_t offset) {
ROMInfo romInfo;
// Read cartridge title
char title[22];
for (int i = 0; i < 21; ++i) {
title[i] = cpu.ReadByte(offset + i);
}
title[21] = '\0'; // Null-terminate the string
romInfo.title = std::string(title);
// Read ROM speed and memory map mode
uint8_t romSpeedAndMapMode = cpu.ReadByte(offset + 0x15);
romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07);
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
// Read ROM type
romInfo.romType = (ROMType)cpu.ReadByte(offset + 0x16);
// Read ROM size
romInfo.romSize = (ROMSize)cpu.ReadByte(offset + 0x17);
// Read RAM size
romInfo.sramSize = (SRAMSize)cpu.ReadByte(offset + 0x18);
// Read country code
romInfo.countryCode = (CountryCode)cpu.ReadByte(offset + 0x19);
// Read license
romInfo.license = (License)cpu.ReadByte(offset + 0x1A);
// Read ROM version
romInfo.version = cpu.ReadByte(offset + 0x1B);
// Read checksum complement
romInfo.checksumComplement = cpu.ReadWord(offset + 0x1E);
// Read checksum
romInfo.checksum = cpu.ReadWord(offset + 0x1C);
// Read NMI VBL vector
romInfo.nmiVblVector = cpu.ReadWord(offset + 0x3E);
// Read reset vector
romInfo.resetVector = cpu.ReadWord(offset + 0x3C);
return romInfo;
}
void SNES::Init(ROM& rom) {
// Load the ROM into memory and set up the memory mapping
memory_.Initialize(rom.vector());
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader(header_offset);
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
// Disable the emulation flag (switch to 65816 native mode)s
cpu.Init();
@@ -237,7 +317,7 @@ void SNES::Run() {
// Enable NMI Interrupts
void SNES::EnableVBlankInterrupts() {
vBlankFlag = 0;
vBlankFlag = false;
// Clear the RDNMI VBlank flag
memory_.ReadByte(0x4210); // RDNMI
@@ -248,7 +328,7 @@ void SNES::EnableVBlankInterrupts() {
// Wait until the VBlank routine has been processed
void SNES::WaitForVBlank() {
vBlankFlag = 1;
vBlankFlag = true;
// Loop until `vBlankFlag` is clear
while (vBlankFlag) {

View File

@@ -62,6 +62,8 @@ class SNES : public DMA {
SNES() = default;
~SNES() = default;
ROMInfo ReadRomHeader(uint32_t offset);
// Initialization
void Init(ROM& rom);
@@ -110,6 +112,7 @@ class SNES : public DMA {
APU apu{memory_};
// Helper classes
ROMInfo rom_info_;
Debugger debugger;
std::vector<uint8_t> rom_data;