Update to release v000r01.

byuu says:

Hooked up a scheduler to enter/exit the CPU core wherever I want. Added
basic 4*1024*1024hz clock, and about eleven or so opcodes. Creating the
disassembler as I encounter each new opcode, not skipping ahead to do
all 'like other' opcodes, eg if I add 'dec b', I don't then add 'dec c'
until I encounter it.
This commit is contained in:
Tim Allen 2010-12-28 17:03:02 +11:00
parent 246d6aaf08
commit e0a9f1cf2c
17 changed files with 336 additions and 13 deletions

View File

@ -1,10 +1,13 @@
gameboy_objects := libco
gameboy_objects += gameboy-system gameboy-cartridge gameboy-memory gameboy-cpu
gameboy_objects += gameboy-system gameboy-scheduler
gameboy_objects += gameboy-cartridge gameboy-memory
gameboy_objects += gameboy-cpu
objects += $(gameboy_objects)
obj/libco.o: libco/libco.c libco/*
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)

View File

@ -41,7 +41,6 @@ void Cartridge::load(uint8_t *data, unsigned size) {
}
loaded = true;
system.interface->message({ "Loaded:\n", info.name });
}
void Cartridge::unload() {

91
gameboy/cpu/core/core.cpp Executable file
View File

@ -0,0 +1,91 @@
#ifdef CPU_CPP
#include "disassembler.cpp"
unsigned opcode_counter = 0;
void CPU::op_unknown() {
uint8 opcode = bus.read(--r[PC]);
print(
"CPU: unknown opcode [", hex<2>(opcode), "]\n",
"af:", hex<4>(r[AF]), " bc:", hex<4>(r[BC]), " de:", hex<4>(r[DE]), " hl:", hex<4>(r[HL]), " ",
"sp:", hex<4>(r[SP]), " pc:", hex<4>(r[PC]), "\n",
"ly:", decimal<3, ' '>(status.lycounter), " exec:", opcode_counter, "\n"
);
while(true) scheduler.exit();
}
//8-bit load commands
template<unsigned x> void CPU::op_ld_r_n() {
r[x] = op_read(r[PC]++);
}
void CPU::op_ldd_hl_a() {
op_write(r[HL], r[A]);
r[HL]--;
}
//16-bit load commands
template<unsigned x, unsigned y> void CPU::op_ld_rr_nn() {
r[y] = op_read(r[PC]++);
r[x] = op_read(r[PC]++);
}
//8-bit arithmetic commands
template<unsigned x> void CPU::op_xor_r() {
r[A] ^= r[x];
r.f.z = r[A] == 0;
r.f.n = 0;
r.f.h = 0;
r.f.c = 0;
}
template<unsigned x> void CPU::op_dec_r() {
r[x]--;
r.f.z = r[x] == 0;
r.f.n = 0; //???
r.f.h = 0; //???
}
//control commands
void CPU::op_nop() {
}
//jump commands
void CPU::op_jp_nn() {
uint8 lo = op_read(r[PC]++);
uint8 hi = op_read(r[PC]++);
r[PC] = (hi << 8) | (lo << 0);
op_io();
}
template<unsigned x, bool y> void CPU::op_jr_f_n() {
int8 n = op_read(r[PC]++);
if(r.f[x] == y) {
r[PC] += n;
op_io();
}
}
void CPU::initialize_opcode_table() {
for(unsigned n = 0; n < 256; n++) opcode_table[n] = &CPU::op_unknown;
opcode_table[0x00] = &CPU::op_nop;
opcode_table[0x05] = &CPU::op_dec_r<B>;
opcode_table[0x06] = &CPU::op_ld_r_n<B>;
opcode_table[0x0d] = &CPU::op_dec_r<C>;
opcode_table[0x0e] = &CPU::op_ld_r_n<C>;
opcode_table[0x20] = &CPU::op_jr_f_n<ZF, 0>;
opcode_table[0x21] = &CPU::op_ld_rr_nn<H, L>;
opcode_table[0x32] = &CPU::op_ldd_hl_a;
opcode_table[0x3e] = &CPU::op_ld_r_n<A>;
opcode_table[0xaf] = &CPU::op_xor_r<A>;
opcode_table[0xc3] = &CPU::op_jp_nn;
}
#endif

27
gameboy/cpu/core/core.hpp Executable file
View File

@ -0,0 +1,27 @@
#include "registers.hpp"
void (CPU::*opcode_table[256])();
void initialize_opcode_table();
void op_unknown();
//8-bit load commands
template<unsigned x> void op_ld_r_n();
void op_ldd_hl_a();
//16-bit load commands
template<unsigned x, unsigned y> void op_ld_rr_nn();
//8-bit arithmetic commands
template<unsigned x> void op_xor_r();
template<unsigned x> void op_dec_r();
//control commands
void op_nop();
//jump commands
void op_jp_nn();
template<unsigned x, bool y> void op_jr_f_n();
//disassembler.cpp
string disassemble(uint16 pc);
string disassemble_opcode(uint16 pc);

View File

@ -0,0 +1,30 @@
#ifdef CPU_CPP
string CPU::disassemble(uint16 pc) {
return { hex<4>(pc), " ", disassemble_opcode(pc) };
}
string CPU::disassemble_opcode(uint16 pc) {
uint8 opcode = bus.read(pc);
uint8 p0 = bus.read(pc + 1);
uint8 p1 = bus.read(pc + 2);
uint8 p2 = bus.read(pc + 3);
switch(opcode) {
case 0x00: return { "nop" };
case 0x05: return { "dec b" };
case 0x06: return { "ld b,$", hex<2>(p0) };
case 0x0d: return { "dec c" };
case 0x0e: return { "ld c,$", hex<2>(p0) };
case 0x20: return { "jp nz,$", hex<2>(p0) };
case 0x21: return { "ld hl,$", hex<2>(p1), hex<2>(p0) };
case 0x32: return { "ldd (hl),a" };
case 0x3e: return { "ld a,$", hex<2>(p0) };
case 0xaf: return { "xor a" };
case 0xc3: return { "jp $", hex<2>(p1), hex<2>(p0) };
}
return { "???? [", hex<2>(opcode), ",", hex<2>(p1), ",", hex<2>(p0), "]" };
}
#endif

View File

@ -1,3 +1,15 @@
enum {
A, F, AF,
B, C, BC,
D, E, DE,
H, L, HL,
SP, PC,
};
enum {
ZF, NF, HF, CF,
};
//register base class
//the idea here is to have all registers derive from a single base class.
//this allows construction of opcodes that can take any register as input or output,
@ -36,6 +48,10 @@ struct RegisterF : Register {
bool z, n, h, c;
operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); }
unsigned operator=(unsigned x) { z = x & 0x80; n = x & 0x40; h = x & 0x20; c = x & 0x10; return *this; }
bool& operator[](unsigned r) {
static bool* table[] = { &z, &n, &h, &c };
return *table[r];
}
};
struct Register16 : Register {
@ -75,5 +91,10 @@ struct Registers {
Register16 sp;
Register16 pc;
Register& operator[](unsigned r) {
static Register* table[] = { &a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc };
return *table[r];
}
Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {}
} r;

View File

@ -1,18 +1,24 @@
//Sharp LR35902 @ 4.19MHz
#include <gameboy.hpp>
#define CPU_CPP
namespace GameBoy {
#include "core/core.cpp"
#include "timing/timing.cpp"
CPU cpu;
void CPU::Main() {
cpu.main();
}
void CPU::main() {
while(true) {
uint8 opcode = bus.read(r.pc++);
switch(opcode) {
case 0x00: break; //NOP
}
print(disassemble(r[PC]), "\n");
uint8 opcode = op_read(r[PC]++);
(this->*opcode_table[opcode])();
opcode_counter++;
}
}
@ -21,7 +27,20 @@ void CPU::power() {
}
void CPU::reset() {
r.pc = 0x0100;
create(Main, 4 * 1024 * 1024);
r[PC] = 0x0100;
r[SP] = 0xfffe;
r[AF] = 0x0000;
r[BC] = 0x0000;
r[DE] = 0x0000;
r[HL] = 0x0000;
status.lycounter = 0;
}
CPU::CPU() {
initialize_opcode_table();
}
}

View File

@ -1,10 +1,16 @@
class CPU {
public:
#include "registers.hpp"
struct CPU : Processor {
#include "core/core.hpp"
#include "timing/timing.hpp"
struct Status {
unsigned lycounter;
} status;
static void Main();
void main();
void power();
void reset();
CPU();
};
extern CPU cpu;

18
gameboy/cpu/timing/opcode.cpp Executable file
View File

@ -0,0 +1,18 @@
#ifdef CPU_CPP
void CPU::op_io() {
add_clocks(4);
}
uint8 CPU::op_read(uint16 addr) {
uint8 r = bus.read(addr);
add_clocks(4);
return r;
}
void CPU::op_write(uint16 addr, uint8 data) {
bus.write(addr, data);
add_clocks(4);
}
#endif

28
gameboy/cpu/timing/timing.cpp Executable file
View File

@ -0,0 +1,28 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
#ifdef CPU_CPP
#include "opcode.cpp"
void CPU::add_clocks(unsigned clocks) {
clock += clocks;
if(clock >= 456) scanline();
}
void CPU::scanline() {
clock -= 456;
status.lycounter++;
if(status.lycounter >= 154) frame();
}
void CPU::frame() {
status.lycounter -= 154;
scheduler.exit();
}
#endif

8
gameboy/cpu/timing/timing.hpp Executable file
View File

@ -0,0 +1,8 @@
void add_clocks(unsigned clocks);
void scanline();
void frame();
//opcode.cpp
void op_io();
uint8 op_read(uint16 addr);
void op_write(uint16 addr, uint8 data);

View File

@ -5,7 +5,7 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const char Version[] = "000";
static const char Version[] = "000.01";
}
}
@ -28,7 +28,23 @@ namespace GameBoy {
typedef uint32_t uint32;
typedef uint64_t uint64;
struct Processor {
cothread_t thread;
unsigned frequency;
int64 clock;
inline void create(void (*entrypoint_)(), unsigned frequency_) {
if(thread) co_delete(thread);
thread = co_create(65536 * sizeof(void*), entrypoint_);
frequency = frequency_;
clock = 0;
}
inline Processor() : thread(0) {}
};
#include <system/system.hpp>
#include <scheduler/scheduler.hpp>
#include <cartridge/cartridge.hpp>
#include <memory/memory.hpp>
#include <cpu/cpu.hpp>

33
gameboy/scheduler/scheduler.cpp Executable file
View File

@ -0,0 +1,33 @@
#include <gameboy.hpp>
#define SCHEDULER_CPP
namespace GameBoy {
Scheduler scheduler;
void Scheduler::enter() {
host_thread = co_active();
co_switch(active_thread);
}
void Scheduler::exit() {
active_thread = co_active();
co_switch(host_thread);
}
void Scheduler::swapto(Processor &p) {
active_thread = p.thread;
co_switch(active_thread);
}
void Scheduler::init() {
host_thread = co_active();
active_thread = cpu.thread;
}
Scheduler::Scheduler() {
host_thread = 0;
active_thread = 0;
}
}

13
gameboy/scheduler/scheduler.hpp Executable file
View File

@ -0,0 +1,13 @@
struct Scheduler {
cothread_t host_thread;
cothread_t active_thread;
void enter();
void exit();
void swapto(Processor&);
void init();
Scheduler();
};
extern Scheduler scheduler;

View File

@ -11,10 +11,16 @@ void System::init(Interface *interface_) {
void System::power() {
cpu.power();
scheduler.init();
}
void System::reset() {
cpu.reset();
scheduler.init();
}
void System::run() {
scheduler.enter();
}
}

View File

@ -5,6 +5,7 @@ public:
void init(Interface*);
void power();
void reset();
void run();
//private:
Interface *interface;

View File

@ -16,6 +16,10 @@ void Application::main(int argc, char **argv) {
while(quit == false) {
OS::run();
if(GameBoy::cartridge.loaded()) {
GameBoy::system.run();
}
}
}