Skip to content

Commit

Permalink
Use references of Memory in CPU (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
kremi151 committed Dec 10, 2020
1 parent 8b51bb3 commit fad90f9
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 71 deletions.
115 changes: 55 additions & 60 deletions core/source/emulator/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@

#include "cpu.h"

#include <utility>
#include <util/typedefs.h>
#include <util/registers.h>
#include <util/return_codes.h>
#include <emulator/io_registers.h>

using namespace FunkyBoy;

CPU::CPU(GameBoyType gbType, MemoryPtr memory, const io_registers& ioRegisters)
: memory(std::move(memory))
, gbType(gbType)
CPU::CPU(GameBoyType gbType, const io_registers& ioRegisters)
: gbType(gbType)
, ioRegisters(ioRegisters)
, instrContext(gbType)
, timerOverflowingCycles(-1)
Expand Down Expand Up @@ -74,12 +72,9 @@ CPU::CPU(GameBoyType gbType, MemoryPtr memory, const io_registers& ioRegisters)
// To simulate this, we set a NOP as the first instruction, which does nothing
operands[0] = Operands::nop;
operands[1] = nullptr;

// Initialize registers
powerUpInit();
}

void CPU::powerUpInit() {
void CPU::powerUpInit(Memory &memory) {
// Ref: https://gbdev.io/pandocs/#power-up-sequence

// AF -> 0x01b0
Expand All @@ -104,53 +99,53 @@ void CPU::powerUpInit() {

instrContext.stackPointer = 0xFFFE;

memory->write8BitsTo(0xff05, 0x00);
memory->write8BitsTo(0xff06, 0x00);
memory->write8BitsTo(0xff07, 0x00);
memory->write8BitsTo(0xff10, 0x80);
memory->write8BitsTo(0xff11, 0xbf);
memory->write8BitsTo(0xff12, 0xf3);
memory->write8BitsTo(0xff14, 0xbf);
memory->write8BitsTo(0xff16, 0x3f);
memory->write8BitsTo(0xff17, 0x00);
memory->write8BitsTo(0xff19, 0xbf);
memory->write8BitsTo(0xff1a, 0x7f);
memory->write8BitsTo(0xff1b, 0xff);
memory->write8BitsTo(0xff1c, 0x9f);
memory->write8BitsTo(0xff1e, 0xbf);
memory->write8BitsTo(0xff20, 0xff);
memory->write8BitsTo(0xff21, 0x00);
memory->write8BitsTo(0xff22, 0x00);
memory->write8BitsTo(0xff23, 0xbf);
memory->write8BitsTo(0xff24, 0x77);
memory->write8BitsTo(0xff25, 0xf3);
memory.write8BitsTo(0xff05, 0x00);
memory.write8BitsTo(0xff06, 0x00);
memory.write8BitsTo(0xff07, 0x00);
memory.write8BitsTo(0xff10, 0x80);
memory.write8BitsTo(0xff11, 0xbf);
memory.write8BitsTo(0xff12, 0xf3);
memory.write8BitsTo(0xff14, 0xbf);
memory.write8BitsTo(0xff16, 0x3f);
memory.write8BitsTo(0xff17, 0x00);
memory.write8BitsTo(0xff19, 0xbf);
memory.write8BitsTo(0xff1a, 0x7f);
memory.write8BitsTo(0xff1b, 0xff);
memory.write8BitsTo(0xff1c, 0x9f);
memory.write8BitsTo(0xff1e, 0xbf);
memory.write8BitsTo(0xff20, 0xff);
memory.write8BitsTo(0xff21, 0x00);
memory.write8BitsTo(0xff22, 0x00);
memory.write8BitsTo(0xff23, 0xbf);
memory.write8BitsTo(0xff24, 0x77);
memory.write8BitsTo(0xff25, 0xf3);
if (gbType == GameBoyDMG) {
memory->write8BitsTo(0xff26, 0xf1);
memory.write8BitsTo(0xff26, 0xf1);
} else {
// TODO: This is for SGB, does this also apply for CGB?
memory->write8BitsTo(0xff26, 0xf0);
memory.write8BitsTo(0xff26, 0xf0);
}
memory->write8BitsTo(0xff40, 0x91);
memory->write8BitsTo(0xff42, 0x00);
memory->write8BitsTo(0xff43, 0x00);
memory->write8BitsTo(0xff45, 0x00);
memory->write8BitsTo(0xff47, 0xfc);
memory->write8BitsTo(0xff48, 0xff);
memory->write8BitsTo(0xff49, 0xff);
memory->write8BitsTo(0xff4a, 0x00);
memory->write8BitsTo(0xff4b, 0x00);
memory->write8BitsTo(0xffff, 0x00);
memory.write8BitsTo(0xff40, 0x91);
memory.write8BitsTo(0xff42, 0x00);
memory.write8BitsTo(0xff43, 0x00);
memory.write8BitsTo(0xff45, 0x00);
memory.write8BitsTo(0xff47, 0xfc);
memory.write8BitsTo(0xff48, 0xff);
memory.write8BitsTo(0xff49, 0xff);
memory.write8BitsTo(0xff4a, 0x00);
memory.write8BitsTo(0xff4b, 0x00);
memory.write8BitsTo(0xffff, 0x00);

// Initialize Joypad
ioRegisters.updateJoypad();
}

ret_code CPU::doMachineCycle() {
ret_code CPU::doMachineCycle(Memory &memory) {
doJoypad();
auto result = doCycle();
auto result = doCycle(memory);

// TODO: Handle timer here or earlier?
doTimers(4);
doTimers(memory, 4);

// Due to a hardware quirk, enabling interrupts becomes active after the instruction following an EI,
// so we simulate this behaviour here
Expand All @@ -168,7 +163,7 @@ ret_code CPU::doMachineCycle() {
return result;
}

ret_code CPU::doCycle() {
ret_code CPU::doCycle(Memory &memory) {
if (instrContext.cpuState == CPUState::STOPPED) {
return FB_RET_SUCCESS | FB_RET_SKIPPED;
}
Expand All @@ -180,7 +175,7 @@ ret_code CPU::doCycle() {
ret_code result = FB_RET_SUCCESS;

if (instrContext.cpuState == CPUState::RUNNING) {
memory->doDMA(); // TODO: Implement delay of 2 clocks
memory.doDMA(); // TODO: Implement delay of 2 clocks

auto op = operands[operandIndex++];

Expand All @@ -189,7 +184,7 @@ ret_code CPU::doCycle() {
result |= FB_RET_INSTRUCTION_DONE;
}

if (!op(instrContext, *memory)) {
if (!op(instrContext, memory)) {
shouldFetch = true;
}
shouldDoInterrupts = shouldFetch;
Expand All @@ -201,26 +196,26 @@ ret_code CPU::doCycle() {
}
#endif

if (shouldDoInterrupts && (instrContext.cpuState == CPUState::RUNNING || instrContext.cpuState == CPUState::HALTED) && doInterrupts()) {
if (shouldDoInterrupts && (instrContext.cpuState == CPUState::RUNNING || instrContext.cpuState == CPUState::HALTED) && doInterrupts(memory)) {
// An interrupt has started (or IME=false && (IF & IE) != 0), so we set the state to RUNNING (again)
instrContext.cpuState = CPUState::RUNNING;
interruptServiced = true;
}

if (interruptServiced || (shouldFetch && instrContext.cpuState == CPUState::RUNNING)) { // TODO: Can this be simplified to just instrContext.cpuState == CPUState::RUNNING ?
result |= doFetchAndDecode();
result |= doFetchAndDecode(memory);
operandIndex = 0;
return result;
}

return result;
}

ret_code CPU::doFetchAndDecode() {
ret_code CPU::doFetchAndDecode(Memory &memory) {
if (!instrContext.haltBugRequested) {
instrContext.instr = memory->read8BitsAt(instrContext.progCounter++);
instrContext.instr = memory.read8BitsAt(instrContext.progCounter++);
} else {
instrContext.instr = memory->read8BitsAt(instrContext.progCounter);
instrContext.instr = memory.read8BitsAt(instrContext.progCounter);
instrContext.haltBugRequested = false;
}

Expand Down Expand Up @@ -262,13 +257,13 @@ void CPU::doJoypad() {
joypadWasNotPressed = isNotPressed;
}

bool CPU::doInterrupts() {
bool CPU::doInterrupts(Memory &memory) {
if (instrContext.cpuState == CPUState::STOPPED) {
return false;
}
u8 &_if = ioRegisters.getIF();
_if %= 0x1fu;
u8 _ie = memory->read8BitsAt(FB_REG_IE) & 0x1fu;
u8 _ie = memory.read8BitsAt(FB_REG_IE) & 0x1fu;
u8 _intr = _if & _ie & 0x1f;
if (!_intr) {
return false;
Expand All @@ -286,7 +281,7 @@ bool CPU::doInterrupts() {
memory_address addr = getInterruptStartAddress(interruptType);
instrContext.interruptMasterEnable = IMEState::DISABLED;
// TODO: do 2 NOP cycles (when implementing cycle accuracy)
instrContext.push16Bits(*memory, instrContext.progCounter);
instrContext.push16Bits(memory, instrContext.progCounter);

#ifdef FB_DEBUG_WRITE_EXECUTION_LOG
FunkyBoy::Debug::writeInterruptToLog(addr, file);
Expand Down Expand Up @@ -339,32 +334,32 @@ bool doTimerObscureCheck(u8 clocks, u16 sysCounter, u8 tac) {
return false;
}

void CPU::doTimers(u8 clocks) {
void CPU::doTimers(Memory &memory, u8 clocks) {
u16 sysCounter = ioRegisters.getSysCounter();
ioRegisters.setSysCounter(sysCounter + clocks); // TODO: Move to end of this function?
if (timerOverflowingCycles != -1) {
timerOverflowingCycles -= clocks;
if (timerOverflowingCycles <= 0) {
//fprintf(stdout, "# Request Timer interrupt\n");
memory->write8BitsTo(FB_REG_TIMA, memory->read8BitsAt(FB_REG_TMA));
memory.write8BitsTo(FB_REG_TIMA, memory.read8BitsAt(FB_REG_TMA));
requestInterrupt(InterruptType::TIMER);
timerOverflowingCycles = -1;
}
}
u8 tac = memory->read8BitsAt(FB_REG_TAC);
u8 tac = memory.read8BitsAt(FB_REG_TAC);
bool comp1 = (tac & 0b100u) != 0;
comp1 &= doTimerObscureCheck(clocks, sysCounter, tac);
// Falling edge detector
if (delayedTIMAIncrease && !comp1) {
u8 tima = memory->read8BitsAt(FB_REG_TIMA);
u8 tima = memory.read8BitsAt(FB_REG_TIMA);
if (tima == 0xff) {
//fprintf(stdout, "# TIMA has overflown\n");
// Delay TIMA load by 1 m-cycle
timerOverflowingCycles = 4;
// In the meantime, set TIMA to 0
memory->write8BitsTo(FB_REG_TIMA, 0x00);
memory.write8BitsTo(FB_REG_TIMA, 0x00);
} else if (timerOverflowingCycles == -1) { // TIME has to be 0 for one full m-cycle, so we do not increase here in that case
memory->write8BitsTo(FB_REG_TIMA, tima + 1);
memory.write8BitsTo(FB_REG_TIMA, tima + 1);
}
}
delayedTIMAIncrease = comp1;
Expand Down
17 changes: 8 additions & 9 deletions core/source/emulator/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ namespace FunkyBoy {
class CPU {
private:
io_registers ioRegisters;
MemoryPtr memory;
const GameBoyType gbType;

#ifdef FB_DEBUG_WRITE_EXECUTION_LOG
Expand All @@ -63,14 +62,12 @@ namespace FunkyBoy {

bool joypadWasNotPressed;

void powerUpInit();

ret_code doCycle();
ret_code doFetchAndDecode();
ret_code doCycle(Memory &memory);
ret_code doFetchAndDecode(Memory &memory);

void doJoypad();
bool doInterrupts();
void doTimers(u8 clocks);
bool doInterrupts(Memory &memory);
void doTimers(Memory &memory, u8 clocks);

test_public:

Expand All @@ -95,12 +92,14 @@ namespace FunkyBoy {
u8 *regA;

public:
CPU(GameBoyType gbType, MemoryPtr memory, const io_registers& ioRegisters);
CPU(GameBoyType gbType, const io_registers& ioRegisters);

void powerUpInit(Memory &memory);

void setProgramCounter(u16 offset);
void requestInterrupt(InterruptType type);

ret_code doMachineCycle();
ret_code doMachineCycle(Memory &memory);
};

typedef std::shared_ptr<CPU> CPUPtr;
Expand Down
6 changes: 4 additions & 2 deletions core/source/emulator/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ Emulator::Emulator(GameBoyType gbType, const Controller::ControllersPtr& control
, ioRegisters(controllers)
, ppuMemory()
, memory(new Memory(cartridge, controllers, ioRegisters, ppuMemory))
, cpu(std::make_shared<CPU>(gbType, memory, ioRegisters))
, cpu(std::make_shared<CPU>(gbType, ioRegisters))
, ppu(cpu, controllers, ioRegisters, ppuMemory)
{
// Initialize registers
cpu->powerUpInit(*memory);
}

Emulator::Emulator(FunkyBoy::GameBoyType gbType): Emulator(
Expand Down Expand Up @@ -86,7 +88,7 @@ void Emulator::writeCartridgeRam(std::ostream &stream) {
}

ret_code Emulator::doTick() {
auto result = cpu->doMachineCycle();
auto result = cpu->doMachineCycle(*memory);
if (!result) {
return 0;
}
Expand Down

0 comments on commit fad90f9

Please sign in to comment.