bind the game boy

TODO: sound
This commit is contained in:
tildearrow 2021-05-26 03:17:12 -05:00
parent 30692985dc
commit c2b3d85f77
6 changed files with 223 additions and 201 deletions

View file

@ -37,6 +37,7 @@ src/engine/platform/abstract.cpp
src/engine/platform/genesis.cpp
src/engine/platform/genesisext.cpp
src/engine/platform/sms.cpp
src/engine/platform/gb.cpp
src/engine/platform/dummy.cpp)
#imgui/imgui.cpp

View file

@ -5,6 +5,7 @@
#include "platform/genesis.h"
#include "platform/genesisext.h"
#include "platform/sms.h"
#include "platform/gb.h"
#include "platform/dummy.h"
#include <math.h>
#include <zlib.h>
@ -693,6 +694,9 @@ bool DivEngine::init() {
case DIV_SYSTEM_SMS:
dispatch=new DivPlatformSMS;
break;
case DIV_SYSTEM_GB:
dispatch=new DivPlatformGB;
break;
default:
dispatch=new DivPlatformDummy;
break;

171
src/engine/platform/gb.cpp Normal file
View file

@ -0,0 +1,171 @@
#include "gb.h"
#include "../engine.h"
#include <math.h>
void DivPlatformGB::acquire(int& l, int& r) {
GB_apu_run(gb);
l=gb->apu_output.summed_samples[0].left+
gb->apu_output.summed_samples[1].left+
gb->apu_output.summed_samples[2].left+
gb->apu_output.summed_samples[3].left;
r=gb->apu_output.summed_samples[0].right+
gb->apu_output.summed_samples[1].right+
gb->apu_output.summed_samples[2].right+
gb->apu_output.summed_samples[3].right;
}
void DivPlatformGB::tick() {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.hadVol) {
chan[i].outVol=(chan[i].vol*chan[i].std.vol)>>4;
//sn->write(0x90|(i<<5)|(15-(chan[i].outVol&15)));
}
if (chan[i].std.hadArp) {
if (chan[i].std.arpMode) {
chan[i].baseFreq=round(1712.0f/pow(2.0f,((float)(chan[i].std.arp)/12.0f)));
} else {
chan[i].baseFreq=round(1712.0f/pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f)));
}
chan[i].freqChanged=true;
}
if (chan[i].std.hadDuty) {
snNoiseMode=(snNoiseMode&2)|(chan[i].std.duty&1);
if (chan[i].std.duty<2) {
chan[3].freqChanged=false;
}
updateSNMode=true;
}
}
for (int i=0; i<3; i++) {
if (chan[i].freqChanged) {
chan[i].freq=(chan[i].baseFreq*(ONE_SEMITONE-chan[i].pitch))/ONE_SEMITONE;
if (chan[i].note>0x5d) chan[i].freq=0x01;
//sn->write(0x80|i<<5|(chan[i].freq&15));
//sn->write(chan[i].freq>>4);
chan[i].freqChanged=false;
}
}
if (chan[3].freqChanged || updateSNMode) {
updateSNMode=false;
chan[3].freq=(chan[3].baseFreq*(ONE_SEMITONE-chan[3].pitch))/ONE_SEMITONE;
if (chan[3].note>0x5d) chan[3].freq=0x01;
chan[3].freqChanged=false;
if (snNoiseMode&2) { // take period from channel 3
if (snNoiseMode&1) {
//sn->write(0xe7);
} else {
//sn->write(0xe3);
}
//sn->write(0xdf);
//sn->write(0xc0|(chan[3].freq&15));
//sn->write(chan[3].freq>>4);
} else { // 3 fixed values
unsigned char value;
if (chan[3].std.hadArp) {
if (chan[3].std.arpMode) {
value=chan[3].std.arp%12;
} else {
value=(chan[3].note+chan[3].std.arp)%12;
}
} else {
value=chan[3].note%12;
}
if (value<3) {
value=2-value;
//sn->write(0xe0|value|((snNoiseMode&1)<<2));
}
}
}
}
int DivPlatformGB::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON:
chan[c.chan].baseFreq=round(1712.0f/pow(2.0f,((float)c.value/12.0f)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
chan[c.chan].active=true;
//sn->write(0x90|c.chan<<5|(15-(chan[c.chan].vol&15)));
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
//sn->write(0x9f|c.chan<<5);
chan[c.chan].std.init(NULL);
break;
case DIV_CMD_INSTRUMENT:
chan[c.chan].ins=c.value;
//chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.hasVol) {
chan[c.chan].outVol=c.value;
}
//sn->write(0x90|c.chan<<5|(15-(chan[c.chan].vol&15)));
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.hasVol) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=round(1712.0f/pow(2.0f,((float)c.value2/12.0f)));
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) return 2;
break;
}
case DIV_CMD_STD_NOISE_MODE:
snNoiseMode=(c.value&1)|((c.value&16)>>3);
updateSNMode=true;
break;
case DIV_CMD_LEGATO:
chan[c.chan].baseFreq=round(1712.0f/pow(2.0f,((float)c.value/12.0f)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
case DIV_CMD_PRE_PORTA:
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
break;
case DIV_CMD_GET_VOLMAX:
return 15;
break;
default:
break;
}
return 1;
}
int DivPlatformGB::init(DivEngine* p, int channels, int sugRate) {
parent=p;
rate=sugRate; // TODO: use blip_buf
gb=new GB_gameboy_t;
memset(gb,0,sizeof(GB_gameboy_t));
GB_apu_init(gb);
GB_set_sample_rate(gb,rate);
snNoiseMode=3;
updateSNMode=false;
return 4;
}

