SNES, CPU, Emulator + tests updated

This commit is contained in:
scawful
2023-11-30 02:12:11 -05:00
parent 0bf45c86a9
commit 446734321c
15 changed files with 1144 additions and 682 deletions

View File

@@ -32,10 +32,6 @@ void CPU::Update(UpdateMode mode, int stepCount) {
}
void CPU::ExecuteInstruction(uint8_t opcode) {
// Update the PC based on the Program Bank Register
PC |= (static_cast<uint16_t>(PB) << 16);
// uint8_t operand = -1;
bool immediate = false;
uint16_t operand = 0;
bool accumulator_mode = GetAccumulatorSize();
@@ -181,7 +177,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
ASL(operand);
break;
case 0x16: // ASL DP Indexed, X
operand = DirectPageIndexedX();
operand = ReadByBitMode((0x00 << 16) + DirectPageIndexedX());
ASL(operand);
break;
case 0x1E: // ASL Absolute Indexed, X
@@ -195,25 +191,30 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0xB0: // BCS Branch if carry set
BCS(memory.ReadByte(PC));
operand = memory.ReadByte(PC);
BCS(operand);
break;
case 0xF0: // BEQ Branch if equal (zero set)
operand = memory.ReadByte(PC);
operand = memory.ReadByte((PB << 16) + PC + 1);
BEQ(operand);
break;
case 0x24: // BIT Direct Page
BIT(DirectPage());
operand = DirectPage();
BIT(operand);
break;
case 0x2C: // BIT Absolute
BIT(Absolute());
operand = Absolute();
BIT(operand);
break;
case 0x34: // BIT DP Indexed, X
BIT(DirectPageIndexedX());
operand = DirectPageIndexedX();
BIT(operand);
break;
case 0x3C: // BIT Absolute Indexed, X
BIT(AbsoluteIndexedX());
operand = AbsoluteIndexedX();
BIT(operand);
break;
case 0x89: // BIT Immediate
operand = Immediate();
@@ -222,22 +223,22 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x30: // BMI Branch if minus (negative set)
operand = memory.ReadByte(PC);
operand = memory.ReadByte((PB << 16) + PC + 1);
BMI(operand);
break;
case 0xD0: // BNE Branch if not equal (zero clear)
operand = memory.ReadByte(PC);
operand = memory.ReadByte((PB << 16) + PC + 1);
BNE(operand);
break;
case 0x10: // BPL Branch if plus (negative clear)
operand = memory.ReadByte(PC);
operand = memory.ReadByte((PB << 16) + PC + 1);
BPL(operand);
break;
case 0x80: // BRA Branch always
operand = memory.ReadByte(PC);
operand = memory.ReadByte((PB << 16) + PC + 1);
BRA(operand);
break;
@@ -247,16 +248,16 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
case 0x82: // BRL Branch always long
operand = FetchSignedWord();
PC += operand;
next_pc_ = operand;
break;
case 0x50: // BVC Branch if overflow clear
operand = memory.ReadByte(PC);
operand = FetchByte();
BVC(operand);
break;
case 0x70: // BVS Branch if overflow set
operand = memory.ReadByte(PC);
operand = FetchByte();
BVS(operand);
break;
@@ -505,7 +506,8 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0x20: // JSR Absolute
JSR(Absolute());
operand = Absolute();
JSR(operand);
break;
case 0x22: // JSL Absolute Long
@@ -526,7 +528,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break;
case 0xA5: // LDA Direct Page
operand = DirectPage();
LDA(operand);
LDA(operand, false, true);
break;
case 0xA7: // LDA DP Indirect Long
operand = DirectPageIndirectLong();
@@ -1102,6 +1104,8 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
}
LogInstructions(PC, opcode, operand, immediate, accumulator_mode);
uint8_t instructionLength = GetInstructionLength(opcode);
UpdatePC(instructionLength);
}
void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
@@ -1109,7 +1113,7 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
if (flags()->kLogInstructions) {
std::ostringstream oss;
oss << "$" << std::uppercase << std::setw(2) << std::setfill('0')
<< static_cast<int>(DB) << ":" << std::hex << PC << ": 0x" << std::hex
<< static_cast<int>(PB) << ":" << std::hex << PC << ": 0x" << std::hex
<< static_cast<int>(opcode) << " " << opcode_to_mnemonic.at(opcode)
<< " ";
@@ -1138,7 +1142,7 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
} else {
// Log the address and opcode.
std::cout << "$" << std::uppercase << std::setw(2) << std::setfill('0')
<< static_cast<int>(DB) << ":" << std::hex << PC << ": 0x"
<< static_cast<int>(PB) << ":" << std::hex << PC << ": 0x"
<< std::hex << static_cast<int>(opcode) << " "
<< opcode_to_mnemonic.at(opcode) << " ";
@@ -1231,7 +1235,7 @@ void CPU::ANDAbsoluteLong(uint32_t address) {
uint32_t operand32 = memory.ReadWordLong(address);
A &= operand32;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80000000);
SetNegativeFlag(A & 0x8000);
}
// ASL: Arithmetic shift left
@@ -1248,21 +1252,21 @@ void CPU::ASL(uint16_t address) {
// BCC: Branch if carry clear
void CPU::BCC(int8_t offset) {
if (!GetCarryFlag()) { // If the carry flag is clear
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
// BCS: Branch if carry set
void CPU::BCS(int8_t offset) {
if (GetCarryFlag()) { // If the carry flag is set
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
// BEQ: Branch if equal (zero set)
void CPU::BEQ(int8_t offset) {
if (GetZeroFlag()) { // If the zero flag is set
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
@@ -1277,26 +1281,27 @@ void CPU::BIT(uint16_t address) {
// BMI: Branch if minus (negative set)
void CPU::BMI(int8_t offset) {
if (GetNegativeFlag()) { // If the negative flag is set
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
// BNE: Branch if not equal (zero clear)
void CPU::BNE(int8_t offset) {
if (!GetZeroFlag()) { // If the zero flag is clear
PC += offset; // Add the offset to the program counter
// PC += offset;
next_pc_ = offset;
}
}
// BPL: Branch if plus (negative clear)
void CPU::BPL(int8_t offset) {
if (!GetNegativeFlag()) { // If the negative flag is clear
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
// BRA: Branch always
void CPU::BRA(int8_t offset) { PC += offset; }
void CPU::BRA(int8_t offset) { next_pc_ = offset; }
// BRK: Break
void CPU::BRK() {
@@ -1312,21 +1317,19 @@ void CPU::BRK() {
}
// BRL: Branch always long
void CPU::BRL(int16_t offset) {
PC += offset; // Add the offset to the program counter
}
void CPU::BRL(int16_t offset) { next_pc_ = offset; }
// BVC: Branch if overflow clear
void CPU::BVC(int8_t offset) {
if (!GetOverflowFlag()) { // If the overflow flag is clear
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
// BVS: Branch if overflow set
void CPU::BVS(int8_t offset) {
if (GetOverflowFlag()) { // If the overflow flag is set
PC += offset; // Add the offset to the program counter
next_pc_ = offset;
}
}
@@ -1497,40 +1500,41 @@ void CPU::INY() {
// JMP: Jump
void CPU::JMP(uint16_t address) {
PC = address; // Set program counter to the new address
next_pc_ = address; // Set program counter to the new address
}
// JML: Jump long
void CPU::JML(uint32_t address) {
// Set the lower 16 bits of PC to the lower 16 bits of the address
PC = static_cast<uint8_t>(address & 0xFFFF);
next_pc_ = static_cast<uint16_t>(address & 0xFFFF);
// Set the PBR to the upper 8 bits of the address
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
}
// JSR: Jump to subroutine
void CPU::JSR(uint16_t address) {
PC -= 1; // Subtract 1 from program counter
memory.PushWord(PC); // Push the program counter onto the stack
PC = address; // Set program counter to the new address
next_pc_ = address; // Set program counter to the new address
}
// JSL: Jump to subroutine long
void CPU::JSL(uint32_t address) {
PC -= 1; // Subtract 1 from program counter
memory.PushLong(PC); // Push the program counter onto the stack as a long
// value (24 bits)
PC = address; // Set program counter to the new address
next_pc_ = address; // Set program counter to the new address
}
// LDA: Load accumulator
void CPU::LDA(uint16_t address, bool isImmediate) {
void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page) {
uint8_t bank = PB;
if (direct_page) {
bank = 0;
}
if (GetAccumulatorSize()) {
A = isImmediate ? address : memory.ReadByte(address);
A = isImmediate ? address : memory.ReadByte((bank << 16) | address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else {
A = isImmediate ? address : memory.ReadWord(address);
A = isImmediate ? address : memory.ReadWord((bank << 16) | address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
@@ -1728,7 +1732,7 @@ void CPU::RTL() {
}
// RTS: Return from subroutine
void CPU::RTS() { PC = memory.PopWord() + 1; } // ASL: Arithmetic shift left
void CPU::RTS() { last_call_frame_ = memory.PopWord(); }
void CPU::SBC(uint16_t value, bool isImmediate) {
uint16_t operand;
@@ -1937,6 +1941,250 @@ void CPU::XCE() {
E = carry;
}
uint8_t CPU::GetInstructionLength(uint8_t opcode) {
switch (opcode) {
case 0x00: // BRK
return 0;
case 0x60: // RTS
PC = last_call_frame_;
return 3;
// TODO: Handle JMPs in logging.
case 0x20: // JSR Absolute
case 0x4C: // JMP Absolute
case 0x6C: // JMP Absolute Indirect
case 0x5C: // JMP Absolute Indexed Indirect
case 0x22: // JSL Absolute Long
PC = next_pc_;
return 0;
// Branch Instructions (BCC, BCS, BNE, BEQ, etc.)
case 0x90: // BCC near
if (!GetCarryFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0xB0: // BCS near
if (GetCarryFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0x30: // BMI near
if (GetNegativeFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0xF0: // BEQ near
if (GetZeroFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0xD0: // BNE Relative
if (!GetZeroFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0x10: // BPL Relative
if (!GetNegativeFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0x50: // BVC Relative
if (!GetOverflowFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0x70: // BVS Relative
if (GetOverflowFlag()) {
PC += next_pc_;
return 0;
} else {
return 2;
}
case 0x80: // BRA Relative
PC += next_pc_;
return 0;
case 0x82: // BRL Relative Long
PC += next_pc_;
return 0;
// Single Byte Instructions
case 0x18: // CLC
case 0xD8: // CLD
case 0x58: // CLI
case 0xB8: // CLV
case 0xCA: // DEX
case 0x88: // DEY
case 0xE8: // INX
case 0xC8: // INY
case 0xEA: // NOP
case 0x48: // PHA
case 0x0B: // PHD
case 0x4B: // PHK
case 0x08: // PHP
case 0xDA: // PHX
case 0x5A: // PHY
case 0x68: // PLA
case 0xAB: // PLB
case 0x2B: // PLD
case 0x28: // PLP
case 0xFA: // PLX
case 0x7A: // PLY
case 0xC2: // REP
case 0x40: // RTI
case 0x38: // SEC
case 0xF8: // SED
case 0xE2: // SEP
case 0x78: // SEI
case 0xAA: // TAX
case 0xA8: // TAY
case 0xBA: // TSX
case 0x8A: // TXA
case 0x9A: // TXS
case 0x98: // TYA
case 0x0A: // ASL Accumulator
return 1;
// Two Byte Instructions
case 0x69: // ADC Immediate
case 0x29: // AND Immediate
case 0xC9: // CMP Immediate
case 0xE0: // CPX Immediate
case 0xC0: // CPY Immediate
case 0x49: // EOR Immediate
case 0xA9: // LDA Immediate
case 0xA2: // LDX Immediate
case 0xA0: // LDY Immediate
case 0x09: // ORA Immediate
case 0xE9: // SBC Immediate
case 0x65: // ADC Direct Page
case 0x72: // ADC Direct Page Indirect
case 0x67: // ADC Direct Page Indirect Long
case 0x75: // ADC Direct Page Indexed, X
case 0x61: // ADC Direct Page Indirect, X
case 0x71: // ADC DP Indirect Indexed, Y
case 0x77: // ADC DP Indirect Long Indexed, Y
case 0x63: // ADC Stack Relative
case 0x73: // ADC SR Indirect Indexed, Y
return GetAccumulatorSize() ? 2 : 3;
case 0x7D: // ADC Absolute Indexed, X
case 0x79: // ADC Absolute Indexed, Y
case 0x6D: // ADC Absolute
return 3;
case 0x7F: // ADC Absolute Long Indexed, X
case 0x6F: // ADC Absolute Long
return 4;
case 0xA5: // LDA Direct Page
case 0x05: // ORA Direct Page
case 0x85: // STA Direct Page
case 0xC6: // DEC Direct Page
case 0x97: // STA Direct Page Indexed Y
case 0x25: // AND Direct Page
case 0x32: // AND Direct Page Indirect Indexed Y
case 0x27: // AND Direct Page Indirect Long
case 0x35: // AND Direct Page Indexed X
case 0x21: // AND Direct Page Indirect Indexed Y
case 0x31: // AND Direct Page Indirect Long Indexed Y
case 0x37: // AND Direct Page Indirect Long Indexed Y
case 0x23: // AND Direct Page Indirect Indexed X
case 0x33: // AND Direct Page Indirect Long Indexed Y
case 0xE6: // INC Direct Page
return 2;
// ASL (Arithmetic Shift Left)
case 0x06: // ASL Direct Page
case 0x16: // ASL Direct Page Indexed, X
return 2;
case 0x0E: // ASL Absolute
case 0x1E: // ASL Absolute Indexed, X
return 3;
// Three Byte Instructions
case 0x2D: // AND Absolute
case 0xCD: // CMP Absolute
case 0xEC: // CPX Absolute
case 0xCC: // CPY Absolute
case 0x4D: // EOR Absolute
case 0xAD: // LDA Absolute
case 0xAE: // LDX Absolute
case 0xAC: // LDY Absolute
case 0x0D: // ORA Absolute
case 0xED: // SBC Absolute
case 0x8D: // STA Absolute
case 0x8E: // STX Absolute
case 0x8C: // STY Absolute
case 0xBD: // LDA Absolute Indexed X
case 0xBC: // LDY Absolute Indexed X
case 0x3D: // AND Absolute Indexed X
case 0x39: // AND Absolute Indexed Y
return 3;
// Four Byte Instructions
case 0x2F: // AND Absolute Long
case 0xCF: // CMP Absolute Long
case 0x4F: // EOR Absolute Long
case 0xAF: // LDA Absolute Long
case 0x0F: // ORA Absolute Long
case 0xEF: // SBC Absolute Long
case 0x8F: // STA Absolute Long
case 0x3F: // AND Absolute Long Indexed X
return 4;
// BIT (Bit Test)
// Include all BIT variants similar to ADC
// BRK (Break) and COP (Co-Processor Enable)
case 0x02: // COP param
return 2;
// CMP (Compare Accumulator)
// Include all CMP variants similar to ADC
// CPX (Compare X Register) and CPY (Compare Y Register)
// Include CPX and CPY variants
// DEC (Decrement Memory)
// Include DEC variants similar to ASL
// EOR (Exclusive OR with Accumulator)
// Include all EOR variants similar to ADC
// Variable length or Special cases
// Add cases for instructions with variable lengths or special handling
default:
auto mnemonic = opcode_to_mnemonic.at(opcode);
std::cerr << "Unknown instruction length: " << std::hex
<< static_cast<int>(opcode) << ", " << mnemonic << std::endl;
return 1; // Default to 1 as a safe fallback
}
}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -134,7 +134,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// Low: First operand byte
//
// LDA addr
uint16_t Absolute() { return FetchWord(); }
uint16_t Absolute() { return ReadWord((PB << 16) | PC + 1); }
// Effective Address:
// The Data Bank Register is concatened with the 16-bit operand
@@ -162,7 +162,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// JMP (addr, X)
uint16_t AbsoluteIndexedIndirect() {
uint16_t address = FetchWord() + X;
return memory.ReadWord(address & 0xFFFF); // Consider PBR if needed
return memory.ReadWord((PB << 16) | address & 0xFFFF);
}
// Effective Address:
@@ -173,7 +173,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// JMP (addr)
uint16_t AbsoluteIndirect() {
uint16_t address = FetchWord();
return memory.ReadWord(address);
return memory.ReadWord((PB << 16) | address);
}
// Effective Address:
@@ -183,7 +183,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// JMP [addr]
uint32_t AbsoluteIndirectLong() {
uint16_t address = FetchWord();
return memory.ReadWordLong(address);
return memory.ReadWordLong((PB << 16) | address);
}
// Effective Address:
@@ -224,7 +224,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
//
// LDA dp
uint16_t DirectPage() {
uint8_t dp = FetchByte();
uint8_t dp = memory.ReadByte((PB << 16) | PC + 1);
return D + dp;
}
@@ -235,8 +235,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
//
// LDA dp, X
uint16_t DirectPageIndexedX() {
uint8_t dp = FetchByte();
return (dp + X) & 0xFF;
uint8_t operand = FetchByte();
return D + operand + X;
}
// Effective Address:
@@ -326,9 +326,9 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// LDA #const
uint16_t Immediate() {
if (GetAccumulatorSize()) {
return FetchByte();
return memory.ReadByte((PB << 16) | PC + 1);
} else {
return FetchWord();
return memory.ReadWord((PB << 16) | PC + 1);
}
}
@@ -342,6 +342,91 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
return memory.ReadWord(SP() + sr + Y);
}
// Memory access routines
uint8_t ReadByte(uint32_t address) const override {
return memory.ReadByte(address);
}
uint16_t ReadWord(uint32_t address) const override {
return memory.ReadWord(address);
}
uint32_t ReadWordLong(uint32_t address) const override {
return memory.ReadWordLong(address);
}
std::vector<uint8_t> ReadByteVector(uint32_t address,
uint16_t size) const override {
return memory.ReadByteVector(address, size);
}
void WriteByte(uint32_t address, uint8_t value) override {
memory.WriteByte(address, value);
}
void WriteWord(uint32_t address, uint16_t value) override {
memory.WriteWord(address, value);
}
uint8_t FetchByte() {
uint32_t address = (PB << 16) | PC + 1;
uint8_t byte = memory.ReadByte(address);
return byte;
}
uint16_t FetchWord() {
uint32_t address = (PB << 16) | PC + 1;
uint16_t value = memory.ReadWord(address);
return value;
}
uint32_t FetchLong() {
uint32_t value = memory.ReadWordLong((PB << 16) | PC + 1);
return value;
}
int8_t FetchSignedByte() { return static_cast<int8_t>(FetchByte()); }
int16_t FetchSignedWord() {
auto offset = static_cast<int16_t>(FetchWord());
return offset;
}
uint8_t FetchByteDirectPage(uint8_t operand) {
uint16_t distance = D * 0x100;
// Calculate the effective address in the Direct Page
uint16_t effectiveAddress = operand + distance;
// Fetch the byte from memory
uint8_t fetchedByte = memory.ReadByte(effectiveAddress);
next_pc_ = PC + 1;
// PC++; // Increment the Program Counter
return fetchedByte;
}
uint16_t ReadByBitMode(uint32_t address) {
if (GetAccumulatorSize()) {
// 8-bit mode
return memory.ReadByte(address) & 0xFF;
} else {
// 16-bit mode
return memory.ReadWord(address);
}
}
void UpdatePC(uint8_t instruction_length) { PC += instruction_length; }
uint8_t GetInstructionLength(uint8_t opcode);
void SetMemory(const std::vector<uint8_t>& data) override {
memory.SetMemory(data);
}
int16_t SP() const override { return memory.SP(); }
void SetSP(int16_t value) override { memory.SetSP(value); }
void set_next_pc(uint16_t value) { next_pc_ = value; }
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
// ======================================================
// Instructions
@@ -449,7 +534,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void JSL(uint32_t address);
// LDA: Load accumulator
void LDA(uint16_t address, bool isImmediate = false);
void LDA(uint16_t address, bool isImmediate = false,
bool direct_page = false);
// LDX: Load X register
void LDX(uint16_t address, bool isImmediate = false);
@@ -622,78 +708,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// XCE: Exchange carry and emulation bits
void XCE();
// Memory access routines
uint8_t ReadByte(uint32_t address) const override {
auto value = memory.ReadByte(address);
return value;
}
uint16_t ReadWord(uint32_t address) const override {
return memory.ReadWord(address);
}
uint32_t ReadWordLong(uint32_t address) const override {
return memory.ReadWordLong(address);
}
std::vector<uint8_t> ReadByteVector(uint32_t address,
uint16_t size) const override {
return memory.ReadByteVector(address, size);
}
void WriteByte(uint32_t address, uint8_t value) override {
memory.WriteByte(address, value);
}
void WriteWord(uint32_t address, uint16_t value) override {
memory.WriteWord(address, value);
}
uint8_t FetchByte() {
uint8_t byte = memory.ReadByte(PC); // Read a byte from memory at PC
PC++; // Increment the Program Counter
return byte;
}
uint16_t FetchWord() {
uint16_t value = memory.ReadWord(PC);
PC += 2;
return value;
}
uint32_t FetchLong() {
uint32_t value = memory.ReadWordLong(PC);
PC += 3;
return value;
}
int8_t FetchSignedByte() { return static_cast<int8_t>(FetchByte()); }
int16_t FetchSignedWord() {
auto offset = static_cast<int16_t>(FetchWord());
return offset;
}
uint8_t FetchByteDirectPage(uint8_t operand) {
uint16_t distance = D * 0x100;
// Calculate the effective address in the Direct Page
uint16_t effectiveAddress = operand + distance;
// Fetch the byte from memory
uint8_t fetchedByte = memory.ReadByte(effectiveAddress);
PC++; // Increment the Program Counter
return fetchedByte;
}
void SetMemory(const std::vector<uint8_t>& data) override {
memory.SetMemory(data);
}
int16_t SP() const override { return memory.SP(); }
void SetSP(int16_t value) override { memory.SetSP(value); }
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
private:
void compare(uint16_t register_value, uint16_t memory_value) {
uint16_t result;
@@ -738,6 +752,9 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
uint8_t operator[](int i) const override { return 0; }
uint8_t at(int i) const override { return 0; }
uint16_t last_call_frame_;
uint16_t next_pc_;
Memory& memory;
Clock& clock;
};

View File

@@ -9,6 +9,7 @@
#include "app/core/constants.h"
#include "app/emu/snes.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
namespace yaze {
@@ -23,104 +24,6 @@ bool ShouldDisplay(const InstructionEntry& entry, const char* filter,
return true;
}
void DrawMemoryWindow(Memory* memory) {
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
static ImGuiTableFlags flags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
if (auto outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
ImGui::BeginTable("table1", 4, flags, outer_size)) {
// Table headers
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Memory Area");
ImGui::TableNextColumn();
ImGui::Text("Start Address");
ImGui::TableNextColumn();
ImGui::Text("Size");
ImGui::TableNextColumn();
ImGui::Text("Mapping");
// Retrieve memory information from MemoryImpl
MemoryImpl* memoryImpl = dynamic_cast<MemoryImpl*>(memory);
if (memoryImpl) {
// Display memory areas
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 0-63");
ImGui::TableNextColumn();
ImGui::Text("0x8000");
ImGui::TableNextColumn();
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 64-111");
ImGui::TableNextColumn();
ImGui::Text("0x0000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("RAM");
ImGui::TableNextColumn();
ImGui::Text("0x700000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("System RAM (WRAM)");
ImGui::TableNextColumn();
ImGui::Text("0x7E0000");
ImGui::TableNextColumn();
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 128-191");
ImGui::TableNextColumn();
ImGui::Text("0x8000");
ImGui::TableNextColumn();
ImGui::Text("128 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("ROM Bank 192-255");
ImGui::TableNextColumn();
ImGui::Text("0x0000");
ImGui::TableNextColumn();
ImGui::Text("64 KB");
ImGui::TableNextColumn();
ImGui::Text("LoROM");
}
ImGui::EndTable();
if (ImGui::Button("Open Memory Viewer", ImVec2(200, 50))) {
ImGui::OpenPopup("Memory Viewer");
}
if (ImGui::BeginPopupModal("Memory Viewer", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
static MemoryEditor mem_edit;
mem_edit.DrawContents((void*)memoryImpl->data(), memoryImpl->size());
ImGui::EndPopup();
}
}
}
} // namespace
using ImGui::NextColumn;
@@ -130,105 +33,28 @@ using ImGui::TableNextColumn;
using ImGui::Text;
void Emulator::Run() {
if (!snes_.running() && loading_) {
// Setup and initialize memory
if (loading_ && !memory_setup_) {
snes_.SetupMemory(*rom());
memory_setup_ = true;
}
if (!snes_.running() && rom()->isLoaded()) {
snes_.SetupMemory(*rom());
}
// Run the emulation
if (rom()->isLoaded() && power_) {
snes_.Init(*rom());
running_ = true;
}
// Setup and initialize memory
if (loading_ && !running_) {
snes_.Init(*rom());
running_ = true;
}
RenderNavBar();
ImGui::Button(ICON_MD_ARROW_FORWARD_IOS);
ImGui::SameLine();
ImGui::Button(ICON_MD_DOUBLE_ARROW);
ImGui::SameLine();
ImGui::Button(ICON_MD_SUBDIRECTORY_ARROW_RIGHT);
if (running_) {
HandleEvents();
UpdateEmulator();
snes_.Run();
}
RenderEmulator();
if (debugger_) {
RenderDebugger();
}
}
void Emulator::RenderEmulator() {
ImVec2 size = ImVec2(320, 240);
if (snes_.running()) {
ImGui::BeginChild(
"EmulatorOutput", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
ImGui::Image((void*)snes_.ppu().GetScreen()->texture(), size, ImVec2(0, 0),
ImVec2(1, 1));
ImGui::EndChild();
ImGui::Separator();
} else {
ImGui::BeginChild(
"EmulatorOutput", ImVec2(0, 0), true,
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
ImGui::Dummy(size);
ImGui::EndChild();
ImGui::Separator();
ImGui::Text("Emulator output not available.");
}
}
void Emulator::RenderNavBar() {
MENU_BAR()
if (ImGui::BeginMenu("Game")) {
MENU_ITEM("Load ROM") { loading_ = true; }
MENU_ITEM("Power On") { power_ = true; }
MENU_ITEM("Power Off") {
power_ = false;
running_ = false;
loading_ = false;
debugger_ = false;
}
MENU_ITEM("Pause") {
running_ = false;
debugger_ = false;
}
MENU_ITEM("Reset") {}
MENU_ITEM("Save State") {}
MENU_ITEM("Load State") {}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Debug")) {
MENU_ITEM("Debugger") { debugger_ = !debugger_; }
if (ImGui::MenuItem("Integrated Debugger", nullptr,
&integrated_debugger_mode_)) {
separate_debugger_mode_ = !integrated_debugger_mode_;
}
if (ImGui::MenuItem("Separate Debugger Windows", nullptr,
&separate_debugger_mode_)) {
integrated_debugger_mode_ = !separate_debugger_mode_;
}
MENU_ITEM("Memory Viewer") {}
MENU_ITEM("Tile Viewer") {}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Options")) {
MENU_ITEM("Input") {}
MENU_ITEM("Audio") {}
@@ -236,6 +62,93 @@ void Emulator::RenderNavBar() {
ImGui::EndMenu();
}
END_MENU_BAR()
if (ImGui::Button(ICON_MD_PLAY_ARROW)) {
loading_ = true;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Start Emulation");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_PAUSE)) {
snes_.SetCpuMode(1);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Pause Emulation");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SKIP_NEXT)) {
// Step through Code logic
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Step Through Code");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_REFRESH)) {
// Reset Emulator logic
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reset Emulator");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_STOP)) {
// Stop Emulation logic
running_ = false;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Stop Emulation");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SAVE)) {
// Save State logic
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Save State");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SYSTEM_UPDATE_ALT)) {
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Load State");
}
// Additional elements
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SETTINGS)) {
// Settings logic
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Settings");
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_INFO)) {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("About Debugger");
}
// About Debugger logic
}
static bool show_memory_viewer = false;
ImGui::SameLine();
if (ImGui::Button(ICON_MD_MEMORY)) {
show_memory_viewer = !show_memory_viewer;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Memory Viewer");
}
if (show_memory_viewer) {
ImGui::Begin("Memory Viewer", &show_memory_viewer);
RenderMemoryViewer();
ImGui::End();
}
}
void Emulator::HandleEvents() {
@@ -243,38 +156,51 @@ void Emulator::HandleEvents() {
// ...
}
void Emulator::UpdateEmulator() {
// Update the emulator state (CPU, PPU, APU, etc.)
// ...
snes_.Run();
void Emulator::RenderEmulator() {
if (ImGui::BeginTable("##Emulator", 3,
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupColumn("CPU");
ImGui::TableSetupColumn("PPU");
ImGui::TableHeadersRow();
TableNextColumn();
RenderCpuInstructionLog(snes_.cpu().instruction_log_);
TableNextColumn();
RenderSnesPpu();
RenderBreakpointList();
TableNextColumn();
ImGui::BeginChild("##", ImVec2(0, 0), true,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
RenderCpuState(snes_.cpu());
ImGui::EndChild();
ImGui::EndTable();
}
}
void Emulator::RenderDebugger() {
// Define a lambda with the actual debugger
auto debugger = [&]() {
if (ImGui::BeginTable(
"DebugTable", 3,
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
TableNextColumn();
RenderCpuState(snes_.cpu());
void Emulator::RenderSnesPpu() {
ImVec2 size = ImVec2(320, 240);
if (snes_.running()) {
ImGui::BeginChild("EmulatorOutput", ImVec2(0, 240), true,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
ImGui::Image((void*)snes_.ppu().GetScreen()->texture(), size, ImVec2(0, 0),
ImVec2(1, 1));
ImGui::EndChild();
TableNextColumn();
RenderCPUInstructionLog(snes_.cpu().instruction_log_);
TableNextColumn();
RenderBreakpointList();
DrawMemoryWindow(snes_.Memory());
ImGui::EndTable();
}
};
if (integrated_debugger_mode_) {
debugger();
} else if (separate_debugger_mode_) {
ImGui::Begin("Debugger");
debugger();
ImGui::End();
} else {
ImGui::Text("Emulator output not available.");
ImGui::BeginChild("EmulatorOutput", ImVec2(0, 240), true,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
ImGui::SetCursorPosX(((ImGui::GetWindowSize().x * 0.5f) - size.x) * 0.5f);
ImGui::SetCursorPosY(((ImGui::GetWindowSize().y * 0.5f) - size.y) * 0.5f);
ImGui::Dummy(size);
ImGui::EndChild();
}
ImGui::Separator();
}
void Emulator::RenderBreakpointList() {
@@ -326,6 +252,13 @@ void Emulator::RenderBreakpointList() {
}
ImGui::EndChild();
}
Separator();
gui::InputHexByte("PB", &manual_pb_, 1, 25.f);
gui::InputHexWord("PC", &manual_pc_, 25.f);
if (ImGui::Button("Set Current Address")) {
snes_.cpu().PC = manual_pc_;
snes_.cpu().PB = manual_pb_;
}
}
void Emulator::RenderCpuState(CPU& cpu) {
@@ -352,26 +285,74 @@ void Emulator::RenderCpuState(CPU& cpu) {
ImGui::Columns(1);
Separator();
}
// Call Stack
if (ImGui::CollapsingHeader("Call Stack", ImGuiTreeNodeFlags_DefaultOpen)) {
// For each return address in the call stack:
Text("Return Address: 0x%08X", 0xFFFFFF); // Placeholder
}
static int debugger_mode_ = 0;
const char* debugger_modes_[] = {"Run", "Step", "Pause"};
Text("Mode");
ImGui::ListBox("##DebuggerMode", &debugger_mode_, debugger_modes_,
IM_ARRAYSIZE(debugger_modes_));
snes_.SetCpuMode(debugger_mode_);
snes_.SetCpuMode(0);
}
void Emulator::RenderMemoryViewer() {
// Render memory viewer
static MemoryEditor mem_edit;
if (ImGui::Button("RAM")) {
mem_edit.GotoAddrAndHighlight(0x7E0000, 0x7E0001);
}
if (ImGui::BeginTable("MemoryViewerTable", 2,
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupColumn("Bookmarks");
ImGui::TableSetupColumn("Memory");
ImGui::TableHeadersRow();
TableNextColumn();
if (ImGui::CollapsingHeader("Bookmarks", ImGuiTreeNodeFlags_DefaultOpen)) {
// Input for adding a new bookmark
static char nameBuf[256];
static uint64_t uint64StringBuf;
ImGui::InputText("Name", nameBuf, IM_ARRAYSIZE(nameBuf));
gui::InputHex("Address", &uint64StringBuf);
if (ImGui::Button("Add Bookmark")) {
bookmarks.push_back({nameBuf, uint64StringBuf});
memset(nameBuf, 0, sizeof(nameBuf));
uint64StringBuf = 0;
}
// Tree view of bookmarks
for (const auto& bookmark : bookmarks) {
if (ImGui::TreeNode(bookmark.name.c_str(), ICON_MD_STAR)) {
auto bookmark_string = absl::StrFormat(
"%s: 0x%08X", bookmark.name.c_str(), bookmark.value);
if (ImGui::Selectable(bookmark_string.c_str())) {
mem_edit.GotoAddrAndHighlight(static_cast<ImU64>(bookmark.value),
1);
}
ImGui::SameLine();
if (ImGui::Button("Delete")) {
// Logic to delete the bookmark
bookmarks.erase(std::remove_if(bookmarks.begin(), bookmarks.end(),
[&](const Bookmark& b) {
return b.name == bookmark.name &&
b.value == bookmark.value;
}),
bookmarks.end());
}
ImGui::TreePop();
}
}
}
TableNextColumn();
mem_edit.DrawContents((void*)snes_.Memory()->data(),
snes_.Memory()->size());
ImGui::EndTable();
}
}
void Emulator::RenderCPUInstructionLog(
void Emulator::RenderCpuInstructionLog(
const std::vector<InstructionEntry>& instructionLog) {
if (ImGui::CollapsingHeader("CPU Instruction Log")) {
// Filtering options
@@ -382,7 +363,7 @@ void Emulator::RenderCPUInstructionLog(
}
// Toggle for showing all opcodes
static bool showAllOpcodes = false;
static bool showAllOpcodes = true;
ImGui::Checkbox("Show All Opcodes", &showAllOpcodes);
// Instruction list
@@ -391,10 +372,11 @@ void Emulator::RenderCPUInstructionLog(
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
for (const auto& entry : instructionLog) {
if (ShouldDisplay(entry, filterBuf, showAllOpcodes)) {
if (ImGui::Selectable(absl::StrFormat("%04X: %02X %s %s", entry.address,
entry.opcode, entry.operands,
entry.instruction)
.c_str())) {
if (ImGui::Selectable(
absl::StrFormat("%06X: %s %s", entry.address,
opcode_to_mnemonic.at(entry.opcode),
entry.operands)
.c_str())) {
// Logic to handle click (e.g., jump to address, set breakpoint)
}
}

View File

@@ -1,6 +1,8 @@
#ifndef YAZE_APP_CORE_EMULATOR_H
#define YAZE_APP_CORE_EMULATOR_H
#include <imgui/imgui.h>
#include <cstdint>
#include <vector>
@@ -13,39 +15,34 @@ namespace emu {
class Emulator : public SharedROM {
public:
// Runs the emulator loop, including event handling and rendering
void Run();
private:
// Renders the emulator output to an ImGui child window
void RenderEmulator();
// Draws the navigation bar with various controls
void RenderNavBar();
// Handles user input events
void HandleEvents();
// Updates the emulator state (CPU, PPU, APU, etc.)
void UpdateEmulator();
void RenderDebugger();
void RenderEmulator();
void RenderSnesPpu();
void RenderBreakpointList();
void RenderCpuState(CPU& cpu);
void RenderMemoryViewer();
void RenderCPUInstructionLog(
struct Bookmark {
std::string name;
uint64_t value;
};
std::vector<Bookmark> bookmarks;
void RenderCpuInstructionLog(
const std::vector<InstructionEntry>& instructionLog);
SNES snes_;
uint16_t manual_pc_ = 0;
uint8_t manual_pb_ = 0;
bool power_ = false;
bool loading_ = false;
bool running_ = false;
bool debugger_ = true;
bool memory_setup_ = false;
bool integrated_debugger_mode_ = true;
bool separate_debugger_mode_ = false;
};
} // namespace emu

View File

@@ -0,0 +1,118 @@
#pragma once
#include <cstdint>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
#include "app/emu/internal/opcodes.h"
namespace yaze {
namespace app {
namespace emu {
class AsmParser {
public:
std::vector<uint8_t> Parse(const std::string& instruction) {
std::smatch match;
if (!std::regex_match(instruction, match, instruction_regex_)) {
throw std::runtime_error("Invalid instruction format: " + instruction);
}
std::string mnemonic = match[1];
std::string addressing_mode = match[2];
std::string operand = match[3];
std::string lookup_string = mnemonic.substr(0, 3);
auto opcode_entry = mnemonic_to_opcode_.find(mnemonic);
if (opcode_entry == mnemonic_to_opcode_.end()) {
throw std::runtime_error(
"Unknown mnemonic or addressing mode: " + mnemonic + addressing_mode);
}
std::vector<uint8_t> bytes = {opcode_entry->second};
// AppendOperandBytes(bytes, operand, addressing_mode);
return bytes;
}
void CreateInternalOpcodeMap() {
for (const auto& opcode_entry : opcode_to_mnemonic) {
std::string name = opcode_entry.second;
uint8_t opcode = opcode_entry.first;
mnemonic_to_opcode_[name] = opcode;
}
}
private:
void AppendOperandBytes(std::vector<uint8_t>& bytes,
const std::string& operand,
const std::string& addressing_mode) {
if (addressing_mode == ".b") {
bytes.push_back(static_cast<uint8_t>(std::stoi(operand, nullptr, 16)));
} else if (addressing_mode == ".w") {
uint16_t word_operand =
static_cast<uint16_t>(std::stoi(operand, nullptr, 16));
bytes.push_back(static_cast<uint8_t>(word_operand & 0xFF));
bytes.push_back(static_cast<uint8_t>((word_operand >> 8) & 0xFF));
} else if (addressing_mode == ".l") {
uint32_t long_operand =
static_cast<uint32_t>(std::stoul(operand, nullptr, 16));
bytes.push_back(static_cast<uint8_t>(long_operand & 0xFF));
bytes.push_back(static_cast<uint8_t>((long_operand >> 8) & 0xFF));
bytes.push_back(static_cast<uint8_t>((long_operand >> 16) & 0xFF));
}
}
enum class AddressingMode {
kAbsolute,
kAbsoluteLong,
kAbsoluteIndexedIndirect,
kAbsoluteIndexedX,
kAbsoluteIndexedY,
kAbsoluteIndirect,
kAbsoluteIndirectLong,
kAbsoluteLongIndexedX,
kAccumulator,
kBlockMove,
kDirectPage,
kDirectPageIndexedX,
kDirectPageIndexedY,
kDirectPageIndirect,
kDirectPageIndirectIndexedY,
kDirectPageIndirectLong,
kDirectPageIndirectLongIndexedY,
kDirectPageIndirectIndexedX,
kDirectPageIndirectLongIndexedX,
kImmediate,
kImplied,
kProgramCounterRelative,
kProgramCounterRelativeLong,
kStackRelative,
kStackRelativeIndirectIndexedY,
kStackRelativeIndirectIndexedYLong,
kStack,
kStackRelativeIndexedY,
};
AddressingMode InferAddressingModeFromOperand(const std::string& operand) {
if (operand[0] == '$') {
return AddressingMode::kAbsolute;
} else if (operand[0] == '#') {
return AddressingMode::kImmediate;
} else {
return AddressingMode::kImplied;
}
}
const std::regex instruction_regex_{R"((\w+)\s*(\.\w)?\s*(\$\w+|\#\w+|\w+))"};
std::unordered_map<std::string, uint8_t> mnemonic_to_opcode_;
};
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,82 @@
#include "app/emu/memory/memory.h"
#include <imgui/imgui.h>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#include "app/emu/debug/log.h"
namespace yaze {
namespace app {
namespace emu {
void DrawSnesMemoryMapping(const MemoryImpl& memory) {
// Using those as a base value to create width/height that are factor of the
// size of our font
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
const char* column_names[] = {
"Offset", "0x00", "0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "0x07",
"0x08", "0x09", "0x0A", "0x0B", "0x0C", "0x0D", "0x0E", "0x0F", "0x10",
"0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19",
"0x1A", "0x1B", "0x1C", "0x1D", "0x1E", "0x1F"};
const int columns_count = IM_ARRAYSIZE(column_names);
const int rows_count = 16;
static ImGuiTableFlags table_flags =
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX |
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable |
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
ImGuiTableFlags_HighlightHoveredColumn;
static bool bools[columns_count * rows_count] = {};
static int frozen_cols = 1;
static int frozen_rows = 2;
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags,
ImGuiTableFlags_NoBordersInBody);
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags,
ImGuiTableFlags_HighlightHoveredColumn);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags,
ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) {
ImGui::TableSetupColumn(
column_names[0],
ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
for (int n = 1; n < columns_count; n++)
ImGui::TableSetupColumn(column_names[n],
ImGuiTableColumnFlags_AngledHeader |
ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
ImGui::TableAngledHeadersRow();
ImGui::TableHeadersRow();
for (int row = 0; row < rows_count; row++) {
ImGui::PushID(row);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::AlignTextToFramePadding();
ImGui::Text("Offset 0x%04X", row);
for (int column = 1; column < columns_count; column++)
if (ImGui::TableSetColumnIndex(column)) {
ImGui::PushID(column);
ImGui::Checkbox("", &bools[row * columns_count + column]);
ImGui::PopID();
}
ImGui::PopID();
}
ImGui::EndTable();
}
}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -96,8 +96,8 @@ class Observer {
virtual void Notify(uint32_t address, uint8_t data) = 0;
};
constexpr uint32_t kROMStart = 0xC00000;
constexpr uint32_t kROMSize = 0x400000;
constexpr uint32_t kROMStart = 0x008000;
constexpr uint32_t kROMSize = 0x200000;
constexpr uint32_t kRAMStart = 0x7E0000;
constexpr uint32_t kRAMSize = 0x20000;
constexpr uint32_t kVRAMStart = 0x210000;
@@ -163,7 +163,7 @@ class MemoryImpl : public Memory, public Loggable {
// 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 bank = 0x00; bank <= 0x3F; ++bank) {
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
if (romAddress < romSize) {
std::copy(romData.begin() + romAddress,
@@ -360,7 +360,8 @@ class MemoryImpl : public Memory, public Loggable {
} else if (offset <= 0x7FFF) {
return offset - 0x6000 + 0x6000; // Expansion RAM
} else {
return (bank << 15) + (offset - 0x8000) + 0x8000; // ROM
// Return lorom mapping
return (bank << 16) + (offset - 0x8000) + 0x8000; // ROM
}
} else if (bank == 0x7D) {
return offset + 0x7D0000; // SRAM
@@ -390,6 +391,8 @@ class MemoryImpl : public Memory, public Loggable {
MemoryMapping mapping_ = MemoryMapping::SNES_LOROM;
};
void DrawSnesMemoryMapping(const MemoryImpl& memory);
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -48,7 +48,7 @@ class MockMemory : public Memory {
MOCK_METHOD0(ClearMemory, void());
MOCK_CONST_METHOD1(at, uint8_t(int i));
uint8_t operator[](int i) const override { return at(i); }
uint8_t operator[](int i) const override { return memory_[i]; }
void SetMemoryContents(const std::vector<uint8_t>& data) {
memory_.resize(64000);

View File

@@ -28,8 +28,8 @@ uint16_t GetHeaderOffset(const Memory& memory) {
switch (mapMode & 0x07) {
case 0: // LoROM
// offset = 0x7F;
offset = 0xFFC0;
offset = 0x7FC0;
// offset = 0xFFC0;
break;
case 1: // HiROM
offset = 0xFFC0;
@@ -110,13 +110,14 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
void SNES::Init(ROM& rom) {
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
// Disable the emulation flag (switch to 65816 native mode)
cpu_.E = 0;
// Initialize CPU
cpu_.Init();
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader(header_offset);
rom_info_ = ReadRomHeader((0x00 << 16) + header_offset);
cpu_.PC = rom_info_.resetVector;
// Initialize PPU
@@ -215,8 +216,6 @@ void SNES::Init(ROM& rom) {
}
void SNES::Run() {
running_ = true;
const double targetFPS = 60.0; // 60 frames per second
const double frame_time = 1.0 / targetFPS;
double frame_accumulated_time = 0.0;

View File

@@ -78,7 +78,8 @@ class SNES : public DMA {
memory_.AddObserver(&ppu_);
// Load the ROM into memory and set up the memory mapping
memory_.Initialize(rom.vector());
rom_data = rom.vector();
memory_.Initialize(rom_data);
}
private:

View File

@@ -56,7 +56,7 @@ void Ppu::RenderScanline() {
// Fetch the tile data from VRAM, tile map data from memory, and palette data
// from CGRAM
UpdateTileData(); // Fetches the tile data from VRAM and stores it in an
// UpdateTileData(); // Fetches the tile data from VRAM and stores it in an
// internal buffer
UpdateTileMapData(); // Fetches the tile map data from memory and stores it
// in an internal buffer