finally output envelopes

apparently emulating the timers was necessary after all...
This commit is contained in:
tildearrow 2021-05-27 05:06:43 -05:00
parent fff64acf09
commit c78cc8c676
7 changed files with 20 additions and 300 deletions

View file

@ -27,6 +27,7 @@ src/log.cpp
extern/Nuked-OPN2/ym3438.c extern/Nuked-OPN2/ym3438.c
src/engine/platform/sound/sn76496.cpp src/engine/platform/sound/sn76496.cpp
src/engine/platform/sound/gb/apu.c src/engine/platform/sound/gb/apu.c
src/engine/platform/sound/gb/timing.c
src/engine/blip_buf.c src/engine/blip_buf.c
src/engine/safeReader.cpp src/engine/safeReader.cpp

View file

@ -8,16 +8,9 @@
#define FREQ_BASE 7943.75f #define FREQ_BASE 7943.75f
void DivPlatformGB::acquire(int& l, int& r) { void DivPlatformGB::acquire(int& l, int& r) {
gb->apu.apu_cycles=4; GB_advance_cycles(gb,2);
GB_apu_run(gb); l=gb->apu_output.final_sample.left<<3;
l=(gb->apu_output.current_sample[0].left+ r=gb->apu_output.final_sample.right<<3;
gb->apu_output.current_sample[1].left+
gb->apu_output.current_sample[2].left+
gb->apu_output.current_sample[3].left)<<6;
r=(gb->apu_output.current_sample[0].right+
gb->apu_output.current_sample[1].right+
gb->apu_output.current_sample[2].right+
gb->apu_output.current_sample[3].right)<<6;
} }
void DivPlatformGB::tick() { void DivPlatformGB::tick() {

View file

@ -4,7 +4,6 @@
#include <assert.h> #include <assert.h>
#include "gb.h" #include "gb.h"
#define CGB 0
#define GB_CLOCK_RATE 0x400000 #define GB_CLOCK_RATE 0x400000
#define likely(x) __builtin_expect((x), 1) #define likely(x) __builtin_expect((x), 1)
@ -277,8 +276,7 @@ static void render(GB_gameboy_t *gb)
filtered_output.left = MAX(MIN(filtered_output.left + interference_bias, 0x7FFF), -0x8000); filtered_output.left = MAX(MIN(filtered_output.left + interference_bias, 0x7FFF), -0x8000);
filtered_output.right = MAX(MIN(filtered_output.right + interference_bias, 0x7FFF), -0x8000); filtered_output.right = MAX(MIN(filtered_output.right + interference_bias, 0x7FFF), -0x8000);
} }
assert(gb->apu_output.sample_callback); gb->apu_output.final_sample=filtered_output;
gb->apu_output.sample_callback(gb, &filtered_output);
} }
static void update_square_sample(GB_gameboy_t *gb, unsigned index) static void update_square_sample(GB_gameboy_t *gb, unsigned index)

View file

@ -5,10 +5,6 @@
#include <stddef.h> #include <stddef.h>
#include "gb_struct_def.h" #include "gb_struct_def.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Speed = 1 / Length (in seconds) */ /* Speed = 1 / Length (in seconds) */
#define DAC_DECAY_SPEED 20000 #define DAC_DECAY_SPEED 20000
#define DAC_ATTACK_SPEED 20000 #define DAC_ATTACK_SPEED 20000
@ -164,6 +160,8 @@ typedef struct {
GB_double_sample_t highpass_diff; GB_double_sample_t highpass_diff;
GB_sample_callback_t sample_callback; GB_sample_callback_t sample_callback;
GB_sample_t final_sample;
bool rate_set_in_clocks; bool rate_set_in_clocks;
double interference_volume; double interference_volume;
@ -186,8 +184,4 @@ void GB_apu_run(GB_gameboy_t *gb);
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb); void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
void GB_borrow_sgb_border(GB_gameboy_t *gb); void GB_borrow_sgb_border(GB_gameboy_t *gb);
#ifdef __cplusplus
}
#endif
#endif /* apu_h */ #endif /* apu_h */

View file

