Compare commits
5
Commits
6a44e48244
...
2c870e89c6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c870e89c6 | ||
|
|
e2db1598af | ||
|
|
2a234fabfd | ||
|
|
07345d0552 | ||
|
|
7689efeb44 |
@@ -14,6 +14,7 @@ AlignTrailingComments: true
|
|||||||
AllowAllArgumentsOnNextLine: false
|
AllowAllArgumentsOnNextLine: false
|
||||||
AllowAllConstructorInitializersOnNextLine: true
|
AllowAllConstructorInitializersOnNextLine: true
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
AllowShortLambdasOnASingleLine: All
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
|||||||
+92
-14
@@ -44,13 +44,6 @@ CPU::CPU(uint32_t frequency)
|
|||||||
m_shared_registers.emplace("nf", &m_nf);
|
m_shared_registers.emplace("nf", &m_nf);
|
||||||
m_shared_registers.emplace("hf", &m_hf);
|
m_shared_registers.emplace("hf", &m_hf);
|
||||||
m_shared_registers.emplace("cf", &m_cf);
|
m_shared_registers.emplace("cf", &m_cf);
|
||||||
|
|
||||||
// Add opcode functions to lookup table
|
|
||||||
m_opcode_lookup_table.emplace(0xc6, std::bind(&CPU::add, this));
|
|
||||||
m_opcode_lookup_table.emplace(0x01, std::bind(&CPU::ld16, this));
|
|
||||||
m_opcode_lookup_table.emplace(0x11, std::bind(&CPU::ld16, this));
|
|
||||||
m_opcode_lookup_table.emplace(0x21, std::bind(&CPU::ld16, this));
|
|
||||||
m_opcode_lookup_table.emplace(0x31, std::bind(&CPU::ld16, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU::~CPU()
|
CPU::~CPU()
|
||||||
@@ -65,8 +58,23 @@ void CPU::update()
|
|||||||
if (m_wait_cycles <= 0) {
|
if (m_wait_cycles <= 0) {
|
||||||
// Read next opcode
|
// Read next opcode
|
||||||
uint8_t opcode = peekMemory();
|
uint8_t opcode = peekMemory();
|
||||||
VERIFY(m_opcode_lookup_table.find(opcode) != m_opcode_lookup_table.end(), "opcode {:#x} not implemented", opcode);
|
switch (opcode) {
|
||||||
m_opcode_lookup_table[opcode]();
|
case 0x01: ld16(); break;
|
||||||
|
case 0x08: ld16(); break;
|
||||||
|
case 0x11: ld16(); break;
|
||||||
|
case 0x21: ld16(); break;
|
||||||
|
case 0x31: ld16(); break;
|
||||||
|
case 0x3e: ld8(); break;
|
||||||
|
case 0xc3: jp16(); break;
|
||||||
|
case 0xc6: add(); break;
|
||||||
|
case 0xe0: ldh8(); break;
|
||||||
|
case 0xf0: ldh8(); break;
|
||||||
|
case 0xf8: ld16(); break;
|
||||||
|
case 0xf9: ld16(); break;
|
||||||
|
default:
|
||||||
|
print("opcode {:#x} not implemented", opcode);
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// print("This is an update from the CPU\n");
|
// print("This is an update from the CPU\n");
|
||||||
@@ -77,11 +85,8 @@ void CPU::add()
|
|||||||
uint8_t opcode = consumeMemory();
|
uint8_t opcode = consumeMemory();
|
||||||
uint8_t immediate = consumeMemory();
|
uint8_t immediate = consumeMemory();
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case 0xc6: // ADD A,d8
|
case 0xc6:
|
||||||
// Program counter +2
|
// ADD A,d8
|
||||||
m_pc += 2;
|
|
||||||
|
|
||||||
// Clock cycles +8
|
|
||||||
m_wait_cycles += 8;
|
m_wait_cycles += 8;
|
||||||
|
|
||||||
// Flags: Z0HC
|
// Flags: Z0HC
|
||||||
@@ -101,6 +106,40 @@ void CPU::add()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::ld8()
|
||||||
|
{
|
||||||
|
uint8_t opcode = consumeMemory();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x3e:
|
||||||
|
m_wait_cycles += 8;
|
||||||
|
m_a = immediate8();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::ldh8()
|
||||||
|
{
|
||||||
|
uint8_t opcode = consumeMemory();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0xe0:
|
||||||
|
// LD ($ff00 + n),A == LDH (n),A
|
||||||
|
// Put value in A into address (0xff00 + next byte in memory):
|
||||||
|
m_wait_cycles += 12;
|
||||||
|
writeFf(m_a);
|
||||||
|
break;
|
||||||
|
case 0xf0:
|
||||||
|
// LD A,($ff00 + n) == LDH A,(n)
|
||||||
|
// Put value at address (0xff00 + next byte in memory) into A:
|
||||||
|
m_wait_cycles += 12;
|
||||||
|
m_a = readFf();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::ld16()
|
void CPU::ld16()
|
||||||
{
|
{
|
||||||
uint8_t opcode = consumeMemory();
|
uint8_t opcode = consumeMemory();
|
||||||
@@ -123,6 +162,35 @@ void CPU::ld16()
|
|||||||
m_sp = immediate16();
|
m_sp = immediate16();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 0x08: {
|
||||||
|
m_wait_cycles += 20;
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xf8: {
|
||||||
|
m_wait_cycles += 12;
|
||||||
|
// TODO
|
||||||
|
// TODO flags
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0xf9: {
|
||||||
|
m_wait_cycles += 8;
|
||||||
|
m_sp = hl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::jp16()
|
||||||
|
{
|
||||||
|
uint8_t opcode = consumeMemory();
|
||||||
|
switch (opcode) {
|
||||||
|
case 0xc3:
|
||||||
|
m_wait_cycles += 16;
|
||||||
|
m_pc = immediate16();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -140,6 +208,16 @@ uint8_t CPU::consumeMemory()
|
|||||||
return Emu::the().readMemory(m_pc++);
|
return Emu::the().readMemory(m_pc++);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPU::writeFf(uint32_t value)
|
||||||
|
{
|
||||||
|
Emu::the().writeMemory(immediate8() | (0xff << 8), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CPU::readFf()
|
||||||
|
{
|
||||||
|
return Emu::the().readMemory(immediate8() | (0xff << 8));
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::setBc(uint32_t value)
|
void CPU::setBc(uint32_t value)
|
||||||
{
|
{
|
||||||
m_b = value >> 8;
|
m_b = value >> 8;
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ public:
|
|||||||
// Load Instructions
|
// Load Instructions
|
||||||
|
|
||||||
// 8-bit
|
// 8-bit
|
||||||
|
void ld8();
|
||||||
|
void ldh8();
|
||||||
|
|
||||||
// 16-bit
|
// 16-bit
|
||||||
void ld16();
|
void ld16();
|
||||||
@@ -45,6 +47,8 @@ public:
|
|||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
// Jumps and Subroutines
|
// Jumps and Subroutines
|
||||||
|
|
||||||
|
void jp16();
|
||||||
|
|
||||||
// -------------------------------------
|
// -------------------------------------
|
||||||
// Stack Operations Instructions
|
// Stack Operations Instructions
|
||||||
|
|
||||||
@@ -55,8 +59,12 @@ private:
|
|||||||
uint8_t peekMemory(int32_t offset = 0) const;
|
uint8_t peekMemory(int32_t offset = 0) const;
|
||||||
uint8_t consumeMemory();
|
uint8_t consumeMemory();
|
||||||
|
|
||||||
|
uint32_t immediate8() { return consumeMemory(); }
|
||||||
uint32_t immediate16() { return consumeMemory() | (consumeMemory() << 8); }
|
uint32_t immediate16() { return consumeMemory() | (consumeMemory() << 8); }
|
||||||
|
|
||||||
|
void writeFf(uint32_t value);
|
||||||
|
uint32_t readFf();
|
||||||
|
|
||||||
void setBc(uint32_t value);
|
void setBc(uint32_t value);
|
||||||
void setDe(uint32_t value);
|
void setDe(uint32_t value);
|
||||||
void setHl(uint32_t value);
|
void setHl(uint32_t value);
|
||||||
@@ -83,6 +91,4 @@ private:
|
|||||||
uint32_t m_cf { 0 }; // Carry flag
|
uint32_t m_cf { 0 }; // Carry flag
|
||||||
|
|
||||||
int8_t m_wait_cycles { 0 };
|
int8_t m_wait_cycles { 0 };
|
||||||
|
|
||||||
std::unordered_map<uint32_t, std::function<void()>> m_opcode_lookup_table;
|
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-5
@@ -34,12 +34,15 @@ int main(int argc, char* argv[])
|
|||||||
Emu::the().addMemorySpace("BOOTROM1", 0x0000, 0x00ff); // 256B
|
Emu::the().addMemorySpace("BOOTROM1", 0x0000, 0x00ff); // 256B
|
||||||
Emu::the().addMemorySpace("BOOTROM2", 0x0200, 0x08ff); // 1792B
|
Emu::the().addMemorySpace("BOOTROM2", 0x0200, 0x08ff); // 1792B
|
||||||
Emu::the().addMemorySpace("VRAM", 0x8000, 0x9fff, 2); // 8KiB * 2 banks
|
Emu::the().addMemorySpace("VRAM", 0x8000, 0x9fff, 2); // 8KiB * 2 banks
|
||||||
Emu::the().addMemorySpace("CARDRAM", 0xa000, 0xbfff); // 8KiB
|
Emu::the().addMemorySpace("CARDRAM", 0xa000, 0xbfff, 1); // 8KiB * ? banks, if any
|
||||||
Emu::the().addMemorySpace("WRAM1", 0xc000, 0xcfff, 1); // 4 KiB, Work RAM
|
Emu::the().addMemorySpace("WRAM1", 0xc000, 0xcfff); // 4 KiB, Work RAM
|
||||||
Emu::the().addMemorySpace("WRAM2", 0xd000, 0xdfff, 7); // 4 KiB * 7 banks, Work RAM
|
Emu::the().addMemorySpace("WRAM2", 0xd000, 0xdfff, 7); // 4 KiB * 7 banks, Work RAM
|
||||||
Emu::the().addMemorySpace("ECHORAM", 0xe000, 0xfdff, 1); // 7680B, Mirror of 0xc000~0xddff
|
Emu::the().addMemorySpace("ECHORAM", 0xe000, 0xfdff); // 7680B, Mirror of 0xc000~0xddff
|
||||||
Emu::the().addMemorySpace("OAM", 0xfe00, 0xfe9f, 1); // 160B, Object Attribute Memory (VRAM Sprite Attribute Table)
|
Emu::the().addMemorySpace("OAM", 0xfe00, 0xfe9f); // 160B, Object Attribute Memory (VRAM Sprite Attribute Table)
|
||||||
Emu::the().addMemorySpace("HRAM", 0xff80, 0xfffe, 1); // 127B, High RAM (CPU cache)
|
Emu::the().addMemorySpace("Not Usable", 0xfea0, 0xfeff); // 96B, Nintendo probibits this area
|
||||||
|
Emu::the().addMemorySpace("IO", 0xff00, 0xff7f); // 128B, I/O Registers
|
||||||
|
Emu::the().addMemorySpace("HRAM", 0xff80, 0xfffe); // 127B, High RAM (CPU cache)
|
||||||
|
Emu::the().addMemorySpace("IE", 0xffff, 0xffff); // 1B, Interrupt Enable register
|
||||||
|
|
||||||
// Load bootrom
|
// Load bootrom
|
||||||
auto bootrom = ruc::File("gbc_bios.bin").data();
|
auto bootrom = ruc::File("gbc_bios.bin").data();
|
||||||
|
|||||||
Reference in New Issue
Block a user