39
src/engine/platform/gb.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef _GB_H
#define _GB_H
#include "../dispatch.h"
#include "../macroInt.h"
#include "sound/gb/gb.h"
class DivPlatformGB: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch;
unsigned char ins, note;
bool active, insChanged, freqChanged, keyOn, keyOff;
signed char vol, outVol;
DivMacroInt std;
Channel():
freq(0),
baseFreq(0),
pitch(0),
ins(-1),
note(0),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
vol(15) {}
};
Channel chan[4];
unsigned char snNoiseMode;
bool updateSNMode;
GB_gameboy_t* gb;
public:
void acquire(int& l, int& r);
int dispatch(DivCommand c);
void tick();
int init(DivEngine* parent, int channels, int sugRate);
};
#endif

View file

@ -5,6 +5,9 @@
#include <stddef.h>
#include "gb_struct_def.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000
@ -183,4 +186,8 @@ void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
void GB_borrow_sgb_border(GB_gameboy_t *gb);
#ifdef __cplusplus
}
#endif
#endif /* apu_h */

View file

@ -346,219 +346,19 @@ typedef struct {
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
bit platforms. */
/* minimal GB_gameboy_s struct with just the APU */
struct GB_gameboy_s {
/* Registers */
uint16_t pc;
union {
uint16_t registers[GB_REGISTERS_16_BIT];
struct {
uint16_t af,
bc,
de,
hl,
sp;
};
struct {
#ifdef GB_BIG_ENDIAN
uint8_t a, f,
b, c,
d, e,
h, l;
#else
uint8_t f, a,
c, b,
e, d,
l, h;
#endif
};
};
uint8_t ime;
uint8_t interrupt_enable;
uint8_t cgb_ram_bank;
/* CPU and General Hardware Flags*/
GB_model_t model;
bool cgb_mode;
bool cgb_double_speed;
bool halted;
bool stopped;
bool boot_rom_finished;
bool ime_toggle; /* ei has delayed a effect.*/
bool halt_bug;
bool just_halted;
/* Misc state */
bool infrared_input;
uint8_t extra_oam[0xff00 - 0xfea0];
uint32_t ram_size; // Different between CGB and DMG
int32_t ir_sensor;
bool effective_ir_input;
/* DMA and HDMA */
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
int16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left;
uint8_t dma_current_dest;
uint16_t dma_current_src;
int16_t dma_cycles;
bool is_dma_restarting;
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
bool hdma_starting;
/* MBC */
uint16_t mbc_rom_bank;
uint8_t mbc_ram_bank;
uint32_t mbc_ram_size;
bool mbc_ram_enable;
union {
struct {
uint8_t bank_low:5;
uint8_t bank_high:2;
uint8_t mode:1;
} mbc1;
struct {
uint8_t rom_bank:4;
} mbc2;
struct {
uint8_t rom_bank:8;
uint8_t ram_bank:3;
} mbc3;
struct {
uint8_t rom_bank_low;
uint8_t rom_bank_high:1;
uint8_t ram_bank:4;
} mbc5;
struct {
uint8_t bank_low:6;
uint8_t bank_high:3;
bool mode:1;
bool ir_mode:1;
} huc1;
struct {
uint8_t rom_bank:7;
uint8_t padding:1;
uint8_t ram_bank:4;
} huc3;
};
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
uint8_t rumble_strength;
bool cart_ir;
// TODO: move to huc3/mbc3/tpp1 struct when breaking save compat
uint8_t huc3_mode;
uint8_t huc3_access_index;
uint16_t huc3_minutes, huc3_days;
uint16_t huc3_alarm_minutes, huc3_alarm_days;
bool huc3_alarm_enabled;
uint8_t huc3_read;
uint8_t huc3_access_flags;
bool mbc3_rtc_mapped;
uint16_t tpp1_rom_bank;
uint8_t tpp1_ram_bank;
uint8_t tpp1_mode;
/* HRAM and HW Registers */
uint8_t hram[0xFFFF - 0xFF80];
uint8_t io_registers[0x80];
// oops
uint16_t div_counter;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles;
uint16_t serial_length;
uint8_t double_speed_alignment;
uint8_t serial_count;
GB_apu_t apu;
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
/* This data is reserved on reset and must come last in the struct */
/* ROM */
unsigned pending_cycles;
/* Various RAMs */
uint8_t *ram;
uint8_t *vram;
uint8_t *mbc_ram;
/* I/O */
uint32_t *screen;
uint32_t background_palettes_rgb[0x20];
uint32_t sprite_palettes_rgb[0x20];
const GB_palette_t *dmg_palette;
double light_temperature;
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
GB_rtc_mode_t rtc_mode;
/* Audio */
GB_apu_output_t apu_output;
/*** Debugger ***/
volatile bool debug_stopped, debug_disable;
bool debug_fin_command, debug_next_command;
/* SLD (Todo: merge with backtrace) */
bool stack_leak_detection;
signed debug_call_depth;
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
uint16_t addr_for_call_depth[0x200];
/* Backtrace */
unsigned backtrace_size;
uint16_t backtrace_sps[0x200];
struct {
uint16_t bank;
uint16_t addr;
} backtrace_returns[0x200];
/* Ticks command */
uint64_t debugger_ticks;
/* Undo */
uint8_t *undo_state;
const char *undo_label;
/* Rewind */
#define GB_REWIND_FRAMES_PER_KEY 255
size_t rewind_buffer_length;
struct {
uint8_t *key_state;
uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY];
unsigned pos;
} *rewind_sequences; // lasts about 4 seconds
size_t rewind_pos;
/* Misc */
bool turbo;
bool turbo_dont_skip;
bool disable_rendering;
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier;
/* Temporary state */
bool wx_just_changed;
bool tile_sel_glitch;
};
#ifndef __printflike