mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-12-23 15:29:24 +00:00
Ensure LR35902 fuse tests run successfully to completion.
Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
parent
cae34d61d1
commit
129286f1a7
@ -4,7 +4,7 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "Memory.h"
|
||||
#include <Register.h>
|
||||
|
||||
namespace Fuse {
|
||||
class RegisterState {
|
||||
|
@ -5,18 +5,18 @@
|
||||
Fuse::TestRunner::TestRunner(const Test& test, const ExpectedTestResult& expected)
|
||||
: m_test(test),
|
||||
m_expected(expected),
|
||||
m_cpu(m_bus),
|
||||
m_ram(0x10000),
|
||||
m_cpu(*this),
|
||||
m_failed(false),
|
||||
m_unimplemented(false) {
|
||||
m_bus.clear();
|
||||
m_cpu.initialise();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void Fuse::TestRunner::initialise() {
|
||||
m_bus.disableBootRom();
|
||||
m_bus.disableGameRom();
|
||||
disableBootRom();
|
||||
disableGameRom();
|
||||
initialiseRegisters();
|
||||
initialiseMemory();
|
||||
m_cpu.powerOn();
|
||||
@ -41,7 +41,7 @@ void Fuse::TestRunner::initialiseMemory() {
|
||||
auto address = memoryDatum.address;
|
||||
auto bytes = memoryDatum.bytes;
|
||||
for (int i = 0; i < bytes.size(); ++i)
|
||||
m_bus.poke(address + i, bytes[i]);
|
||||
poke(address + i, bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,9 +55,9 @@ void Fuse::TestRunner::check() {
|
||||
void Fuse::TestRunner::dumpDifference(const std::string& description, uint8_t actual, uint8_t expected) const {
|
||||
std::cerr
|
||||
<< "**** " << description << ", Expected: "
|
||||
<< EightBit::Disassembler::hex(expected)
|
||||
<< EightBit::GameBoy::Disassembler::hex(expected)
|
||||
<< ", Got: "
|
||||
<< EightBit::Disassembler::hex(actual)
|
||||
<< EightBit::GameBoy::Disassembler::hex(actual)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
@ -111,9 +111,9 @@ void Fuse::TestRunner::checkregisters() {
|
||||
if (expectedF != gotF) {
|
||||
std::cerr
|
||||
<< "**** F, Expected: "
|
||||
<< EightBit::Disassembler::flags(expectedF)
|
||||
<< EightBit::GameBoy::Disassembler::flags(expectedF)
|
||||
<< ", Got: "
|
||||
<< EightBit::Disassembler::flags(gotF)
|
||||
<< EightBit::GameBoy::Disassembler::flags(gotF)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ void Fuse::TestRunner::checkMemory() {
|
||||
for (int i = 0; i < bytes.size(); ++i) {
|
||||
auto expected = bytes[i];
|
||||
uint16_t address = memoryDatum.address + i;
|
||||
auto actual = m_cpu.getMemory().peek(address);
|
||||
auto actual = m_cpu.BUS().peek(address);
|
||||
if (expected != actual) {
|
||||
m_failed = true;
|
||||
if (first) {
|
||||
@ -168,9 +168,9 @@ void Fuse::TestRunner::checkMemory() {
|
||||
}
|
||||
std::cerr
|
||||
<< "**** Difference: "
|
||||
<< "Address: " << EightBit::Disassembler::hex(address)
|
||||
<< " Expected: " << EightBit::Disassembler::hex(expected)
|
||||
<< " Actual: " << EightBit::Disassembler::hex(actual)
|
||||
<< "Address: " << EightBit::GameBoy::Disassembler::hex(address)
|
||||
<< " Expected: " << EightBit::GameBoy::Disassembler::hex(expected)
|
||||
<< " Actual: " << EightBit::GameBoy::Disassembler::hex(actual)
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <Bus.h>
|
||||
#include <LR35902.h>
|
||||
|
||||
#include "FuseTest.h"
|
||||
#include "FuseExpectedTestResult.h"
|
||||
|
||||
#include "Bus.h"
|
||||
#include "LR35902.h"
|
||||
#include <LR35902.h>
|
||||
#include <GameBoyBus.h>
|
||||
#include <Ram.h>
|
||||
|
||||
namespace Fuse {
|
||||
class TestRunner {
|
||||
class TestRunner : public EightBit::GameBoy::Bus {
|
||||
private:
|
||||
const Test& m_test;
|
||||
const ExpectedTestResult& m_expected;
|
||||
@ -15,8 +19,8 @@ namespace Fuse {
|
||||
bool m_failed;
|
||||
bool m_unimplemented;
|
||||
|
||||
EightBit::Bus m_bus;
|
||||
EightBit::LR35902 m_cpu;
|
||||
EightBit::Ram m_ram;
|
||||
EightBit::GameBoy::LR35902 m_cpu;
|
||||
|
||||
void initialise();
|
||||
void initialiseRegisters();
|
||||
@ -32,6 +36,12 @@ namespace Fuse {
|
||||
const std::string& lowDescription,
|
||||
EightBit::register16_t actual, EightBit::register16_t expected) const;
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address, bool& rom) {
|
||||
rom = false;
|
||||
return m_ram.reference(address);
|
||||
}
|
||||
|
||||
public:
|
||||
TestRunner(const Test& test, const ExpectedTestResult& expected);
|
||||
|
||||
|
@ -4,24 +4,26 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace EightBit {
|
||||
class AbstractColourPalette {
|
||||
public:
|
||||
enum {
|
||||
Off,
|
||||
Light,
|
||||
Medium,
|
||||
Dark
|
||||
namespace GameBoy {
|
||||
class AbstractColourPalette {
|
||||
public:
|
||||
enum {
|
||||
Off,
|
||||
Light,
|
||||
Medium,
|
||||
Dark
|
||||
};
|
||||
|
||||
AbstractColourPalette::AbstractColourPalette()
|
||||
: m_colours(4) {
|
||||
}
|
||||
|
||||
uint32_t getColour(size_t index) const {
|
||||
return m_colours[index];
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint32_t> m_colours;
|
||||
};
|
||||
|
||||
AbstractColourPalette::AbstractColourPalette()
|
||||
: m_colours(4) {
|
||||
}
|
||||
|
||||
uint32_t getColour(size_t index) const {
|
||||
return m_colours[index];
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint32_t> m_colours;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Memory.h>
|
||||
#include <Processor.h>
|
||||
|
||||
namespace EightBit {
|
||||
class Bus : public Memory {
|
||||
public:
|
||||
|
||||
enum CartridgeType {
|
||||
ROM = 0,
|
||||
ROM_MBC1 = 1,
|
||||
ROM_MBC1_RAM = 2,
|
||||
ROM_MBC1_RAM_BATTERY = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
TotalLineCount = 154,
|
||||
RomPageSize = 0x4000
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
BASE = 0xFF00,
|
||||
|
||||
// Port/Mode Registers
|
||||
P1 = 0x0,
|
||||
SB = 0x1,
|
||||
SC = 0x2,
|
||||
DIV = 0x4,
|
||||
TIMA = 0x5,
|
||||
TMA = 0x6,
|
||||
TAC = 0x7,
|
||||
|
||||
// Interrupt Flags
|
||||
IF = 0xF,
|
||||
IE = 0xFF,
|
||||
|
||||
// LCD Display Registers
|
||||
LCDC = 0x40,
|
||||
STAT = 0x41,
|
||||
SCY = 0x42,
|
||||
SCX = 0x43,
|
||||
LY = 0x44,
|
||||
LYC = 0x45,
|
||||
DMA = 0x46,
|
||||
BGP = 0x47,
|
||||
OBP0 = 0x48,
|
||||
OBP1 = 0x49,
|
||||
WY = 0x4A,
|
||||
WX = 0x4B,
|
||||
|
||||
// Sound Registers
|
||||
NR10 = 0x10,
|
||||
NR11 = 0x11,
|
||||
NR12 = 0x12,
|
||||
NR13 = 0x13,
|
||||
NR14 = 0x14,
|
||||
NR21 = 0x16,
|
||||
NR22 = 0x17,
|
||||
NR23 = 0x18,
|
||||
NR24 = 0x19,
|
||||
NR30 = 0x1A,
|
||||
NR31 = 0x1B,
|
||||
NR32 = 0x1C,
|
||||
NR33 = 0x1D,
|
||||
NR34 = 0x1E,
|
||||
NR41 = 0x20,
|
||||
NR42 = 0x21,
|
||||
NR43 = 0x22,
|
||||
NR44 = 0x23,
|
||||
NR50 = 0x24,
|
||||
NR51 = 0x25,
|
||||
NR52 = 0x26,
|
||||
|
||||
WPRAM_START = 0x30,
|
||||
WPRAM_END = 0x3F,
|
||||
|
||||
// Boot rom control
|
||||
BOOT_DISABLE = 0x50,
|
||||
};
|
||||
|
||||
// IF and IE flags
|
||||
enum Interrupts {
|
||||
VerticalBlank = Processor::Bit0, // VBLANK
|
||||
DisplayControlStatus = Processor::Bit1, // LCDC Status
|
||||
TimerOverflow = Processor::Bit2, // Timer Overflow
|
||||
SerialTransfer = Processor::Bit3, // Serial Transfer
|
||||
Keypad = Processor::Bit3 // Hi-Lo of P10-P13
|
||||
};
|
||||
|
||||
enum LcdcControl {
|
||||
DisplayBackground = Processor::Bit0,
|
||||
ObjectEnable = Processor::Bit1,
|
||||
ObjectBlockCompositionSelection = Processor::Bit2,
|
||||
BackgroundCodeAreaSelection = Processor::Bit3,
|
||||
BackgroundCharacterDataSelection = Processor::Bit4,
|
||||
WindowEnable = Processor::Bit5,
|
||||
WindowCodeAreaSelection = Processor::Bit6,
|
||||
LcdEnable = Processor::Bit7
|
||||
};
|
||||
|
||||
enum LcdStatusMode {
|
||||
CpuAccessAllowed = 0b00,
|
||||
VerticalBlankingPeriod = 0b01,
|
||||
SearchingOamRam = 0b10,
|
||||
TransferringDataToLcd = 0b11
|
||||
};
|
||||
|
||||
Bus();
|
||||
|
||||
void reset();
|
||||
|
||||
virtual void clear() override;
|
||||
|
||||
void triggerInterrupt(int cause) {
|
||||
pokeRegister(IF, peekRegister(IF) | cause);
|
||||
}
|
||||
|
||||
void writeRegister(int offset, uint8_t content) {
|
||||
Memory::write(BASE + offset, content);
|
||||
}
|
||||
|
||||
void pokeRegister(int offset, uint8_t content) {
|
||||
poke(BASE + offset, content);
|
||||
}
|
||||
|
||||
uint8_t readRegister(int offset) {
|
||||
return Memory::read(BASE + offset);
|
||||
}
|
||||
|
||||
uint8_t peekRegister(int offset) {
|
||||
return peek(BASE + offset);
|
||||
}
|
||||
|
||||
void checkTimers(int cycles);
|
||||
|
||||
int timerClockTicks() {
|
||||
switch (timerClock()) {
|
||||
case 0b00:
|
||||
return 1024; // 4.096 Khz
|
||||
case 0b01:
|
||||
return 16; // 262.144 Khz
|
||||
case 0b10:
|
||||
return 64; // 65.536 Khz
|
||||
case 0b11:
|
||||
return 256; // 16.384 Khz
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
throw std::domain_error("Invalid timer clock specification");
|
||||
}
|
||||
|
||||
int timerClock() {
|
||||
return peekRegister(TAC) & Processor::Mask2;
|
||||
}
|
||||
|
||||
bool timerEnabled() {
|
||||
return !timerDisabled();
|
||||
}
|
||||
|
||||
bool timerDisabled() {
|
||||
return (peekRegister(TAC) & Processor::Bit2) == 0;
|
||||
}
|
||||
|
||||
void incrementDIV(int cycles) {
|
||||
m_divCounter.word += cycles;
|
||||
pokeRegister(DIV, m_divCounter.high);
|
||||
}
|
||||
|
||||
void incrementTIMA() {
|
||||
uint16_t updated = peekRegister(TIMA) + 1;
|
||||
if (updated & Processor::Bit8) {
|
||||
triggerInterrupt(TimerOverflow);
|
||||
updated = peekRegister(TMA);
|
||||
}
|
||||
pokeRegister(TIMA, updated & Processor::Mask8);
|
||||
}
|
||||
|
||||
void incrementLY() {
|
||||
pokeRegister(LY, (peekRegister(LY) + 1) % TotalLineCount);
|
||||
}
|
||||
|
||||
void resetLY() {
|
||||
pokeRegister(LY, 0);
|
||||
}
|
||||
|
||||
void disableBootRom() { m_disableBootRom = true; }
|
||||
void enableBootRom() { m_disableBootRom = false; }
|
||||
|
||||
void disableGameRom() { m_disableGameRom = true; }
|
||||
void enableGameRom() { m_disableGameRom = false; }
|
||||
|
||||
bool bootRomDisabled() const { return m_disableBootRom; }
|
||||
bool bootRomEnabled() const { return !bootRomDisabled(); }
|
||||
|
||||
bool gameRomDisabled() const { return m_disableGameRom; }
|
||||
bool gameRomEnabled() const { return !gameRomDisabled(); }
|
||||
|
||||
void loadBootRom(const std::string& path);
|
||||
void loadGameRom(const std::string& path);
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> m_bootRom;
|
||||
std::vector<uint8_t> m_gameRom;
|
||||
|
||||
bool m_disableBootRom;
|
||||
bool m_disableGameRom;
|
||||
|
||||
bool m_rom;
|
||||
bool m_banked;
|
||||
bool m_ram;
|
||||
bool m_battery;
|
||||
|
||||
bool m_higherRomBank;
|
||||
bool m_ramBankSwitching;
|
||||
|
||||
int m_romBank;
|
||||
int m_ramBank;
|
||||
|
||||
register16_t m_divCounter;
|
||||
int m_timerCounter;
|
||||
int m_timerRate;
|
||||
|
||||
void Bus_WrittenByte(const AddressEventArgs& e);
|
||||
|
||||
void checkTimer(int cycles);
|
||||
|
||||
void validateCartridgeType();
|
||||
|
||||
virtual uint8_t& reference(uint16_t address, bool& rom);
|
||||
};
|
||||
}
|
@ -6,36 +6,38 @@
|
||||
#include "Bus.h"
|
||||
|
||||
namespace EightBit {
|
||||
class CharacterDefinition {
|
||||
public:
|
||||
CharacterDefinition() {}
|
||||
namespace GameBoy {
|
||||
class CharacterDefinition {
|
||||
public:
|
||||
CharacterDefinition() {}
|
||||
|
||||
CharacterDefinition(Bus& bus, uint16_t address) {
|
||||
CharacterDefinition(Bus& bus, uint16_t address) {
|
||||
|
||||
for (auto row = 0; row < 8; ++row) {
|
||||
for (auto row = 0; row < 8; ++row) {
|
||||
|
||||
auto planeAddress = address + row * 2;
|
||||
auto planeAddress = address + row * 2;
|
||||
|
||||
auto planeLow = bus.peek(planeAddress);
|
||||
auto planeHigh = bus.peek(planeAddress + 1);
|
||||
auto planeLow = bus.peek(planeAddress);
|
||||
auto planeHigh = bus.peek(planeAddress + 1);
|
||||
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
|
||||
auto mask = 1 << bit;
|
||||
auto mask = 1 << bit;
|
||||
|
||||
auto bitLow = planeLow & mask ? 1 : 0;
|
||||
auto bitHigh = planeHigh & mask ? 0b10 : 0;
|
||||
auto bitLow = planeLow & mask ? 1 : 0;
|
||||
auto bitHigh = planeHigh & mask ? 0b10 : 0;
|
||||
|
||||
auto colour = bitHigh | bitLow;
|
||||
auto colour = bitHigh | bitLow;
|
||||
|
||||
m_definition[row * 8 + (7 - bit)] = colour;
|
||||
m_definition[row * 8 + (7 - bit)] = colour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<int, 8 * 8>& get() const { return m_definition; }
|
||||
const std::array<int, 8 * 8>& get() const { return m_definition; }
|
||||
|
||||
private:
|
||||
std::array<int, 8 * 8> m_definition;
|
||||
};
|
||||
private:
|
||||
std::array<int, 8 * 8> m_definition;
|
||||
};
|
||||
}
|
||||
}
|
@ -5,61 +5,64 @@
|
||||
|
||||
namespace EightBit {
|
||||
|
||||
class LR35902;
|
||||
namespace GameBoy {
|
||||
|
||||
class Disassembler {
|
||||
public:
|
||||
Disassembler();
|
||||
class LR35902;
|
||||
|
||||
static std::string state(LR35902& cpu);
|
||||
std::string disassemble(LR35902& cpu);
|
||||
class Disassembler {
|
||||
public:
|
||||
Disassembler();
|
||||
|
||||
static std::string flag(uint8_t value, int flag, const std::string& represents);
|
||||
static std::string flags(uint8_t value);
|
||||
static std::string hex(uint8_t value);
|
||||
static std::string hex(uint16_t value);
|
||||
static std::string binary(uint8_t value);
|
||||
static std::string decimal(uint8_t value);
|
||||
static std::string io(uint8_t value);
|
||||
static std::string state(LR35902& cpu);
|
||||
std::string disassemble(LR35902& cpu);
|
||||
|
||||
static std::string invalid(uint8_t value);
|
||||
static std::string flag(uint8_t value, int flag, const std::string& represents);
|
||||
static std::string flags(uint8_t value);
|
||||
static std::string hex(uint8_t value);
|
||||
static std::string hex(uint16_t value);
|
||||
static std::string binary(uint8_t value);
|
||||
static std::string decimal(uint8_t value);
|
||||
static std::string io(uint8_t value);
|
||||
|
||||
private:
|
||||
enum IoRegister {
|
||||
Abbreviated, // FF00 + dd
|
||||
Absolute, // FFdd
|
||||
Register, // C
|
||||
Unused, // Unused!
|
||||
static std::string invalid(uint8_t value);
|
||||
|
||||
private:
|
||||
enum IoRegister {
|
||||
Abbreviated, // FF00 + dd
|
||||
Absolute, // FFdd
|
||||
Register, // C
|
||||
Unused, // Unused!
|
||||
};
|
||||
|
||||
mutable boost::format m_formatter;
|
||||
bool m_prefixCB;
|
||||
|
||||
void disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc);
|
||||
|
||||
void disassembleCB(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
std::string& specification,
|
||||
int& dumpCount,
|
||||
int x, int y, int z,
|
||||
int p, int q);
|
||||
|
||||
void disassembleOther(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
std::string& specification,
|
||||
int& dumpCount,
|
||||
IoRegister& ioRegister,
|
||||
int x, int y, int z,
|
||||
int p, int q);
|
||||
|
||||
std::string RP(int rp) const;
|
||||
std::string RP2(int rp) const;
|
||||
std::string R(int r) const;
|
||||
static std::string cc(int flag);
|
||||
static std::string alu(int which);
|
||||
};
|
||||
|
||||
mutable boost::format m_formatter;
|
||||
bool m_prefixCB;
|
||||
|
||||
void disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc);
|
||||
|
||||
void disassembleCB(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
std::string& specification,
|
||||
int& dumpCount,
|
||||
int x, int y, int z,
|
||||
int p, int q);
|
||||
|
||||
void disassembleOther(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
std::string& specification,
|
||||
int& dumpCount,
|
||||
IoRegister& ioRegister,
|
||||
int x, int y, int z,
|
||||
int p, int q);
|
||||
|
||||
std::string RP(int rp) const;
|
||||
std::string RP2(int rp) const;
|
||||
std::string R(int r) const;
|
||||
static std::string cc(int flag);
|
||||
static std::string alu(int which);
|
||||
};
|
||||
}
|
||||
}
|
@ -3,31 +3,33 @@
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Bus.h"
|
||||
#include "GameBoyBus.h"
|
||||
#include "AbstractColourPalette.h"
|
||||
|
||||
namespace EightBit {
|
||||
class Display {
|
||||
public:
|
||||
enum {
|
||||
BufferWidth = 256,
|
||||
BufferHeight = 256,
|
||||
BufferCharacterWidth = BufferWidth / 8,
|
||||
BufferCharacterHeight = BufferHeight / 8,
|
||||
RasterWidth = 160,
|
||||
RasterHeight = 144,
|
||||
namespace GameBoy {
|
||||
class Display {
|
||||
public:
|
||||
enum {
|
||||
BufferWidth = 256,
|
||||
BufferHeight = 256,
|
||||
BufferCharacterWidth = BufferWidth / 8,
|
||||
BufferCharacterHeight = BufferHeight / 8,
|
||||
RasterWidth = 160,
|
||||
RasterHeight = 144,
|
||||
};
|
||||
|
||||
Display(const AbstractColourPalette* colours, Bus& bus);
|
||||
|
||||
const std::vector<uint32_t>& pixels() const;
|
||||
|
||||
void initialise();
|
||||
void render();
|
||||
|
||||
private:
|
||||
std::vector<uint32_t> m_pixels;
|
||||
Bus& m_bus;
|
||||
const AbstractColourPalette* m_colours;
|
||||
};
|
||||
|
||||
Display(const AbstractColourPalette* colours, Bus& bus);
|
||||
|
||||
const std::vector<uint32_t>& pixels() const;
|
||||
|
||||
void initialise();
|
||||
void render();
|
||||
|
||||
private:
|
||||
std::vector<uint32_t> m_pixels;
|
||||
Bus& m_bus;
|
||||
const AbstractColourPalette* m_colours;
|
||||
};
|
||||
}
|
||||
}
|
266
LR35902/inc/GameBoyBus.h
Normal file
266
LR35902/inc/GameBoyBus.h
Normal file
@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
|
||||
#include <Rom.h>
|
||||
#include <Ram.h>
|
||||
#include <Bus.h>
|
||||
#include <Processor.h>
|
||||
|
||||
namespace EightBit {
|
||||
namespace GameBoy {
|
||||
class Bus : public EightBit::Bus {
|
||||
public:
|
||||
|
||||
enum CartridgeType {
|
||||
ROM = 0,
|
||||
ROM_MBC1 = 1,
|
||||
ROM_MBC1_RAM = 2,
|
||||
ROM_MBC1_RAM_BATTERY = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
TotalLineCount = 154,
|
||||
RomPageSize = 0x4000
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
BASE = 0xFF00,
|
||||
|
||||
// Port/Mode Registers
|
||||
P1 = 0x0,
|
||||
SB = 0x1,
|
||||
SC = 0x2,
|
||||
DIV = 0x4,
|
||||
TIMA = 0x5,
|
||||
TMA = 0x6,
|
||||
TAC = 0x7,
|
||||
|
||||
// Interrupt Flags
|
||||
IF = 0xF,
|
||||
IE = 0xFF,
|
||||
|
||||
// LCD Display Registers
|
||||
LCDC = 0x40,
|
||||
STAT = 0x41,
|
||||
SCY = 0x42,
|
||||
SCX = 0x43,
|
||||
LY = 0x44,
|
||||
LYC = 0x45,
|
||||
DMA = 0x46,
|
||||
BGP = 0x47,
|
||||
OBP0 = 0x48,
|
||||
OBP1 = 0x49,
|
||||
WY = 0x4A,
|
||||
WX = 0x4B,
|
||||
|
||||
// Sound Registers
|
||||
NR10 = 0x10,
|
||||
NR11 = 0x11,
|
||||
NR12 = 0x12,
|
||||
NR13 = 0x13,
|
||||
NR14 = 0x14,
|
||||
NR21 = 0x16,
|
||||
NR22 = 0x17,
|
||||
NR23 = 0x18,
|
||||
NR24 = 0x19,
|
||||
NR30 = 0x1A,
|
||||
NR31 = 0x1B,
|
||||
NR32 = 0x1C,
|
||||
NR33 = 0x1D,
|
||||
NR34 = 0x1E,
|
||||
NR41 = 0x20,
|
||||
NR42 = 0x21,
|
||||
NR43 = 0x22,
|
||||
NR44 = 0x23,
|
||||
NR50 = 0x24,
|
||||
NR51 = 0x25,
|
||||
NR52 = 0x26,
|
||||
|
||||
WPRAM_START = 0x30,
|
||||
WPRAM_END = 0x3F,
|
||||
|
||||
// Boot rom control
|
||||
BOOT_DISABLE = 0x50,
|
||||
};
|
||||
|
||||
// IF and IE flags
|
||||
enum Interrupts {
|
||||
VerticalBlank = Processor::Bit0, // VBLANK
|
||||
DisplayControlStatus = Processor::Bit1, // LCDC Status
|
||||
TimerOverflow = Processor::Bit2, // Timer Overflow
|
||||
SerialTransfer = Processor::Bit3, // Serial Transfer
|
||||
Keypad = Processor::Bit3 // Hi-Lo of P10-P13
|
||||
};
|
||||
|
||||
enum LcdcControl {
|
||||
DisplayBackground = Processor::Bit0,
|
||||
ObjectEnable = Processor::Bit1,
|
||||
ObjectBlockCompositionSelection = Processor::Bit2,
|
||||
BackgroundCodeAreaSelection = Processor::Bit3,
|
||||
BackgroundCharacterDataSelection = Processor::Bit4,
|
||||
WindowEnable = Processor::Bit5,
|
||||
WindowCodeAreaSelection = Processor::Bit6,
|
||||
LcdEnable = Processor::Bit7
|
||||
};
|
||||
|
||||
enum LcdStatusMode {
|
||||
CpuAccessAllowed = 0b00,
|
||||
VerticalBlankingPeriod = 0b01,
|
||||
SearchingOamRam = 0b10,
|
||||
TransferringDataToLcd = 0b11
|
||||
};
|
||||
|
||||
Bus();
|
||||
|
||||
void reset();
|
||||
|
||||
void triggerInterrupt(int cause) {
|
||||
pokeRegister(IF, peekRegister(IF) | cause);
|
||||
}
|
||||
|
||||
void writeRegister(int offset, uint8_t content) {
|
||||
write(BASE + offset, content);
|
||||
}
|
||||
|
||||
void pokeRegister(int offset, uint8_t content) {
|
||||
poke(BASE + offset, content);
|
||||
}
|
||||
|
||||
uint8_t readRegister(int offset) {
|
||||
return read(BASE + offset);
|
||||
}
|
||||
|
||||
uint8_t peekRegister(int offset) {
|
||||
return peek(BASE + offset);
|
||||
}
|
||||
|
||||
void checkTimers(int cycles);
|
||||
|
||||
int timerClockTicks() {
|
||||
switch (timerClock()) {
|
||||
case 0b00:
|
||||
return 1024; // 4.096 Khz
|
||||
case 0b01:
|
||||
return 16; // 262.144 Khz
|
||||
case 0b10:
|
||||
return 64; // 65.536 Khz
|
||||
case 0b11:
|
||||
return 256; // 16.384 Khz
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
throw std::domain_error("Invalid timer clock specification");
|
||||
}
|
||||
|
||||
int timerClock() {
|
||||
return peekRegister(TAC) & Processor::Mask2;
|
||||
}
|
||||
|
||||
bool timerEnabled() {
|
||||
return !timerDisabled();
|
||||
}
|
||||
|
||||
bool timerDisabled() {
|
||||
return (peekRegister(TAC) & Processor::Bit2) == 0;
|
||||
}
|
||||
|
||||
void incrementDIV(int cycles) {
|
||||
m_divCounter.word += cycles;
|
||||
pokeRegister(DIV, m_divCounter.high);
|
||||
}
|
||||
|
||||
void incrementTIMA() {
|
||||
uint16_t updated = peekRegister(TIMA) + 1;
|
||||
if (updated & Processor::Bit8) {
|
||||
triggerInterrupt(TimerOverflow);
|
||||
updated = peekRegister(TMA);
|
||||
}
|
||||
pokeRegister(TIMA, updated & Processor::Mask8);
|
||||
}
|
||||
|
||||
void incrementLY() {
|
||||
pokeRegister(LY, (peekRegister(LY) + 1) % TotalLineCount);
|
||||
}
|
||||
|
||||
void resetLY() {
|
||||
pokeRegister(LY, 0);
|
||||
}
|
||||
|
||||
void disableBootRom() { m_disableBootRom = true; }
|
||||
void enableBootRom() { m_disableBootRom = false; }
|
||||
|
||||
void disableGameRom() { m_disableGameRom = true; }
|
||||
void enableGameRom() { m_disableGameRom = false; }
|
||||
|
||||
bool bootRomDisabled() const { return m_disableBootRom; }
|
||||
bool bootRomEnabled() const { return !bootRomDisabled(); }
|
||||
|
||||
bool gameRomDisabled() const { return m_disableGameRom; }
|
||||
bool gameRomEnabled() const { return !gameRomDisabled(); }
|
||||
|
||||
void loadBootRom(const std::string& path);
|
||||
void loadGameRom(const std::string& path);
|
||||
|
||||
protected:
|
||||
virtual uint8_t& reference(uint16_t address, bool& rom) {
|
||||
|
||||
rom = true;
|
||||
if ((address < 0x100) && bootRomEnabled())
|
||||
return m_bootRom.reference(address);
|
||||
if ((address < 0x4000) && gameRomEnabled())
|
||||
return m_gameRomBanks[0].reference(address);
|
||||
if ((address < 0x8000) && gameRomEnabled())
|
||||
return m_gameRomBanks[m_romBank].reference(address - 0x4000);
|
||||
|
||||
rom = false;
|
||||
if (address < 0xa000)
|
||||
return m_videoRam.reference(address - 0x8000);
|
||||
if (address < 0xc000)
|
||||
return m_ramBanks[m_ramBank].reference(address - 0xa000);
|
||||
if (address < 0xe000)
|
||||
return m_lowInternalRam.reference(address - 0xc000);
|
||||
if (address < 0xfe00)
|
||||
return m_lowInternalRam.reference(address - 0xe000); // Low internal RAM mirror
|
||||
if (address < 0xff00)
|
||||
return m_oamRam.reference(address - 0xfe00);
|
||||
if (address < 0xff80)
|
||||
return m_ioPorts.reference(address - 0xff00);
|
||||
return m_highInternalRam.reference(address - 0xff80);
|
||||
}
|
||||
|
||||
private:
|
||||
Rom m_bootRom; // 0x0000 - 0x00ff
|
||||
std::vector<Rom> m_gameRomBanks; // 0x0000 - 0x3fff, 0x4000 - 0x7fff (switchable)
|
||||
Ram m_videoRam; // 0x8000 - 0x9fff
|
||||
std::vector<Ram> m_ramBanks; // 0xa000 - 0xbfff (switchable)
|
||||
Ram m_lowInternalRam; // 0xc000 - 0xdfff (mirrored at 0xe000)
|
||||
Ram m_oamRam; // 0xfe00 - 0xfe9f
|
||||
Ram m_ioPorts; // 0xff00 - 0xff7f
|
||||
Ram m_highInternalRam; // 0xff80 - 0xffff
|
||||
|
||||
bool m_disableBootRom;
|
||||
bool m_disableGameRom;
|
||||
|
||||
bool m_rom;
|
||||
bool m_banked;
|
||||
bool m_ram;
|
||||
bool m_battery;
|
||||
|
||||
bool m_higherRomBank;
|
||||
bool m_ramBankSwitching;
|
||||
|
||||
int m_romBank;
|
||||
int m_ramBank;
|
||||
|
||||
register16_t m_divCounter;
|
||||
int m_timerCounter;
|
||||
int m_timerRate;
|
||||
|
||||
void Bus_WrittenByte(const AddressEventArgs& e);
|
||||
|
||||
void checkTimer(int cycles);
|
||||
|
||||
void validateCartridgeType();
|
||||
};
|
||||
}
|
||||
}
|
@ -5,211 +5,213 @@
|
||||
#include <IntelProcessor.h>
|
||||
#include <Signal.h>
|
||||
|
||||
#include "Bus.h"
|
||||
#include "GameBoyBus.h"
|
||||
#include "Display.h"
|
||||
|
||||
namespace EightBit {
|
||||
class LR35902 : public IntelProcessor {
|
||||
public:
|
||||
enum StatusBits {
|
||||
ZF = Bit7,
|
||||
NF = Bit6,
|
||||
HC = Bit5,
|
||||
CF = Bit4,
|
||||
namespace GameBoy {
|
||||
class LR35902 : public IntelProcessor {
|
||||
public:
|
||||
enum StatusBits {
|
||||
ZF = Bit7,
|
||||
NF = Bit6,
|
||||
HC = Bit5,
|
||||
CF = Bit4,
|
||||
};
|
||||
|
||||
LR35902(Bus& memory);
|
||||
|
||||
Signal<LR35902> ExecutingInstruction;
|
||||
|
||||
virtual register16_t& AF() override {
|
||||
af.low &= 0xf0;
|
||||
return af;
|
||||
}
|
||||
|
||||
virtual register16_t& BC() override { return bc; }
|
||||
virtual register16_t& DE() override { return de; }
|
||||
virtual register16_t& HL() override { return hl; }
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
static int framesPerSecond() { return 60; }
|
||||
static int cyclesPerFrame() { return cyclesPerSecond() / framesPerSecond(); }
|
||||
|
||||
int runRasterLines();
|
||||
int runRasterLine();
|
||||
int runVerticalBlankLines();
|
||||
|
||||
int singleStep();
|
||||
|
||||
protected:
|
||||
int runRasterLines(int limit);
|
||||
virtual int execute(uint8_t opcode);
|
||||
int step();
|
||||
|
||||
private:
|
||||
Bus& m_bus;
|
||||
|
||||
register16_t af;
|
||||
register16_t bc;
|
||||
register16_t de;
|
||||
register16_t hl;
|
||||
|
||||
bool m_ime;
|
||||
bool m_stopped;
|
||||
|
||||
bool m_prefixCB;
|
||||
|
||||
static int cyclesPerSecond() { return 4 * 1024 * 1024; }
|
||||
static int cyclesPerLine() { return cyclesPerFrame() / Bus::TotalLineCount; }
|
||||
|
||||
bool& IME() { return m_ime; }
|
||||
|
||||
uint8_t R(int r, uint8_t& a) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
return B();
|
||||
case 1:
|
||||
return C();
|
||||
case 2:
|
||||
return D();
|
||||
case 3:
|
||||
return E();
|
||||
case 4:
|
||||
return H();
|
||||
case 5:
|
||||
return L();
|
||||
case 6:
|
||||
return getByte(HL());
|
||||
case 7:
|
||||
return a;
|
||||
}
|
||||
throw std::logic_error("Unhandled registry mechanism");
|
||||
}
|
||||
|
||||
void R(int r, uint8_t& a, uint8_t value) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
B() = value;
|
||||
break;
|
||||
case 1:
|
||||
C() = value;
|
||||
break;
|
||||
case 2:
|
||||
D() = value;
|
||||
break;
|
||||
case 3:
|
||||
E() = value;
|
||||
break;
|
||||
case 4:
|
||||
H() = value;
|
||||
break;
|
||||
case 5:
|
||||
L() = value;
|
||||
break;
|
||||
case 6:
|
||||
setByte(HL(), value);
|
||||
break;
|
||||
case 7:
|
||||
a = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
register16_t& RP(int rp) {
|
||||
__assume(rp < 4);
|
||||
__assume(rp >= 0);
|
||||
switch (rp) {
|
||||
case 0b00:
|
||||
return BC();
|
||||
case 0b01:
|
||||
return DE();
|
||||
case 0b10:
|
||||
return HL();
|
||||
case 0b11:
|
||||
return SP();
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
}
|
||||
|
||||
register16_t& RP2(int rp) {
|
||||
__assume(rp < 4);
|
||||
__assume(rp >= 0);
|
||||
switch (rp) {
|
||||
case 0b00:
|
||||
return BC();
|
||||
case 0b01:
|
||||
return DE();
|
||||
case 0b10:
|
||||
return HL();
|
||||
case 0b11:
|
||||
return AF();
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void adjustHalfCarryAdd(uint8_t& f, uint8_t before, uint8_t value, int calculation) {
|
||||
setFlag(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
||||
}
|
||||
|
||||
static void adjustHalfCarrySub(uint8_t& f, uint8_t before, uint8_t value, int calculation) {
|
||||
setFlag(f, HC, calculateHalfCarrySub(before, value, calculation));
|
||||
}
|
||||
|
||||
static void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
|
||||
|
||||
int interrupt(uint8_t value);
|
||||
|
||||
void executeCB(int x, int y, int z, int p, int q);
|
||||
void executeOther(int x, int y, int z, int p, int q);
|
||||
|
||||
static void increment(uint8_t& f, uint8_t& operand);
|
||||
static void decrement(uint8_t& f, uint8_t& operand);
|
||||
|
||||
void stop() { m_stopped = true; }
|
||||
void start() { m_stopped = false; }
|
||||
bool stopped() const { return m_stopped; }
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
void reti();
|
||||
|
||||
bool jrConditionalFlag(uint8_t& f, int flag);
|
||||
bool returnConditionalFlag(uint8_t& f, int flag);
|
||||
bool jumpConditionalFlag(uint8_t& f, int flag);
|
||||
bool callConditionalFlag(uint8_t& f, int flag);
|
||||
|
||||
void add(uint8_t& f, register16_t& operand, register16_t value);
|
||||
|
||||
static void add(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
|
||||
static void adc(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void sbc(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void andr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void xorr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void orr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void compare(uint8_t& f, uint8_t check, uint8_t value);
|
||||
|
||||
static uint8_t rlc(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rrc(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rl(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rr(uint8_t& f, uint8_t operand);
|
||||
static uint8_t sla(uint8_t& f, uint8_t operand);
|
||||
static uint8_t sra(uint8_t& f, uint8_t operand);
|
||||
static uint8_t srl(uint8_t& f, uint8_t operand);
|
||||
|
||||
static uint8_t bit(uint8_t& f, int n, uint8_t operand);
|
||||
static uint8_t res(int n, uint8_t operand);
|
||||
static uint8_t set(int n, uint8_t operand);
|
||||
|
||||
static void daa(uint8_t& a, uint8_t& f);
|
||||
|
||||
static void scf(uint8_t& a, uint8_t& f);
|
||||
static void ccf(uint8_t& a, uint8_t& f);
|
||||
static void cpl(uint8_t& a, uint8_t& f);
|
||||
|
||||
static uint8_t swap(uint8_t& f, uint8_t operand);
|
||||
};
|
||||
|
||||
LR35902(Bus& memory);
|
||||
|
||||
Signal<LR35902> ExecutingInstruction;
|
||||
|
||||
virtual register16_t& AF() override {
|
||||
af.low &= 0xf0;
|
||||
return af;
|
||||
}
|
||||
|
||||
virtual register16_t& BC() override { return bc; }
|
||||
virtual register16_t& DE() override { return de; }
|
||||
virtual register16_t& HL() override { return hl; }
|
||||
|
||||
virtual void reset() override;
|
||||
|
||||
static int framesPerSecond() { return 60; }
|
||||
static int cyclesPerFrame() { return cyclesPerSecond() / framesPerSecond(); }
|
||||
|
||||
int runRasterLines();
|
||||
int runRasterLine();
|
||||
int runVerticalBlankLines();
|
||||
|
||||
int singleStep();
|
||||
|
||||
protected:
|
||||
int runRasterLines(int limit);
|
||||
virtual int execute(uint8_t opcode);
|
||||
int step();
|
||||
|
||||
private:
|
||||
Bus& m_bus;
|
||||
|
||||
register16_t af;
|
||||
register16_t bc;
|
||||
register16_t de;
|
||||
register16_t hl;
|
||||
|
||||
bool m_ime;
|
||||
bool m_stopped;
|
||||
|
||||
bool m_prefixCB;
|
||||
|
||||
static int cyclesPerSecond() { return 4 * 1024 * 1024; }
|
||||
static int cyclesPerLine() { return cyclesPerFrame() / Bus::TotalLineCount; }
|
||||
|
||||
bool& IME() { return m_ime; }
|
||||
|
||||
uint8_t R(int r, uint8_t& a) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
return B();
|
||||
case 1:
|
||||
return C();
|
||||
case 2:
|
||||
return D();
|
||||
case 3:
|
||||
return E();
|
||||
case 4:
|
||||
return H();
|
||||
case 5:
|
||||
return L();
|
||||
case 6:
|
||||
return getByte(HL());
|
||||
case 7:
|
||||
return a;
|
||||
}
|
||||
throw std::logic_error("Unhandled registry mechanism");
|
||||
}
|
||||
|
||||
void R(int r, uint8_t& a, uint8_t value) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
B() = value;
|
||||
break;
|
||||
case 1:
|
||||
C() = value;
|
||||
break;
|
||||
case 2:
|
||||
D() = value;
|
||||
break;
|
||||
case 3:
|
||||
E() = value;
|
||||
break;
|
||||
case 4:
|
||||
H() = value;
|
||||
break;
|
||||
case 5:
|
||||
L() = value;
|
||||
break;
|
||||
case 6:
|
||||
setByte(HL(), value);
|
||||
break;
|
||||
case 7:
|
||||
a = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
register16_t& RP(int rp) {
|
||||
__assume(rp < 4);
|
||||
__assume(rp >= 0);
|
||||
switch (rp) {
|
||||
case 0b00:
|
||||
return BC();
|
||||
case 0b01:
|
||||
return DE();
|
||||
case 0b10:
|
||||
return HL();
|
||||
case 0b11:
|
||||
return SP();
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
}
|
||||
|
||||
register16_t& RP2(int rp) {
|
||||
__assume(rp < 4);
|
||||
__assume(rp >= 0);
|
||||
switch (rp) {
|
||||
case 0b00:
|
||||
return BC();
|
||||
case 0b01:
|
||||
return DE();
|
||||
case 0b10:
|
||||
return HL();
|
||||
case 0b11:
|
||||
return AF();
|
||||
default:
|
||||
__assume(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void adjustHalfCarryAdd(uint8_t& f, uint8_t before, uint8_t value, int calculation) {
|
||||
setFlag(f, HC, calculateHalfCarryAdd(before, value, calculation));
|
||||
}
|
||||
|
||||
static void adjustHalfCarrySub(uint8_t& f, uint8_t before, uint8_t value, int calculation) {
|
||||
setFlag(f, HC, calculateHalfCarrySub(before, value, calculation));
|
||||
}
|
||||
|
||||
static void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
|
||||
|
||||
int interrupt(uint8_t value);
|
||||
|
||||
void executeCB(int x, int y, int z, int p, int q);
|
||||
void executeOther(int x, int y, int z, int p, int q);
|
||||
|
||||
static void increment(uint8_t& f, uint8_t& operand);
|
||||
static void decrement(uint8_t& f, uint8_t& operand);
|
||||
|
||||
void stop() { m_stopped = true; }
|
||||
void start() { m_stopped = false; }
|
||||
bool stopped() const { return m_stopped; }
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
void reti();
|
||||
|
||||
bool jrConditionalFlag(uint8_t& f, int flag);
|
||||
bool returnConditionalFlag(uint8_t& f, int flag);
|
||||
bool jumpConditionalFlag(uint8_t& f, int flag);
|
||||
bool callConditionalFlag(uint8_t& f, int flag);
|
||||
|
||||
void add(uint8_t& f, register16_t& operand, register16_t value);
|
||||
|
||||
static void add(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
|
||||
static void adc(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void sbc(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void andr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void xorr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void orr(uint8_t& f, uint8_t& operand, uint8_t value);
|
||||
static void compare(uint8_t& f, uint8_t check, uint8_t value);
|
||||
|
||||
static uint8_t rlc(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rrc(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rl(uint8_t& f, uint8_t operand);
|
||||
static uint8_t rr(uint8_t& f, uint8_t operand);
|
||||
static uint8_t sla(uint8_t& f, uint8_t operand);
|
||||
static uint8_t sra(uint8_t& f, uint8_t operand);
|
||||
static uint8_t srl(uint8_t& f, uint8_t operand);
|
||||
|
||||
static uint8_t bit(uint8_t& f, int n, uint8_t operand);
|
||||
static uint8_t res(int n, uint8_t operand);
|
||||
static uint8_t set(int n, uint8_t operand);
|
||||
|
||||
static void daa(uint8_t& a, uint8_t& f);
|
||||
|
||||
static void scf(uint8_t& a, uint8_t& f);
|
||||
static void ccf(uint8_t& a, uint8_t& f);
|
||||
static void cpl(uint8_t& a, uint8_t& f);
|
||||
|
||||
static uint8_t swap(uint8_t& f, uint8_t operand);
|
||||
};
|
||||
}
|
||||
}
|
@ -6,25 +6,27 @@
|
||||
#include "Disassembler.h"
|
||||
|
||||
namespace EightBit {
|
||||
namespace GameBoy {
|
||||
|
||||
class LR35902;
|
||||
class LR35902;
|
||||
|
||||
class Profiler {
|
||||
public:
|
||||
Profiler(LR35902& cpu);
|
||||
class Profiler {
|
||||
public:
|
||||
Profiler(LR35902& cpu);
|
||||
|
||||
void add(uint16_t address, uint8_t instruction);
|
||||
void add(uint16_t address, uint8_t instruction);
|
||||
|
||||
void dump() const;
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
std::array<uint64_t, 0x100> m_instructions;
|
||||
std::array<uint64_t, 0x10000> m_addresses;
|
||||
LR35902& m_cpu;
|
||||
private:
|
||||
std::array<uint64_t, 0x100> m_instructions;
|
||||
std::array<uint64_t, 0x10000> m_addresses;
|
||||
LR35902& m_cpu;
|
||||
|
||||
Disassembler m_disassembler;
|
||||
Disassembler m_disassembler;
|
||||
|
||||
void dumpInstructionProfiles() const;
|
||||
void dumpAddressProfiles() const;
|
||||
};
|
||||
void dumpInstructionProfiles() const;
|
||||
void dumpAddressProfiles() const;
|
||||
};
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@
|
||||
#include "Memory.h"
|
||||
#include "LR35902.h"
|
||||
|
||||
EightBit::Disassembler::Disassembler() {
|
||||
EightBit::GameBoy::Disassembler::Disassembler() {
|
||||
// Disable exceptions where too many format arguments are available
|
||||
m_formatter.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::state(EightBit::LR35902& cpu) {
|
||||
std::string EightBit::GameBoy::Disassembler::state(LR35902& cpu) {
|
||||
|
||||
auto pc = cpu.PC();
|
||||
auto sp = cpu.SP();
|
||||
@ -44,7 +44,7 @@ std::string EightBit::Disassembler::state(EightBit::LR35902& cpu) {
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::RP(int rp) const {
|
||||
std::string EightBit::GameBoy::Disassembler::RP(int rp) const {
|
||||
switch (rp) {
|
||||
case 0:
|
||||
return "BC";
|
||||
@ -58,7 +58,7 @@ std::string EightBit::Disassembler::RP(int rp) const {
|
||||
throw std::logic_error("Unhandled register pair");
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::RP2(int rp) const {
|
||||
std::string EightBit::GameBoy::Disassembler::RP2(int rp) const {
|
||||
switch (rp) {
|
||||
case 0:
|
||||
return "BC";
|
||||
@ -72,7 +72,7 @@ std::string EightBit::Disassembler::RP2(int rp) const {
|
||||
throw std::logic_error("Unhandled register pair");
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::R(int r) const {
|
||||
std::string EightBit::GameBoy::Disassembler::R(int r) const {
|
||||
switch (r) {
|
||||
case 0:
|
||||
return "B";
|
||||
@ -94,7 +94,7 @@ std::string EightBit::Disassembler::R(int r) const {
|
||||
throw std::logic_error("Unhandled register");
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::cc(int flag) {
|
||||
std::string EightBit::GameBoy::Disassembler::cc(int flag) {
|
||||
switch (flag) {
|
||||
case 0:
|
||||
return "NZ";
|
||||
@ -116,7 +116,7 @@ std::string EightBit::Disassembler::cc(int flag) {
|
||||
throw std::logic_error("Unhandled condition");
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::alu(int which) {
|
||||
std::string EightBit::GameBoy::Disassembler::alu(int which) {
|
||||
switch (which) {
|
||||
case 0: // ADD A,n
|
||||
return "ADD";
|
||||
@ -138,17 +138,17 @@ std::string EightBit::Disassembler::alu(int which) {
|
||||
throw std::logic_error("Unhandled alu operation");
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::disassemble(LR35902& cpu) {
|
||||
std::string EightBit::GameBoy::Disassembler::disassemble(LR35902& cpu) {
|
||||
m_prefixCB = false;
|
||||
std::ostringstream output;
|
||||
disassemble(output, cpu, cpu.PC().word);
|
||||
return output.str();
|
||||
}
|
||||
|
||||
void EightBit::Disassembler::disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc) {
|
||||
void EightBit::GameBoy::Disassembler::disassemble(std::ostringstream& output, LR35902& cpu, uint16_t pc) {
|
||||
|
||||
auto& memory = cpu.getMemory();
|
||||
auto opcode = memory.peek(pc);
|
||||
auto& bus = cpu.BUS();
|
||||
auto opcode = bus.peek(pc);
|
||||
|
||||
// hex opcode
|
||||
output << hex(opcode);
|
||||
@ -160,11 +160,11 @@ void EightBit::Disassembler::disassemble(std::ostringstream& output, LR35902& cp
|
||||
auto p = (y & 0b110) >> 1;
|
||||
auto q = (y & 1);
|
||||
|
||||
auto immediate = memory.peek(pc + 1);
|
||||
auto absolute = memory.peekWord(pc + 1);
|
||||
auto immediate = bus.peek(pc + 1);
|
||||
auto absolute = bus.peekWord(pc + 1);
|
||||
auto displacement = (int8_t)immediate;
|
||||
auto relative = pc + displacement + 2;
|
||||
auto indexedImmediate = memory.peek(pc + 1);
|
||||
auto indexedImmediate = bus.peek(pc + 1);
|
||||
|
||||
auto dumpCount = 0;
|
||||
auto ioRegister = IoRegister::Unused;
|
||||
@ -183,7 +183,7 @@ void EightBit::Disassembler::disassemble(std::ostringstream& output, LR35902& cp
|
||||
x, y, z, p, q);
|
||||
|
||||
for (int i = 0; i < dumpCount; ++i)
|
||||
output << hex(memory.peek(pc + i + 1));
|
||||
output << hex(bus.peek(pc + i + 1));
|
||||
|
||||
output << '\t';
|
||||
m_formatter.parse(specification);
|
||||
@ -206,7 +206,7 @@ void EightBit::Disassembler::disassemble(std::ostringstream& output, LR35902& cp
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Disassembler::disassembleCB(
|
||||
void EightBit::GameBoy::Disassembler::disassembleCB(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
@ -256,7 +256,7 @@ void EightBit::Disassembler::disassembleCB(
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Disassembler::disassembleOther(
|
||||
void EightBit::GameBoy::Disassembler::disassembleOther(
|
||||
std::ostringstream& output,
|
||||
LR35902& cpu,
|
||||
uint16_t pc,
|
||||
@ -525,13 +525,13 @@ void EightBit::Disassembler::disassembleOther(
|
||||
}
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::flag(uint8_t value, int flag, const std::string& represents) {
|
||||
std::string EightBit::GameBoy::Disassembler::flag(uint8_t value, int flag, const std::string& represents) {
|
||||
std::ostringstream output;
|
||||
output << (value & flag ? represents : "-");
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::flags(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::flags(uint8_t value) {
|
||||
std::ostringstream output;
|
||||
output
|
||||
<< flag(value, LR35902::ZF, "Z")
|
||||
@ -545,37 +545,37 @@ std::string EightBit::Disassembler::flags(uint8_t value) {
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::hex(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::hex(uint8_t value) {
|
||||
std::ostringstream output;
|
||||
output << std::hex << std::setw(2) << std::setfill('0') << (int)value;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::hex(uint16_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::hex(uint16_t value) {
|
||||
std::ostringstream output;
|
||||
output << std::hex << std::setw(4) << std::setfill('0') << (int)value;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::binary(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::binary(uint8_t value) {
|
||||
std::ostringstream output;
|
||||
output << std::bitset<8>(value);
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::decimal(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::decimal(uint8_t value) {
|
||||
std::ostringstream output;
|
||||
output << (int)value;
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::invalid(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::invalid(uint8_t value) {
|
||||
std::ostringstream output;
|
||||
output << "Invalid instruction: " << hex(value) << "(" << binary(value) << ")";
|
||||
return output.str();
|
||||
}
|
||||
|
||||
std::string EightBit::Disassembler::io(uint8_t value) {
|
||||
std::string EightBit::GameBoy::Disassembler::io(uint8_t value) {
|
||||
switch (value) {
|
||||
|
||||
// Port/Mode Registers
|
||||
|
@ -3,20 +3,20 @@
|
||||
#include "Processor.h"
|
||||
#include "CharacterDefinition.h"
|
||||
|
||||
EightBit::Display::Display(const AbstractColourPalette* colours, Bus& bus)
|
||||
EightBit::GameBoy::Display::Display(const AbstractColourPalette* colours, Bus& bus)
|
||||
: m_bus(bus),
|
||||
m_colours(colours) {
|
||||
}
|
||||
|
||||
const std::vector<uint32_t>& EightBit::Display::pixels() const {
|
||||
const std::vector<uint32_t>& EightBit::GameBoy::Display::pixels() const {
|
||||
return m_pixels;
|
||||
}
|
||||
|
||||
void EightBit::Display::initialise() {
|
||||
void EightBit::GameBoy::Display::initialise() {
|
||||
m_pixels.resize(RasterWidth * RasterHeight);
|
||||
}
|
||||
|
||||
void EightBit::Display::render() {
|
||||
void EightBit::GameBoy::Display::render() {
|
||||
|
||||
auto control = m_bus.peekRegister(Bus::LCDC);
|
||||
auto on = control & Bus::LcdEnable;
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "Bus.h"
|
||||
#include "GameBoyBus.h"
|
||||
|
||||
EightBit::Bus::Bus()
|
||||
: Memory(0xffff),
|
||||
m_disableBootRom(false),
|
||||
EightBit::GameBoy::Bus::Bus()
|
||||
: m_disableBootRom(false),
|
||||
m_disableGameRom(false),
|
||||
m_rom(false),
|
||||
m_banked(false),
|
||||
m_ram(false),
|
||||
m_battery(false),
|
||||
m_higherRomBank(true),
|
||||
@ -14,47 +14,35 @@ EightBit::Bus::Bus()
|
||||
m_ramBank(0),
|
||||
m_timerCounter(0),
|
||||
m_timerRate(0) {
|
||||
m_bootRom.resize(0x100);
|
||||
m_gameRom.resize(0x10000);
|
||||
WrittenByte.connect(std::bind(&Bus::Bus_WrittenByte, this, std::placeholders::_1));
|
||||
WrittenByte.connect(std::bind(&GameBoy::Bus::Bus_WrittenByte, this, std::placeholders::_1));
|
||||
m_divCounter.word = 0xabcc;
|
||||
}
|
||||
|
||||
void EightBit::Bus::reset() {
|
||||
void EightBit::GameBoy::Bus::reset() {
|
||||
pokeRegister(NR52, 0xf1);
|
||||
pokeRegister(LCDC, DisplayBackground | BackgroundCharacterDataSelection | LcdEnable);
|
||||
m_divCounter.word = 0xabcc;
|
||||
m_timerCounter = 0;
|
||||
}
|
||||
|
||||
void EightBit::Bus::clear() {
|
||||
Memory::clear();
|
||||
std::fill(m_bootRom.begin(), m_bootRom.end(), 0);
|
||||
std::fill(m_gameRom.begin(), m_gameRom.end(), 0);
|
||||
void EightBit::GameBoy::Bus::loadBootRom(const std::string& path) {
|
||||
m_bootRom.load(path);
|
||||
}
|
||||
|
||||
void EightBit::Bus::loadBootRom(const std::string& path) {
|
||||
loadBinary(path, m_bootRom, 0, 0x100);
|
||||
}
|
||||
|
||||
void EightBit::Bus::loadGameRom(const std::string& path) {
|
||||
loadBinary(path, m_gameRom);
|
||||
void EightBit::GameBoy::Bus::loadGameRom(const std::string& path) {
|
||||
const auto bankSize = 0x4000;
|
||||
m_gameRomBanks.resize(1);
|
||||
const auto size = m_gameRomBanks[0].load(path, bankSize);
|
||||
auto banks = size / bankSize;
|
||||
if (banks > 1) {
|
||||
m_gameRomBanks.resize(banks);
|
||||
for (int bank = 1; bank < banks; ++banks)
|
||||
m_gameRomBanks[bank].load(path, bankSize, bankSize * bank);
|
||||
}
|
||||
validateCartridgeType();
|
||||
}
|
||||
|
||||
uint8_t& EightBit::Bus::reference(uint16_t address, bool& rom) {
|
||||
rom = true;
|
||||
if ((address < 0x100) && bootRomEnabled())
|
||||
return m_bootRom[address];
|
||||
if ((address < 0x4000) && gameRomEnabled())
|
||||
return m_gameRom[address];
|
||||
if ((address < 0x8000) && gameRomEnabled())
|
||||
return m_gameRom[(address - RomPageSize) + (m_romBank * RomPageSize)];
|
||||
rom = false;
|
||||
return m_bus[address];
|
||||
}
|
||||
|
||||
void EightBit::Bus::Bus_WrittenByte(const AddressEventArgs& e) {
|
||||
void EightBit::GameBoy::Bus::Bus_WrittenByte(const AddressEventArgs& e) {
|
||||
|
||||
const auto address = e.getAddress();
|
||||
const auto value = e.getCell();
|
||||
@ -110,7 +98,7 @@ void EightBit::Bus::Bus_WrittenByte(const AddressEventArgs& e) {
|
||||
break;
|
||||
|
||||
case BASE + DIV: // R/W
|
||||
Memory::reference() = 0;
|
||||
EightBit::Bus::reference() = 0;
|
||||
m_timerCounter = m_divCounter.word = 0;
|
||||
break;
|
||||
case BASE + TIMA: // R/W
|
||||
@ -145,7 +133,7 @@ void EightBit::Bus::Bus_WrittenByte(const AddressEventArgs& e) {
|
||||
case BASE + DMA:
|
||||
break;
|
||||
case BASE + LY: // R/O
|
||||
Memory::reference() = 0;
|
||||
EightBit::Bus::reference() = 0;
|
||||
break;
|
||||
case BASE + BGP:
|
||||
case BASE + OBP0:
|
||||
@ -165,12 +153,12 @@ void EightBit::Bus::Bus_WrittenByte(const AddressEventArgs& e) {
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Bus::checkTimers(int cycles) {
|
||||
void EightBit::GameBoy::Bus::checkTimers(int cycles) {
|
||||
incrementDIV(cycles);
|
||||
checkTimer(cycles);
|
||||
}
|
||||
|
||||
void EightBit::Bus::checkTimer(int cycles) {
|
||||
void EightBit::GameBoy::Bus::checkTimer(int cycles) {
|
||||
if (timerEnabled()) {
|
||||
m_timerCounter -= cycles;
|
||||
if (m_timerCounter <= 0) {
|
||||
@ -180,11 +168,11 @@ void EightBit::Bus::checkTimer(int cycles) {
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Bus::validateCartridgeType() {
|
||||
void EightBit::GameBoy::Bus::validateCartridgeType() {
|
||||
|
||||
m_rom = m_banked = m_ram = m_battery = false;
|
||||
|
||||
switch (m_gameRom[0x147]) {
|
||||
switch (m_gameRomBanks[0].peek(0x147)) {
|
||||
case ROM:
|
||||
m_rom = true;
|
||||
break;
|
@ -5,7 +5,7 @@
|
||||
|
||||
#pragma region Reset and initialisation
|
||||
|
||||
EightBit::LR35902::LR35902(Bus& memory)
|
||||
EightBit::GameBoy::LR35902::LR35902(Bus& memory)
|
||||
: IntelProcessor(memory),
|
||||
m_bus(memory),
|
||||
m_ime(false),
|
||||
@ -13,7 +13,7 @@ EightBit::LR35902::LR35902(Bus& memory)
|
||||
m_prefixCB(false) {
|
||||
}
|
||||
|
||||
void EightBit::LR35902::reset() {
|
||||
void EightBit::GameBoy::LR35902::reset() {
|
||||
IntelProcessor::reset();
|
||||
di();
|
||||
SP().word = Mask16 - 1;
|
||||
@ -24,15 +24,15 @@ void EightBit::LR35902::reset() {
|
||||
|
||||
#pragma region Interrupt routines
|
||||
|
||||
void EightBit::LR35902::di() {
|
||||
void EightBit::GameBoy::LR35902::di() {
|
||||
IME() = false;
|
||||
}
|
||||
|
||||
void EightBit::LR35902::ei() {
|
||||
void EightBit::GameBoy::LR35902::ei() {
|
||||
IME() = true;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::interrupt(uint8_t value) {
|
||||
int EightBit::GameBoy::LR35902::interrupt(uint8_t value) {
|
||||
di();
|
||||
restart(value);
|
||||
return 4;
|
||||
@ -42,13 +42,13 @@ int EightBit::LR35902::interrupt(uint8_t value) {
|
||||
|
||||
#pragma region Flag manipulation helpers
|
||||
|
||||
void EightBit::LR35902::increment(uint8_t& f, uint8_t& operand) {
|
||||
void EightBit::GameBoy::LR35902::increment(uint8_t& f, uint8_t& operand) {
|
||||
clearFlag(f, NF);
|
||||
adjustZero<LR35902>(f, ++operand);
|
||||
clearFlag(f, HC, lowNibble(operand));
|
||||
}
|
||||
|
||||
void EightBit::LR35902::decrement(uint8_t& f, uint8_t& operand) {
|
||||
void EightBit::GameBoy::LR35902::decrement(uint8_t& f, uint8_t& operand) {
|
||||
setFlag(f, NF);
|
||||
clearFlag(f, HC, lowNibble(operand));
|
||||
adjustZero<LR35902>(f, --operand);
|
||||
@ -58,7 +58,7 @@ void EightBit::LR35902::decrement(uint8_t& f, uint8_t& operand) {
|
||||
|
||||
#pragma region PC manipulation: call/ret/jp/jr
|
||||
|
||||
bool EightBit::LR35902::jrConditionalFlag(uint8_t& f, int flag) {
|
||||
bool EightBit::GameBoy::LR35902::jrConditionalFlag(uint8_t& f, int flag) {
|
||||
switch (flag) {
|
||||
case 0: // NZ
|
||||
return jrConditional(!(f & ZF));
|
||||
@ -74,7 +74,7 @@ bool EightBit::LR35902::jrConditionalFlag(uint8_t& f, int flag) {
|
||||
throw std::logic_error("Unhandled JR conditional");
|
||||
}
|
||||
|
||||
bool EightBit::LR35902::jumpConditionalFlag(uint8_t& f, int flag) {
|
||||
bool EightBit::GameBoy::LR35902::jumpConditionalFlag(uint8_t& f, int flag) {
|
||||
switch (flag) {
|
||||
case 0: // NZ
|
||||
return jumpConditional(!(f & ZF));
|
||||
@ -90,12 +90,12 @@ bool EightBit::LR35902::jumpConditionalFlag(uint8_t& f, int flag) {
|
||||
throw std::logic_error("Unhandled JP conditional");
|
||||
}
|
||||
|
||||
void EightBit::LR35902::reti() {
|
||||
void EightBit::GameBoy::LR35902::reti() {
|
||||
ret();
|
||||
ei();
|
||||
}
|
||||
|
||||
bool EightBit::LR35902::returnConditionalFlag(uint8_t& f, int flag) {
|
||||
bool EightBit::GameBoy::LR35902::returnConditionalFlag(uint8_t& f, int flag) {
|
||||
switch (flag) {
|
||||
case 0: // NZ
|
||||
return returnConditional(!(f & ZF));
|
||||
@ -111,7 +111,7 @@ bool EightBit::LR35902::returnConditionalFlag(uint8_t& f, int flag) {
|
||||
throw std::logic_error("Unhandled RET conditional");
|
||||
}
|
||||
|
||||
bool EightBit::LR35902::callConditionalFlag(uint8_t& f, int flag) {
|
||||
bool EightBit::GameBoy::LR35902::callConditionalFlag(uint8_t& f, int flag) {
|
||||
switch (flag) {
|
||||
case 0: // NZ
|
||||
return callConditional(!(f & ZF));
|
||||
@ -131,7 +131,7 @@ bool EightBit::LR35902::callConditionalFlag(uint8_t& f, int flag) {
|
||||
|
||||
#pragma region 16-bit arithmetic
|
||||
|
||||
void EightBit::LR35902::add(uint8_t& f, register16_t& operand, register16_t value) {
|
||||
void EightBit::GameBoy::LR35902::add(uint8_t& f, register16_t& operand, register16_t value) {
|
||||
|
||||
MEMPTR() = operand;
|
||||
|
||||
@ -148,7 +148,7 @@ void EightBit::LR35902::add(uint8_t& f, register16_t& operand, register16_t valu
|
||||
|
||||
#pragma region ALU
|
||||
|
||||
void EightBit::LR35902::add(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
|
||||
void EightBit::GameBoy::LR35902::add(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
|
||||
|
||||
register16_t result;
|
||||
result.word = operand + value + carry;
|
||||
@ -162,11 +162,11 @@ void EightBit::LR35902::add(uint8_t& f, uint8_t& operand, uint8_t value, int car
|
||||
adjustZero<LR35902>(f, operand);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::adc(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::adc(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
add(f, operand, value, (f & CF) >> 4);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
|
||||
void EightBit::GameBoy::LR35902::subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry) {
|
||||
|
||||
register16_t result;
|
||||
result.word = operand - value - carry;
|
||||
@ -180,27 +180,27 @@ void EightBit::LR35902::subtract(uint8_t& f, uint8_t& operand, uint8_t value, in
|
||||
adjustZero<LR35902>(f, operand);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::sbc(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::sbc(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
subtract(f, operand, value, (f & CF) >> 4);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::andr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::andr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
setFlag(f, HC);
|
||||
clearFlag(f, CF | NF);
|
||||
adjustZero<LR35902>(f, operand &= value);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::xorr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::xorr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
clearFlag(f, HC | CF | NF);
|
||||
adjustZero<LR35902>(f, operand ^= value);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::orr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::orr(uint8_t& f, uint8_t& operand, uint8_t value) {
|
||||
clearFlag(f, HC | CF | NF);
|
||||
adjustZero<LR35902>(f, operand |= value);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::compare(uint8_t& f, uint8_t check, uint8_t value) {
|
||||
void EightBit::GameBoy::LR35902::compare(uint8_t& f, uint8_t check, uint8_t value) {
|
||||
subtract(f, check, value);
|
||||
}
|
||||
|
||||
@ -208,50 +208,50 @@ void EightBit::LR35902::compare(uint8_t& f, uint8_t check, uint8_t value) {
|
||||
|
||||
#pragma region Shift and rotate
|
||||
|
||||
uint8_t EightBit::LR35902::rlc(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::rlc(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
setFlag(f, CF, operand & Bit7);
|
||||
return _rotl8(operand, 1);
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::rrc(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::rrc(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
setFlag(f, CF, operand & Bit0);
|
||||
return _rotr8(operand, 1);
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::rl(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::rl(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
const auto carry = f & CF;
|
||||
setFlag(f, CF, operand & Bit7);
|
||||
return (operand << 1) | (carry >> 4); // CF at Bit4
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::rr(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::rr(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
const auto carry = f & CF;
|
||||
setFlag(f, CF, operand & Bit0);
|
||||
return (operand >> 1) | (carry << 3); // CF at Bit4
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::sla(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::sla(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
setFlag(f, CF, operand & Bit7);
|
||||
return operand << 1;
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::sra(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::sra(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
setFlag(f, CF, operand & Bit0);
|
||||
return (operand >> 1) | (operand & Bit7);
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::swap(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::swap(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | CF);
|
||||
return promoteNibble(operand) | demoteNibble(operand);
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::srl(uint8_t& f, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::srl(uint8_t& f, uint8_t operand) {
|
||||
clearFlag(f, NF | HC | ZF);
|
||||
setFlag(f, CF, operand & Bit0);
|
||||
return (operand >> 1) & ~Bit7;
|
||||
@ -261,7 +261,7 @@ uint8_t EightBit::LR35902::srl(uint8_t& f, uint8_t operand) {
|
||||
|
||||
#pragma region BIT/SET/RES
|
||||
|
||||
uint8_t EightBit::LR35902::bit(uint8_t& f, int n, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::bit(uint8_t& f, int n, uint8_t operand) {
|
||||
auto carry = f & CF;
|
||||
uint8_t discarded = operand;
|
||||
andr(f, discarded, 1 << n);
|
||||
@ -269,11 +269,11 @@ uint8_t EightBit::LR35902::bit(uint8_t& f, int n, uint8_t operand) {
|
||||
return operand;
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::res(int n, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::res(int n, uint8_t operand) {
|
||||
return operand & ~(1 << n);
|
||||
}
|
||||
|
||||
uint8_t EightBit::LR35902::set(int n, uint8_t operand) {
|
||||
uint8_t EightBit::GameBoy::LR35902::set(int n, uint8_t operand) {
|
||||
return operand | (1 << n);
|
||||
}
|
||||
|
||||
@ -281,7 +281,7 @@ uint8_t EightBit::LR35902::set(int n, uint8_t operand) {
|
||||
|
||||
#pragma region Miscellaneous instructions
|
||||
|
||||
void EightBit::LR35902::daa(uint8_t& a, uint8_t& f) {
|
||||
void EightBit::GameBoy::LR35902::daa(uint8_t& a, uint8_t& f) {
|
||||
|
||||
int updated = a;
|
||||
|
||||
@ -304,17 +304,17 @@ void EightBit::LR35902::daa(uint8_t& a, uint8_t& f) {
|
||||
adjustZero<LR35902>(f, a);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::cpl(uint8_t& a, uint8_t& f) {
|
||||
void EightBit::GameBoy::LR35902::cpl(uint8_t& a, uint8_t& f) {
|
||||
setFlag(f, HC | NF);
|
||||
a = ~a;
|
||||
}
|
||||
|
||||
void EightBit::LR35902::scf(uint8_t& a, uint8_t& f) {
|
||||
void EightBit::GameBoy::LR35902::scf(uint8_t& a, uint8_t& f) {
|
||||
setFlag(f, CF);
|
||||
clearFlag(f, HC | NF);
|
||||
}
|
||||
|
||||
void EightBit::LR35902::ccf(uint8_t& a, uint8_t& f) {
|
||||
void EightBit::GameBoy::LR35902::ccf(uint8_t& a, uint8_t& f) {
|
||||
clearFlag(f, NF | HC);
|
||||
clearFlag(f, CF, f & CF);
|
||||
}
|
||||
@ -323,19 +323,19 @@ void EightBit::LR35902::ccf(uint8_t& a, uint8_t& f) {
|
||||
|
||||
#pragma region Controlled instruction execution
|
||||
|
||||
int EightBit::LR35902::runRasterLines() {
|
||||
int EightBit::GameBoy::LR35902::runRasterLines() {
|
||||
m_bus.resetLY();
|
||||
return runRasterLines(Display::RasterHeight);
|
||||
}
|
||||
|
||||
int EightBit::LR35902::runRasterLines(int limit) {
|
||||
int EightBit::GameBoy::LR35902::runRasterLines(int limit) {
|
||||
int count = 0;
|
||||
for (int line = 0; line < limit; ++line)
|
||||
count += runRasterLine();
|
||||
return count;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::runRasterLine() {
|
||||
int EightBit::GameBoy::LR35902::runRasterLine() {
|
||||
const auto count = run(cyclesPerLine());
|
||||
m_bus.incrementLY();
|
||||
if ((m_bus.peekRegister(Bus::STAT) & Bit6) && (m_bus.peekRegister(Bus::LYC) == m_bus.peekRegister(Bus::LY)))
|
||||
@ -343,12 +343,12 @@ int EightBit::LR35902::runRasterLine() {
|
||||
return count;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::runVerticalBlankLines() {
|
||||
int EightBit::GameBoy::LR35902::runVerticalBlankLines() {
|
||||
m_bus.triggerInterrupt(Bus::Interrupts::VerticalBlank);
|
||||
return runRasterLines(Bus::TotalLineCount - Display::RasterHeight);
|
||||
}
|
||||
|
||||
int EightBit::LR35902::singleStep() {
|
||||
int EightBit::GameBoy::LR35902::singleStep() {
|
||||
|
||||
int current = 0;
|
||||
|
||||
@ -385,7 +385,7 @@ int EightBit::LR35902::singleStep() {
|
||||
return current;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::step() {
|
||||
int EightBit::GameBoy::LR35902::step() {
|
||||
ExecutingInstruction.fire(*this);
|
||||
m_prefixCB = false;
|
||||
cycles = 0;
|
||||
@ -396,7 +396,7 @@ int EightBit::LR35902::step() {
|
||||
|
||||
#pragma region Instruction decode and execution
|
||||
|
||||
int EightBit::LR35902::execute(uint8_t opcode) {
|
||||
int EightBit::GameBoy::LR35902::execute(uint8_t opcode) {
|
||||
|
||||
const auto& decoded = getDecodedOpcode(opcode);
|
||||
|
||||
@ -418,7 +418,7 @@ int EightBit::LR35902::execute(uint8_t opcode) {
|
||||
return cycles * 4;
|
||||
}
|
||||
|
||||
void EightBit::LR35902::executeCB(int x, int y, int z, int p, int q) {
|
||||
void EightBit::GameBoy::LR35902::executeCB(int x, int y, int z, int p, int q) {
|
||||
auto& a = A();
|
||||
auto& f = F();
|
||||
switch (x) {
|
||||
@ -481,7 +481,7 @@ void EightBit::LR35902::executeCB(int x, int y, int z, int p, int q) {
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::LR35902::executeOther(int x, int y, int z, int p, int q) {
|
||||
void EightBit::GameBoy::LR35902::executeOther(int x, int y, int z, int p, int q) {
|
||||
auto& a = A();
|
||||
auto& f = F();
|
||||
switch (x) {
|
||||
|
@ -140,7 +140,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\inc\AbstractColourPalette.h" />
|
||||
<ClInclude Include="..\inc\Bus.h" />
|
||||
<ClInclude Include="..\inc\GameBoyBus.h" />
|
||||
<ClInclude Include="..\inc\CharacterDefinition.h" />
|
||||
<ClInclude Include="..\inc\Disassembler.h" />
|
||||
<ClInclude Include="..\inc\Display.h" />
|
||||
@ -149,7 +149,7 @@
|
||||
<ClInclude Include="stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Bus.cpp" />
|
||||
<ClCompile Include="GameBoyBus.cpp" />
|
||||
<ClCompile Include="Disassembler.cpp" />
|
||||
<ClCompile Include="Display.cpp" />
|
||||
<ClCompile Include="LR35902.cpp" />
|
||||
|
@ -23,9 +23,6 @@
|
||||
<ClInclude Include="..\inc\Profiler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\Bus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\CharacterDefinition.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -35,6 +32,9 @@
|
||||
<ClInclude Include="..\inc\Display.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\inc\GameBoyBus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
@ -49,10 +49,10 @@
|
||||
<ClCompile Include="Profiler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Bus.cpp">
|
||||
<ClCompile Include="Display.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Display.cpp">
|
||||
<ClCompile Include="GameBoyBus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
@ -2,13 +2,13 @@
|
||||
#include "Profiler.h"
|
||||
#include "LR35902.h"
|
||||
|
||||
EightBit::Profiler::Profiler(LR35902& cpu)
|
||||
EightBit::GameBoy::Profiler::Profiler(LR35902& cpu)
|
||||
: m_cpu(cpu) {
|
||||
std::fill(m_instructions.begin(), m_instructions.end(), 0);
|
||||
std::fill(m_addresses.begin(), m_addresses.end(), 0);
|
||||
}
|
||||
|
||||
void EightBit::Profiler::add(uint16_t address, uint8_t instruction) {
|
||||
void EightBit::GameBoy::Profiler::add(uint16_t address, uint8_t instruction) {
|
||||
|
||||
m_instructions[instruction]++;
|
||||
|
||||
@ -19,12 +19,12 @@ void EightBit::Profiler::add(uint16_t address, uint8_t instruction) {
|
||||
m_addresses[address]++;
|
||||
}
|
||||
|
||||
void EightBit::Profiler::dump() const {
|
||||
void EightBit::GameBoy::Profiler::dump() const {
|
||||
dumpInstructionProfiles();
|
||||
dumpAddressProfiles();
|
||||
}
|
||||
|
||||
void EightBit::Profiler::dumpInstructionProfiles() const {
|
||||
void EightBit::GameBoy::Profiler::dumpInstructionProfiles() const {
|
||||
std::cout << "** instructions" << std::endl;
|
||||
for (int i = 0; i < 0x100; ++i) {
|
||||
auto count = m_instructions[i];
|
||||
@ -33,7 +33,7 @@ void EightBit::Profiler::dumpInstructionProfiles() const {
|
||||
}
|
||||
}
|
||||
|
||||
void EightBit::Profiler::dumpAddressProfiles() const {
|
||||
void EightBit::GameBoy::Profiler::dumpAddressProfiles() const {
|
||||
std::cout << "** addresses" << std::endl;
|
||||
for (int i = 0; i < 0x10000; ++i) {
|
||||
auto count = m_addresses[i];
|
||||
|
Loading…
Reference in New Issue
Block a user