mirror of
https://github.com/tildearrow/furnace.git
synced 2024-12-31 20:11:29 +00:00
bind the game boy
TODO: sound
This commit is contained in:
parent
30692985dc
commit
c2b3d85f77
6 changed files with 223 additions and 201 deletions
|
@ -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
|
||||
|
|
|
@ -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
171
src/engine/platform/gb.cpp
Normal 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
39
src/engine/platform/gb.h
Normal 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
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue