Compare commits
5
Commits
009938b126
...
8e4dacca8a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e4dacca8a | ||
|
|
dd2920b2a4 | ||
|
|
2aa50d3532 | ||
|
|
589faceb9b | ||
|
|
e9b47d7d68 |
+1
-1
@@ -36,7 +36,7 @@ We start counting from the right as bit *#0*!
|
||||
| register | 8 | unsigned | r8 | | r8 | r | | Any of the registers | |
|
||||
| register | 16 | unsigned | r16 | | r16 | rr | | Any of the registers | little-endian |
|
||||
| address | 8 | unsigned | a8 | | | (n) | | Address at value | |
|
||||
| address | 16 | unsigned | a16 | a16 | n16 | (nn) | nn | Address at value | little-endian |
|
||||
| address | 16 | unsigned | a16 or () | a16 | n16 | (nn) | nn | Address at value | little-endian |
|
||||
| immediate | 8 | signed | s8 | r8 | e8 | e | dd | Next byte in memory | |
|
||||
| immediate | 8 | unsigned | io8 | (a8) | n16 | (n) | n | 0xff00 + next byte in memory | write to I/O-port |
|
||||
| condition | | | <ZNHC> | NZ | cc | | f | Execute if condition met | |
|
||||
|
||||
+39
-1
@@ -72,24 +72,28 @@ void CPU::update()
|
||||
case 0x06: ldi8(); break;
|
||||
case 0x08: ldr16(); break;
|
||||
case 0x0a: ldr8(); break;
|
||||
case 0x0b: decr16(); break;
|
||||
case 0x0d: dec8(); break;
|
||||
case 0x0e: ldi8(); break;
|
||||
case 0x11: ldi16(); break;
|
||||
case 0x12: ldr8(); break;
|
||||
case 0x16: ldi8(); break;
|
||||
case 0x1a: ldr8(); break;
|
||||
case 0x1b: decr16(); break;
|
||||
case 0x1e: ldi8(); break;
|
||||
case 0x20: jr8(); break;
|
||||
case 0x21: ldi16(); break;
|
||||
case 0x22: ldr8(); break;
|
||||
case 0x26: ldi8(); break;
|
||||
case 0x2a: ldr8(); break;
|
||||
case 0x2a: lda8(); break;
|
||||
case 0x2b: decr16(); break;
|
||||
case 0x2e: ldi8(); break;
|
||||
case 0x2f: misc(); break;
|
||||
case 0x31: ldi16(); break;
|
||||
case 0x32: ldr8(); break;
|
||||
case 0x36: ldi8(); break;
|
||||
case 0x3a: ldr8(); break;
|
||||
case 0x3b: decr16(); break;
|
||||
case 0x3e: ldi8(); break;
|
||||
case 0x40: ldr8(); break;
|
||||
case 0x41: ldr8(); break;
|
||||
@@ -170,6 +174,8 @@ void CPU::update()
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
void CPU::add()
|
||||
{
|
||||
uint8_t opcode = pcRead();
|
||||
@@ -279,6 +285,38 @@ void CPU::lda8()
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::decr16()
|
||||
{
|
||||
uint8_t opcode = pcRead();
|
||||
switch (opcode) {
|
||||
case 0x0b: { // DEC BC
|
||||
uint32_t data = (bc() - 1) & 0xffff;
|
||||
m_c = data & 0xff;
|
||||
m_b = data >> 8;
|
||||
break;
|
||||
}
|
||||
case 0x1b: { // DEC DE
|
||||
uint32_t data = (de() - 1) & 0xffff;
|
||||
m_e = data & 0xff;
|
||||
m_d = data >> 8;
|
||||
break;
|
||||
}
|
||||
case 0x2b: { // DEC HL
|
||||
uint32_t data = (hl() - 1) & 0xffff;
|
||||
m_l = data & 0xff;
|
||||
m_h = data >> 8;
|
||||
break;
|
||||
}
|
||||
case 0x3b: /* DEC SP */ m_sp = (m_sp - 1) & 0xffff; break;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
m_wait_cycles += 8;
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
void CPU::ldi8()
|
||||
{
|
||||
uint8_t opcode = pcRead();
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
void xor8();
|
||||
|
||||
// 16-bit
|
||||
void decr16();
|
||||
|
||||
// -------------------------------------
|
||||
// Bit Operations Instructions
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
|
||||
#include "cpu.h"
|
||||
#include "emu.h"
|
||||
#include "loader.h"
|
||||
#include "ppu.h"
|
||||
#include "ruc/file.h"
|
||||
#include "ruc/format/print.h"
|
||||
|
||||
void Loader::loadRom(std::string_view rom_path)
|
||||
{
|
||||
if (!rom_path.empty()) {
|
||||
m_rom_data = ruc::File(rom_path).data();
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void Loader::init()
|
||||
{
|
||||
destroy();
|
||||
|
||||
Emu::the().init(8000000);
|
||||
|
||||
CPU cpu(8000000);
|
||||
PPU ppu(4000000);
|
||||
|
||||
Emu::the().addProcessingUnit("cpu", &cpu);
|
||||
Emu::the().addProcessingUnit("ppu", &ppu);
|
||||
|
||||
// https://gbdev.io/pandocs/Memory_Map.html
|
||||
// https://gbdev.io/pandocs/Power_Up_Sequence.html
|
||||
Emu::the().addMemorySpace("BOOTROM1", 0x0000, 0x00ff); // 256B
|
||||
loadCartridgeHeader();
|
||||
Emu::the().addMemorySpace("BOOTROM2", 0x0200, 0x08ff); // 1792B
|
||||
Emu::the().addMemorySpace("VRAM", 0x8000, 0x9fff, 2); // 8KiB * 2 banks
|
||||
Emu::the().addMemorySpace("CARDRAM", 0xa000, 0xbfff, 1); // 8KiB * ? banks, if any
|
||||
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("ECHORAM", 0xe000, 0xfdff); // 7680B, Mirror of 0xc000~0xddff
|
||||
Emu::the().addMemorySpace("OAM", 0xfe00, 0xfe9f); // 160B, Object Attribute Memory (VRAM Sprite Attribute Table)
|
||||
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
|
||||
auto bootrom = ruc::File(m_bootrom_path).data();
|
||||
for (size_t i = 0; i < bootrom.length(); ++i) {
|
||||
// Skip cartridge header memory range
|
||||
if (i >= 0x0100 && i <= 0x01ff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Emu::the().writeMemory(i, bootrom[i]);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void Loader::update()
|
||||
{
|
||||
while (true) {
|
||||
Emu::the().update();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::destroy()
|
||||
{
|
||||
Emu::destroy();
|
||||
}
|
||||
|
||||
void Loader::loadCartridgeHeader()
|
||||
{
|
||||
if (m_rom_data.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Emu::the().addMemorySpace("CARTHEADER", 0x100, 0x14f);
|
||||
|
||||
for (size_t i = 0x100; i <= 0x14f; ++i) {
|
||||
Emu::the().writeMemory(i, m_rom_data[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "ruc/singleton.h"
|
||||
|
||||
class Loader final : public ruc::Singleton<Loader> {
|
||||
public:
|
||||
Loader(s) {}
|
||||
|
||||
void loadRom(std::string_view rom_path);
|
||||
|
||||
void setBootromPath(std::string_view bootrom_path) { m_bootrom_path = bootrom_path; }
|
||||
|
||||
private:
|
||||
void init();
|
||||
void update();
|
||||
void destroy();
|
||||
|
||||
void loadCartridgeHeader();
|
||||
|
||||
std::string_view m_bootrom_path;
|
||||
std::string m_rom_data;
|
||||
};
|
||||
+12
-51
@@ -5,63 +5,24 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "emu.h"
|
||||
#include "ppu.h"
|
||||
#include "ruc/file.h"
|
||||
#include "loader.h"
|
||||
#include "ruc/argparser.h"
|
||||
#include "ruc/format/print.h"
|
||||
#include "ruc/timer.h"
|
||||
#include <string_view>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
std::string_view bootrom_path = "gbc_bios.bin";
|
||||
std::string_view rom_path;
|
||||
|
||||
ruc::Timer t;
|
||||
ruc::ArgParser argParser;
|
||||
argParser.addOption(bootrom_path, 'b', "bootrom", nullptr, nullptr, "", ruc::ArgParser::Required::Yes);
|
||||
argParser.addOption(rom_path, 'r', "rom", nullptr, nullptr, "", ruc::ArgParser::Required::Yes);
|
||||
argParser.parse(argc, argv);
|
||||
|
||||
print("{}ms\n", t.elapsedNanoseconds() / 1000000.0);
|
||||
|
||||
Emu::the().init(8000000);
|
||||
|
||||
CPU cpu(8000000);
|
||||
PPU ppu(4000000);
|
||||
|
||||
Emu::the().addProcessingUnit("cpu", &cpu);
|
||||
Emu::the().addProcessingUnit("ppu", &ppu);
|
||||
|
||||
// https://gbdev.io/pandocs/Memory_Map.html
|
||||
// https://gbdev.io/pandocs/Power_Up_Sequence.html
|
||||
Emu::the().addMemorySpace("BOOTROM1", 0x0000, 0x00ff); // 256B
|
||||
Emu::the().addMemorySpace("BOOTROM2", 0x0200, 0x08ff); // 1792B
|
||||
Emu::the().addMemorySpace("VRAM", 0x8000, 0x9fff, 2); // 8KiB * 2 banks
|
||||
Emu::the().addMemorySpace("CARDRAM", 0xa000, 0xbfff, 1); // 8KiB * ? banks, if any
|
||||
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("ECHORAM", 0xe000, 0xfdff); // 7680B, Mirror of 0xc000~0xddff
|
||||
Emu::the().addMemorySpace("OAM", 0xfe00, 0xfe9f); // 160B, Object Attribute Memory (VRAM Sprite Attribute Table)
|
||||
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
|
||||
auto bootrom = ruc::File("gbc_bios.bin").data();
|
||||
for (size_t i = 0; i < bootrom.length(); ++i) {
|
||||
// Skip cartridge header memory range
|
||||
if (i >= 0x0100 && i <= 0x01ff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Emu::the().writeMemory(i, bootrom[i]);
|
||||
}
|
||||
|
||||
// Get shared register
|
||||
print("{}\n", *Emu::the().processingUnit("cpu")->sharedRegister("a"));
|
||||
print("{}\n", *Emu::the().processingUnit("cpu")->sharedRegister("b"));
|
||||
|
||||
while (true) {
|
||||
Emu::the().update();
|
||||
}
|
||||
Loader::the().setBootromPath(bootrom_path);
|
||||
Loader::the().loadRom(rom_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user