diff --git a/core/source/emulator/cpu.cpp b/core/source/emulator/cpu.cpp index e1ba469e..6ec1d31c 100644 --- a/core/source/emulator/cpu.cpp +++ b/core/source/emulator/cpu.cpp @@ -43,24 +43,6 @@ CPU::CPU(GameBoyType gbType, const io_registers& ioRegisters) , instructionCompleted(false) #endif { - regB = registers; - regC = registers + 1; - regD = registers + 2; - regE = registers + 3; - regH = registers + 4; - regL = registers + 5; - regF_do_not_use_directly = registers + 6; - regA = registers + 7; - - instrContext.registers = registers; - instrContext.regB = regB; - instrContext.regC = regC; - instrContext.regD = regD; - instrContext.regE = regE; - instrContext.regH = regH; - instrContext.regL = regL; - instrContext.regF = regF_do_not_use_directly; - instrContext.regA = regA; instrContext.operandsPtr = &operands; instrContext.progCounter = 0; instrContext.stackPointer = 0xFFFE; @@ -82,23 +64,23 @@ void CPU::powerUpInit(Memory &memory) { // AF -> 0x01b0 if (gbType == GameBoyType::GameBoyCGB) { - *regA = 0x11; + *instrContext.regA = 0x11; } else { - *regA = 0x01; + *instrContext.regA = 0x01; } - *regF_do_not_use_directly = 0xb0; + *instrContext.regF = 0xb0; // BC -> 0x0013 - *regB = 0x00; - *regC = 0x13; + *instrContext.regB = 0x00; + *instrContext.regC = 0x13; // DE -> 0x00d8 - *regD = 0x00; - *regE = 0xd8; + *instrContext.regD = 0x00; + *instrContext.regE = 0xd8; // HL -> 0x014d - *regH = 0x01; - *regL = 0x4d; + *instrContext.regH = 0x01; + *instrContext.regL = 0x4d; instrContext.stackPointer = 0xFFFE; @@ -379,12 +361,12 @@ void CPU::setProgramCounter(u16 offset) { } u16 CPU::readAF() { - return (*regF_do_not_use_directly & 0b11110000) | (*regA << 8); + return (*instrContext.regF & 0b11110000) | (*instrContext.regA << 8); } void CPU::writeAF(FunkyBoy::u16 val) { - *regF_do_not_use_directly = val & 0b11110000; // Only the 4 most significant bits are written to register F - *regA = (val >> 8) & 0xff; + *instrContext.regF = val & 0b11110000; // Only the 4 most significant bits are written to register F + *instrContext.regA = (val >> 8) & 0xff; } void CPU::serialize(std::ostream &ostream) const { diff --git a/core/source/emulator/cpu.h b/core/source/emulator/cpu.h index 634fe3a2..0460773e 100644 --- a/core/source/emulator/cpu.h +++ b/core/source/emulator/cpu.h @@ -53,8 +53,6 @@ namespace FunkyBoy { std::ofstream file; #endif - u8 registers[8]{}; - i8 timerOverflowingCycles; bool delayedTIMAIncrease; @@ -78,17 +76,7 @@ namespace FunkyBoy { bool instructionCompleted; #endif - // Do not free these pointers, they are proxies to specific locations in the registers array - const Operand *operands; - - u8 *regB; - u8 *regC; - u8 *regD; - u8 *regE; - u8 *regH; - u8 *regL; - u8 *regF_do_not_use_directly; - u8 *regA; + const Operand *operands; // Do not free this pointer public: CPU(GameBoyType gbType, const io_registers& ioRegisters); diff --git a/core/source/operands/instruction_context.cpp b/core/source/operands/instruction_context.cpp index 1f3096c6..7ba8f7f0 100644 --- a/core/source/operands/instruction_context.cpp +++ b/core/source/operands/instruction_context.cpp @@ -21,7 +21,17 @@ using namespace FunkyBoy; -InstrContext::InstrContext(FunkyBoy::GameBoyType gbType): gbType(gbType) { +InstrContext::InstrContext(FunkyBoy::GameBoyType gbType) + : gbType(gbType) +{ + regB = registers; + regC = registers + 1; + regD = registers + 2; + regE = registers + 3; + regH = registers + 4; + regL = registers + 5; + regF = registers + 6; + regA = registers + 7; } void InstrContext::push16Bits(Memory &memory, u16 val) { diff --git a/core/source/operands/instruction_context.h b/core/source/operands/instruction_context.h index a81439d3..f0023a0a 100644 --- a/core/source/operands/instruction_context.h +++ b/core/source/operands/instruction_context.h @@ -50,7 +50,9 @@ namespace FunkyBoy { u8 instr; u8 cbInstr; - u8 *registers; + u8 registers[8]{}; + + // Do not free these pointers, they are proxies to specific locations in the registers array u8 *regB; u8 *regC; u8 *regD; diff --git a/test/source/unit_tests.cpp b/test/source/unit_tests.cpp index 71ed37c5..d62b6f1c 100644 --- a/test/source/unit_tests.cpp +++ b/test/source/unit_tests.cpp @@ -109,19 +109,21 @@ TEST(testReadWriteHLAndAF) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // In this test, we check for enforcing little-endianness cpu.writeAF(0x1234); // 0x34 -> 0b110100 // 0x34 -> F -> 0b110000 -> 0x30 (register F only stores the 4 most significant bits) - assertEquals(0x30, *cpu.regF_do_not_use_directly); - assertEquals(0x12, *cpu.regA); + assertEquals(0x30, *context.regF); + assertEquals(0x12, *context.regA); auto val = cpu.readAF(); assertEquals(0x1230, val); cpu.instrContext.writeHL(0x1806); - assertEquals(0x06, *cpu.regL); - assertEquals(0x18, *cpu.regH); + assertEquals(0x06, *context.regL); + assertEquals(0x18, *context.regH); val = cpu.instrContext.readHL(); assertEquals(0x1806, val); } @@ -131,26 +133,28 @@ TEST(testReadWrite16BitRegisters) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // In this test, we check for enforcing little-endianness // BC cpu.instrContext.write16BitRegister(0, 0x1234); - assertEquals(0x34, *cpu.regC); - assertEquals(0x12, *cpu.regB); + assertEquals(0x34, *context.regC); + assertEquals(0x12, *context.regB); auto val = cpu.instrContext.read16BitRegister(0); assertEquals(0x1234, val); // DE cpu.instrContext.write16BitRegister(1, 0x1806); - assertEquals(0x06, *cpu.regE); - assertEquals(0x18, *cpu.regD); + assertEquals(0x06, *context.regE); + assertEquals(0x18, *context.regD); val = cpu.instrContext.read16BitRegister(1); assertEquals(0x1806, val); // HL cpu.instrContext.write16BitRegister(2, 0x2809); - assertEquals(0x09, *cpu.regL); - assertEquals(0x28, *cpu.regH); + assertEquals(0x09, *context.regL); + assertEquals(0x28, *context.regH); val = cpu.instrContext.read16BitRegister(2); assertEquals(0x2809, val); } @@ -160,6 +164,8 @@ TEST(test16BitLoads) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // Allocate a simulated ROM, will be destroyed by the cartridge's destructor memory.rom = new FunkyBoy::u8[0x105]; @@ -176,8 +182,8 @@ TEST(test16BitLoads) { // PC = initial + fetch(1) + execution(2) + next fetch(1) = initial + 4 assertEquals(initialProgCounter + 4, cpu.instrContext.progCounter); - assertEquals(0x06, (*cpu.regC) & 0xff); - assertEquals(0x18, (*cpu.regB) & 0xff); + assertEquals(0x06, (*context.regC) & 0xff); + assertEquals(0x18, (*context.regB) & 0xff); // Set opcode 0x11 (LD DE,d16) cpu.instrContext.progCounter = initialProgCounter; @@ -187,8 +193,8 @@ TEST(test16BitLoads) { // PC = initial + fetch(1) + execution(2) + next fetch(1) = initial + 4 assertEquals(initialProgCounter + 4, cpu.instrContext.progCounter); - assertEquals(0x06, (*cpu.regE) & 0xff); - assertEquals(0x18, (*cpu.regD) & 0xff); + assertEquals(0x06, (*context.regE) & 0xff); + assertEquals(0x18, (*context.regD) & 0xff); // Set opcode 0x21 (LD HL,d16) cpu.instrContext.progCounter = initialProgCounter; @@ -198,8 +204,8 @@ TEST(test16BitLoads) { // PC = initial + fetch(1) + execution(2) + next fetch(1) = initial + 4 assertEquals(initialProgCounter + 4, cpu.instrContext.progCounter); - assertEquals(0x06, (*cpu.regL) & 0xff); - assertEquals(0x18, (*cpu.regH) & 0xff); + assertEquals(0x06, (*context.regL) & 0xff); + assertEquals(0x18, (*context.regH) & 0xff); // Set opcode 0x31 (LD SP,d16) cpu.instrContext.progCounter = initialProgCounter; @@ -258,6 +264,8 @@ TEST(testHALTBugSkipping) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // Allocate a simulated ROM, will be destroyed by the cartridge's destructor memory.rom = new FunkyBoy::u8[0x105]; @@ -278,24 +286,24 @@ TEST(testHALTBugSkipping) { // Initial fetch without executing anything assertDoFullMachineCycle(cpu, memory); - assertEquals(0x76, cpu.instrContext.instr); - assertEquals(initialProgCounter + 1, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(0x76, context.instr); + assertEquals(initialProgCounter + 1, context.progCounter); + assertEquals(originalA, *context.regA); assertDoFullMachineCycle(cpu, memory); - assertEquals(0x3C, cpu.instrContext.instr); // Fetched next instruction already - assertEquals(initialProgCounter + 1, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(0x3C, context.instr); // Fetched next instruction already + assertEquals(initialProgCounter + 1, context.progCounter); + assertEquals(originalA, *context.regA); assertDoFullMachineCycle(cpu, memory); - assertEquals(0x3C, cpu.instrContext.instr); - assertEquals(initialProgCounter + 2, cpu.instrContext.progCounter); - assertEquals(originalA + 1, *cpu.regA); + assertEquals(0x3C, context.instr); + assertEquals(initialProgCounter + 2, context.progCounter); + assertEquals(originalA + 1, *context.regA); assertDoFullMachineCycle(cpu, memory); - assertEquals(0x00, cpu.instrContext.instr); // Fetched next instruction already - assertEquals(initialProgCounter + 3, cpu.instrContext.progCounter); - assertEquals(originalA + 2, *cpu.regA); + assertEquals(0x00, context.instr); // Fetched next instruction already + assertEquals(initialProgCounter + 3, context.progCounter); + assertEquals(originalA + 2, *context.regA); } TEST(testHALTNoSkippingIfIMEDisabled) { @@ -303,6 +311,8 @@ TEST(testHALTNoSkippingIfIMEDisabled) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // Allocate a simulated ROM, will be destroyed by the cartridge's destructor memory.rom = new FunkyBoy::u8[0x105]; @@ -326,7 +336,7 @@ TEST(testHALTNoSkippingIfIMEDisabled) { assertDoFullMachineCycle(cpu, memory); assertEquals(0x76, cpu.instrContext.instr); assertEquals(initialProgCounter + 1, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(originalA, *context.regA); // Assert that we are indeed in HALT mode (no execution of any opcode) for (int i = 0 ; i < 5 ; i++) { @@ -336,7 +346,7 @@ TEST(testHALTNoSkippingIfIMEDisabled) { assertEquals(0x76, cpu.instrContext.instr); // No fetch of next instruction, we stay at the HALT instruction assertEquals(initialProgCounter + 1, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(originalA, *context.regA); } // Set IE and IF to same value -> should request an interrupt @@ -349,7 +359,7 @@ TEST(testHALTNoSkippingIfIMEDisabled) { assertDoFullMachineCycle(cpu, memory); assertEquals(0x3C, cpu.instrContext.instr & 0xffff); // Fetched next instruction already assertEquals(initialProgCounter + 2, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(originalA, *context.regA); } TEST(testHALTBugHanging) { @@ -357,6 +367,8 @@ TEST(testHALTBugHanging) { FunkyBoy::CPU cpu(TEST_GB_TYPE, memory.getIoRegisters()); cpu.powerUpInit(memory); + FunkyBoy::InstrContext &context = cpu.instrContext; + // Allocate a simulated ROM, will be destroyed by the cartridge's destructor memory.rom = new FunkyBoy::u8[0x105]; @@ -381,7 +393,7 @@ TEST(testHALTBugHanging) { assertDoFullMachineCycle(cpu, memory); assertEquals(0x76, cpu.instrContext.instr); assertEquals(initialProgCounter + 1, cpu.instrContext.progCounter); - assertEquals(originalA, *cpu.regA); + assertEquals(originalA, *context.regA); } }