@ -6,6 +6,10 @@
#include <stdint.h> #include <stdint.h>
#include <time.h> #include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "gb_struct_def.h" #include "gb_struct_def.h"
#include "apu.h" #include "apu.h"
@ -13,6 +17,8 @@
#define GB_STRUCT_VERSION 13 #define GB_STRUCT_VERSION 13
#define CGB 0
#define GB_MODEL_FAMILY_MASK 0xF00 #define GB_MODEL_FAMILY_MASK 0xF00
#define GB_MODEL_DMG_FAMILY 0x000 #define GB_MODEL_DMG_FAMILY 0x000
#define GB_MODEL_MGB_FAMILY 0x100 #define GB_MODEL_MGB_FAMILY 0x100
@ -357,7 +363,9 @@ struct GB_gameboy_s {
bool stopped; bool stopped;
uint8_t io_registers[0x80]; uint8_t io_registers[0x80];
GB_UNIT(div);
uint16_t div_counter; uint16_t div_counter;
uint8_t tima_reload_state;
GB_apu_t apu; GB_apu_t apu;
GB_apu_output_t apu_output; GB_apu_output_t apu_output;
}; };
@ -382,4 +390,8 @@ typedef enum {
GB_DIRECT_ACCESS_IE, GB_DIRECT_ACCESS_IE,
} GB_direct_access_t; } GB_direct_access_t;
#ifdef __cplusplus
}
#endif
#endif /* GB_h */ #endif /* GB_h */

View file

@ -10,118 +10,10 @@
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128}; static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
#ifndef GB_DISABLE_TIMEKEEPING
static int64_t get_nanoseconds(void)
{
#ifndef _WIN32
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_usec) * 1000 + now.tv_sec * 1000000000L;
#else
FILETIME time;
GetSystemTimeAsFileTime(&time);
return (((int64_t)time.dwHighDateTime << 32) | time.dwLowDateTime) * 100L;
#endif
}
static void nsleep(uint64_t nanoseconds)
{
#ifndef _WIN32
struct timespec sleep = {0, nanoseconds};
nanosleep(&sleep, NULL);
#else
HANDLE timer;
LARGE_INTEGER time;
timer = CreateWaitableTimer(NULL, true, NULL);
time.QuadPart = -(nanoseconds / 100L);
SetWaitableTimer(timer, &time, 0, NULL, NULL, false);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
#endif
}
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
if (!gb->turbo_dont_skip) {
int64_t nanoseconds = get_nanoseconds();
if (nanoseconds <= gb->last_sync + (1000000000LL * LCDC_PERIOD / GB_get_clock_rate(gb))) {
return true;
}
gb->last_sync = nanoseconds;
}
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
if (gb->turbo) {
gb->cycles_since_last_sync = 0;
return;
}
/* Prevent syncing if not enough time has passed.*/
if (gb->cycles_since_last_sync < LCDC_PERIOD / 3) return;
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
int64_t nanoseconds = get_nanoseconds();
int64_t time_to_sleep = target_nanoseconds + gb->last_sync - nanoseconds;
if (time_to_sleep > 0 && time_to_sleep < LCDC_PERIOD * 1200000000LL / GB_get_clock_rate(gb)) { // +20% to be more forgiving
nsleep(time_to_sleep);
gb->last_sync += target_nanoseconds;
}
else {
if (time_to_sleep < 0 && -time_to_sleep < LCDC_PERIOD * 1200000000LL / GB_get_clock_rate(gb)) {
// We're running a bit too slow, but the difference is small enough,
// just skip this sync and let it even out
return;
}
gb->last_sync = nanoseconds;
}
gb->cycles_since_last_sync = 0;
if (gb->update_input_hint_callback) {
gb->update_input_hint_callback(gb);
}
}
#else
bool GB_timing_sync_turbo(GB_gameboy_t *gb)
{
return false;
}
void GB_timing_sync(GB_gameboy_t *gb)
{
}
#endif
#define IR_DECAY 31500 #define IR_DECAY 31500
#define IR_THRESHOLD 19900 #define IR_THRESHOLD 19900
#define IR_MAX IR_THRESHOLD * 2 + IR_DECAY #define IR_MAX IR_THRESHOLD * 2 + IR_DECAY
static void GB_ir_run(GB_gameboy_t *gb, uint32_t cycles)
{
if (gb->model == GB_MODEL_AGB) return;
if (gb->infrared_input || gb->cart_ir || (gb->io_registers[GB_IO_RP] & 1)) {
gb->ir_sensor += cycles;
if (gb->ir_sensor > IR_MAX) {
gb->ir_sensor = IR_MAX;
}
gb->effective_ir_input = gb->ir_sensor >= IR_THRESHOLD && gb->ir_sensor <= IR_THRESHOLD + IR_DECAY;
}
else {
if (gb->ir_sensor <= cycles) {
gb->ir_sensor = 0;
}
else {
gb->ir_sensor -= cycles;
}
gb->effective_ir_input = false;
}
}
static void advance_tima_state_machine(GB_gameboy_t *gb) static void advance_tima_state_machine(GB_gameboy_t *gb)
{ {
if (gb->tima_reload_state == GB_TIMA_RELOADED) { if (gb->tima_reload_state == GB_TIMA_RELOADED) {
@ -170,7 +62,7 @@ static void GB_set_internal_div_counter(GB_gameboy_t *gb, uint16_t value)
static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles) static void GB_timers_run(GB_gameboy_t *gb, uint8_t cycles)
{ {
if (gb->stopped) { if (gb->stopped) {
if (GB_is_cgb(gb)) { if (CGB) {
gb->apu.apu_cycles += 4 << !gb->cgb_double_speed; gb->apu.apu_cycles += 4 << !gb->cgb_double_speed;
} }
return; return;
@ -201,187 +93,19 @@ main:
} }
} }
static void advance_serial(GB_gameboy_t *gb, uint8_t cycles)
{
if (gb->printer.command_state || gb->printer.bits_received) {
gb->printer.idle_time += cycles;
}
if (gb->serial_length == 0) {
gb->serial_cycles += cycles;
return;
}
while (cycles > gb->serial_length) {
advance_serial(gb, gb->serial_length);
cycles -= gb->serial_length;
}
uint16_t previous_serial_cycles = gb->serial_cycles;
gb->serial_cycles += cycles;
if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) {
gb->serial_count++;
if (gb->serial_count == 8) {
gb->serial_length = 0;
gb->serial_count = 0;
gb->io_registers[GB_IO_SC] &= ~0x80;
gb->io_registers[GB_IO_IF] |= 8;
}
gb->io_registers[GB_IO_SB] <<= 1;
if (gb->serial_transfer_bit_end_callback) {
gb->io_registers[GB_IO_SB] |= gb->serial_transfer_bit_end_callback(gb);
}
else {
gb->io_registers[GB_IO_SB] |= 1;
}
if (gb->serial_length) {
/* Still more bits to send */
if (gb->serial_transfer_bit_start_callback) {
gb->serial_transfer_bit_start_callback(gb, gb->io_registers[GB_IO_SB] & 0x80);
}
}
}
return;
}
static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles)
{
if (gb->cartridge_type->mbc_type != GB_HUC3 && !gb->cartridge_type->has_rtc) return;
gb->rtc_cycles += cycles;
time_t current_time = 0;
switch (gb->rtc_mode) {
case GB_RTC_MODE_SYNC_TO_HOST:
// Sync in a 1/32s resolution
if (gb->rtc_cycles < GB_get_unmultiplied_clock_rate(gb) / 16) return;
gb->rtc_cycles -= GB_get_unmultiplied_clock_rate(gb) / 16;
current_time = time(NULL);
break;
case GB_RTC_MODE_ACCURATE:
if (gb->cartridge_type->mbc_type != GB_HUC3 && (gb->rtc_real.high & 0x40)) {
gb->rtc_cycles -= cycles;
return;
}
if (gb->rtc_cycles < GB_get_unmultiplied_clock_rate(gb) * 2) return;
gb->rtc_cycles -= GB_get_unmultiplied_clock_rate(gb) * 2;
current_time = gb->last_rtc_second + 1;
break;
}
if (gb->cartridge_type->mbc_type == GB_HUC3) {
while (gb->last_rtc_second / 60 < current_time / 60) {
gb->last_rtc_second += 60;
gb->huc3_minutes++;
if (gb->huc3_minutes == 60 * 24) {
gb->huc3_days++;
gb->huc3_minutes = 0;
}
}
return;
}
bool running = false;
if (gb->cartridge_type->mbc_type == GB_TPP1) {
running = gb->tpp1_mr4 & 0x4;
}
else {
running = (gb->rtc_real.high & 0x40) == 0;
}
if (running) { /* is timer running? */
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
gb->last_rtc_second += 60 * 60 * 24;
if (gb->cartridge_type->mbc_type == GB_TPP1) {
if (++gb->rtc_real.tpp1.weekday == 7) {
gb->rtc_real.tpp1.weekday = 0;
if (++gb->rtc_real.tpp1.weeks == 0) {
gb->tpp1_mr4 |= 8; /* Overflow bit */
}
}
}
else if (++gb->rtc_real.days == 0) {
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_real.seconds != 60) continue;
gb->rtc_real.seconds = 0;
if (++gb->rtc_real.minutes != 60) continue;
gb->rtc_real.minutes = 0;
if (gb->cartridge_type->mbc_type == GB_TPP1) {
if (++gb->rtc_real.tpp1.hours != 24) continue;
gb->rtc_real.tpp1.hours = 0;
if (++gb->rtc_real.tpp1.weekday != 7) continue;
gb->rtc_real.tpp1.weekday = 0;
if (++gb->rtc_real.tpp1.weeks == 0) {
gb->tpp1_mr4 |= 8; /* Overflow bit */
}
}
else {
if (++gb->rtc_real.hours != 24) continue;
gb->rtc_real.hours = 0;
if (++gb->rtc_real.days != 0) continue;
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
gb->rtc_real.high |= 0x80; /* Overflow bit */
}
gb->rtc_real.high ^= 1;
}
}
}
}
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{ {
gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF; // Sort of hacky, but too many cross-component interactions to do it right gb->apu.pcm_mask[0] = gb->apu.pcm_mask[1] = 0xFF; // Sort of hacky, but too many cross-component interactions to do it right
// Affected by speed boost
gb->dma_cycles += cycles;
GB_timers_run(gb, cycles); GB_timers_run(gb, cycles);
if (!gb->stopped) {
advance_serial(gb, cycles); // TODO: Verify what happens in STOP mode
}
gb->debugger_ticks += cycles;
if (!gb->cgb_double_speed) { if (!gb->cgb_double_speed) {
cycles <<= 1; cycles <<= 1;
} }
// Not affected by speed boost
if (gb->io_registers[GB_IO_LCDC] & 0x80) {
gb->double_speed_alignment += cycles;
}
gb->hdma_cycles += cycles;
gb->apu_output.sample_cycles += cycles; gb->apu_output.sample_cycles += cycles;
gb->cycles_since_last_sync += cycles;
gb->cycles_since_run += cycles;
gb->rumble_on_cycles += gb->rumble_strength & 3;
gb->rumble_off_cycles += (gb->rumble_strength & 3) ^ 3;
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
GB_dma_run(gb);
GB_hdma_run(gb);
}
GB_apu_run(gb); GB_apu_run(gb);
GB_display_run(gb, cycles);
GB_ir_run(gb, cycles);
GB_rtc_run(gb, cycles);
} }
/* /*

View file

@ -2,7 +2,6 @@
#define timing_h #define timing_h
#include "gb_struct_def.h" #include "gb_struct_def.h"
#ifdef GB_INTERNAL
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac); void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac);
bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */ bool GB_timing_sync_turbo(GB_gameboy_t *gb); /* Returns true if should skip frame */
@ -31,7 +30,6 @@ if ((gb)->unit##_cycles <= 0) {\
return;\ return;\
}\ }\
switch ((gb)->unit##_state) switch ((gb)->unit##_state)
#endif
#define GB_STATE(gb, unit, state) case state: goto unit##state #define GB_STATE(gb, unit, state) case state: goto unit##state