mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
246d6aaf08
commit
e0a9f1cf2c
|
@ -1,10 +1,13 @@
|
||||||
gameboy_objects := libco
|
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)
|
objects += $(gameboy_objects)
|
||||||
|
|
||||||
obj/libco.o: libco/libco.c libco/*
|
obj/libco.o: libco/libco.c libco/*
|
||||||
|
|
||||||
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
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-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||||
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
|
obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/memory/)
|
||||||
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/)
|
||||||
|
|
|
@ -41,7 +41,6 @@ void Cartridge::load(uint8_t *data, unsigned size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
system.interface->message({ "Loaded:\n", info.name });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::unload() {
|
void Cartridge::unload() {
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
//register base class
|
||||||
//the idea here is to have all registers derive from a single 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,
|
//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;
|
bool z, n, h, c;
|
||||||
operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); }
|
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; }
|
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 {
|
struct Register16 : Register {
|
||||||
|
@ -75,5 +91,10 @@ struct Registers {
|
||||||
Register16 sp;
|
Register16 sp;
|
||||||
Register16 pc;
|
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) {}
|
Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {}
|
||||||
} r;
|
} r;
|
|
@ -1,18 +1,24 @@
|
||||||
//Sharp LR35902 @ 4.19MHz
|
|
||||||
|
|
||||||
#include <gameboy.hpp>
|
#include <gameboy.hpp>
|
||||||
|
|
||||||
#define CPU_CPP
|
#define CPU_CPP
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
|
|
||||||
|
#include "core/core.cpp"
|
||||||
|
#include "timing/timing.cpp"
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
|
|
||||||
|
void CPU::Main() {
|
||||||
|
cpu.main();
|
||||||
|
}
|
||||||
|
|
||||||
void CPU::main() {
|
void CPU::main() {
|
||||||
while(true) {
|
while(true) {
|
||||||
uint8 opcode = bus.read(r.pc++);
|
print(disassemble(r[PC]), "\n");
|
||||||
switch(opcode) {
|
|
||||||
case 0x00: break; //NOP
|
uint8 opcode = op_read(r[PC]++);
|
||||||
}
|
(this->*opcode_table[opcode])();
|
||||||
|
|
||||||
|
opcode_counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +27,20 @@ void CPU::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::reset() {
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
class CPU {
|
struct CPU : Processor {
|
||||||
public:
|
#include "core/core.hpp"
|
||||||
#include "registers.hpp"
|
#include "timing/timing.hpp"
|
||||||
|
|
||||||
|
struct Status {
|
||||||
|
unsigned lycounter;
|
||||||
|
} status;
|
||||||
|
|
||||||
|
static void Main();
|
||||||
void main();
|
void main();
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
CPU();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -5,7 +5,7 @@
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bgameboy";
|
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 uint32_t uint32;
|
||||||
typedef uint64_t uint64;
|
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 <system/system.hpp>
|
||||||
|
#include <scheduler/scheduler.hpp>
|
||||||
#include <cartridge/cartridge.hpp>
|
#include <cartridge/cartridge.hpp>
|
||||||
#include <memory/memory.hpp>
|
#include <memory/memory.hpp>
|
||||||
#include <cpu/cpu.hpp>
|
#include <cpu/cpu.hpp>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
@ -11,10 +11,16 @@ void System::init(Interface *interface_) {
|
||||||
|
|
||||||
void System::power() {
|
void System::power() {
|
||||||
cpu.power();
|
cpu.power();
|
||||||
|
scheduler.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::reset() {
|
void System::reset() {
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
|
scheduler.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::run() {
|
||||||
|
scheduler.enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ public:
|
||||||
void init(Interface*);
|
void init(Interface*);
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
void run();
|
||||||
|
|
||||||
//private:
|
//private:
|
||||||
Interface *interface;
|
Interface *interface;
|
||||||
|
|
|
@ -16,6 +16,10 @@ void Application::main(int argc, char **argv) {
|
||||||
|
|
||||||
while(quit == false) {
|
while(quit == false) {
|
||||||
OS::run();
|
OS::run();
|
||||||
|
|
||||||
|
if(GameBoy::cartridge.loaded()) {
|
||||||
|
GameBoy::system.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue