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 += 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/)
|
||||
|
|
|
@ -41,7 +41,6 @@ void Cartridge::load(uint8_t *data, unsigned size) {
|
|||
}
|
||||
|
||||
loaded = true;
|
||||
system.interface->message({ "Loaded:\n", info.name });
|
||||
}
|
||||
|
||||
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
|
||||
//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;
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 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>
|
||||
|
|
|
@ -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() {
|
||||
cpu.power();
|
||||
scheduler.init();
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
cpu.reset();
|
||||
scheduler.init();
|
||||
}
|
||||
|
||||
void System::run() {
|
||||
scheduler.enter();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ public:
|
|||
void init(Interface*);
|
||||
void power();
|
||||
void reset();
|
||||
void run();
|
||||
|
||||
//private:
|
||||
Interface *interface;
|
||||
|
|
|
@ -16,6 +16,10 @@ void Application::main(int argc, char **argv) {
|
|||
|
||||
while(quit == false) {
|
||||
OS::run();
|
||||
|
||||
if(GameBoy::cartridge.loaded()) {
|
||||
GameBoy::system.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue