Skip to content

Commit

Permalink
Move registers from CPU to InstrContext (#39) (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
kremi151 committed Jan 2, 2021
1 parent 5651182 commit f6419e2
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 77 deletions.
42 changes: 12 additions & 30 deletions core/source/emulator/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down
14 changes: 1 addition & 13 deletions core/source/emulator/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ namespace FunkyBoy {
std::ofstream file;
#endif

u8 registers[8]{};

i8 timerOverflowingCycles;
bool delayedTIMAIncrease;

Expand All @@ -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);
Expand Down
12 changes: 11 additions & 1 deletion core/source/operands/instruction_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion core/source/operands/instruction_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
76 changes: 44 additions & 32 deletions test/source/unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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];

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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];

Expand All @@ -278,31 +286,33 @@ 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) {
auto memory = createMemory();
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];

Expand All @@ -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++) {
Expand All @@ -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
Expand All @@ -349,14 +359,16 @@ 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) {
auto memory = createMemory();
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];

Expand All @@ -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);
}
}

Expand Down

0 comments on commit f6419e2

Please sign in to comment.