Redesign and reimplement the audio API, let the frontends handle more stuff. Probably affects #161
This commit is contained in:
parent
083b4a2970
commit
e268efefef
@ -55,6 +55,12 @@ enum model {
|
|||||||
|
|
||||||
bool rewind;
|
bool rewind;
|
||||||
bool modelsChanging;
|
bool modelsChanging;
|
||||||
|
|
||||||
|
NSCondition *audioLock;
|
||||||
|
GB_sample_t *audioBuffer;
|
||||||
|
size_t audioBufferSize;
|
||||||
|
size_t audioBufferPosition;
|
||||||
|
size_t audioBufferNeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property GBAudioClient *audioClient;
|
@property GBAudioClient *audioClient;
|
||||||
@ -67,6 +73,7 @@ enum model {
|
|||||||
- (void) printImage:(uint32_t *)image height:(unsigned) height
|
- (void) printImage:(uint32_t *)image height:(unsigned) height
|
||||||
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
|
||||||
exposure:(unsigned) exposure;
|
exposure:(unsigned) exposure;
|
||||||
|
- (void) gotNewSample:(GB_sample_t *)sample;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb)
|
static void vblank(GB_gameboy_t *gb)
|
||||||
@ -118,6 +125,12 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
|
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||||
|
{
|
||||||
|
Document *self = (__bridge Document *)GB_get_user_data(gb);
|
||||||
|
[self gotNewSample:sample];
|
||||||
|
}
|
||||||
|
|
||||||
@implementation Document
|
@implementation Document
|
||||||
{
|
{
|
||||||
GB_gameboy_t gb;
|
GB_gameboy_t gb;
|
||||||
@ -133,6 +146,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
|
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
|
||||||
debugger_input_queue = [[NSMutableArray alloc] init];
|
debugger_input_queue = [[NSMutableArray alloc] init];
|
||||||
console_output_lock = [[NSRecursiveLock alloc] init];
|
console_output_lock = [[NSRecursiveLock alloc] init];
|
||||||
|
audioLock = [[NSCondition alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -184,6 +198,7 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
|
||||||
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
|
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
|
||||||
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
|
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
|
||||||
|
GB_apu_set_sample_callback(&gb, audioCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) vblank
|
- (void) vblank
|
||||||
@ -201,13 +216,57 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)gotNewSample:(GB_sample_t *)sample
|
||||||
|
{
|
||||||
|
[audioLock lock];
|
||||||
|
if (self.audioClient.isPlaying) {
|
||||||
|
if (audioBufferPosition == audioBufferSize) {
|
||||||
|
if (audioBufferSize >= 0x4000) {
|
||||||
|
audioBufferPosition = 0;
|
||||||
|
[audioLock unlock];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioBufferSize == 0) {
|
||||||
|
audioBufferSize = 512;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
audioBufferSize += audioBufferSize >> 2;
|
||||||
|
}
|
||||||
|
audioBuffer = realloc(audioBuffer, sizeof(*sample) * audioBufferSize);
|
||||||
|
}
|
||||||
|
audioBuffer[audioBufferPosition++] = *sample;
|
||||||
|
}
|
||||||
|
if (audioBufferPosition == audioBufferNeeded) {
|
||||||
|
[audioLock signal];
|
||||||
|
audioBufferNeeded = 0;
|
||||||
|
}
|
||||||
|
[audioLock unlock];
|
||||||
|
}
|
||||||
|
|
||||||
- (void) run
|
- (void) run
|
||||||
{
|
{
|
||||||
running = true;
|
running = true;
|
||||||
GB_set_pixels_output(&gb, self.view.pixels);
|
GB_set_pixels_output(&gb, self.view.pixels);
|
||||||
GB_set_sample_rate(&gb, 96000);
|
GB_set_sample_rate(&gb, 96000);
|
||||||
self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
|
self.audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
|
||||||
GB_apu_copy_buffer(&gb, buffer, nFrames);
|
[audioLock lock];
|
||||||
|
|
||||||
|
if (audioBufferPosition < nFrames) {
|
||||||
|
audioBufferNeeded = nFrames;
|
||||||
|
[audioLock wait];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioBufferPosition >= nFrames && audioBufferPosition < nFrames + 4800) {
|
||||||
|
memcpy(buffer, audioBuffer, nFrames * sizeof(*buffer));
|
||||||
|
memmove(audioBuffer, audioBuffer + nFrames, (audioBufferPosition - nFrames) * sizeof(*buffer));
|
||||||
|
audioBufferPosition = audioBufferPosition - nFrames;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(buffer, audioBuffer + (audioBufferPosition - nFrames), nFrames * sizeof(*buffer));
|
||||||
|
audioBufferPosition = 0;
|
||||||
|
}
|
||||||
|
[audioLock unlock];
|
||||||
} andSampleRate:96000];
|
} andSampleRate:96000];
|
||||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"Mute"]) {
|
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"Mute"]) {
|
||||||
[self.audioClient start];
|
[self.audioClient start];
|
||||||
@ -227,6 +286,11 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[hex_timer invalidate];
|
[hex_timer invalidate];
|
||||||
|
[audioLock lock];
|
||||||
|
memset(audioBuffer, 0, (audioBufferSize - audioBufferPosition) * sizeof(*audioBuffer));
|
||||||
|
audioBufferPosition = audioBufferNeeded;
|
||||||
|
[audioLock signal];
|
||||||
|
[audioLock unlock];
|
||||||
[self.audioClient stop];
|
[self.audioClient stop];
|
||||||
self.audioClient = nil;
|
self.audioClient = nil;
|
||||||
self.view.mouseHidingEnabled = NO;
|
self.view.mouseHidingEnabled = NO;
|
||||||
@ -329,6 +393,9 @@ static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
|
|||||||
if (cameraImage) {
|
if (cameraImage) {
|
||||||
CVBufferRelease(cameraImage);
|
CVBufferRelease(cameraImage);
|
||||||
}
|
}
|
||||||
|
if (audioBuffer) {
|
||||||
|
free(audioBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
|
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
|
||||||
|
84
Core/apu.c
84
Core/apu.c
@ -1,6 +1,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
#define likely(x) __builtin_expect((x), 1)
|
#define likely(x) __builtin_expect((x), 1)
|
||||||
@ -117,7 +118,7 @@ static double smooth(double x)
|
|||||||
return 3*x*x - 2*x*x*x;
|
return 3*x*x - 2*x*x*x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
|
static void render(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
GB_sample_t output = {0,0};
|
GB_sample_t output = {0,0};
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(gb->apu_output.last_update[i] == 0 || no_downsampling)) {
|
if (likely(gb->apu_output.last_update[i] == 0)) {
|
||||||
output.left += gb->apu_output.current_sample[i].left * multiplier;
|
output.left += gb->apu_output.current_sample[i].left * multiplier;
|
||||||
output.right += gb->apu_output.current_sample[i].right * multiplier;
|
output.right += gb->apu_output.current_sample[i].right * multiplier;
|
||||||
}
|
}
|
||||||
@ -205,17 +206,9 @@ static void render(GB_gameboy_t *gb, bool no_downsampling, GB_sample_t *dest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (dest) {
|
|
||||||
*dest = filtered_output;
|
assert(gb->apu_output.sample_callback);
|
||||||
return;
|
gb->apu_output.sample_callback(gb, &filtered_output);
|
||||||
}
|
|
||||||
|
|
||||||
while (gb->apu_output.copy_in_progress);
|
|
||||||
while (!__sync_bool_compare_and_swap(&gb->apu_output.lock, false, true));
|
|
||||||
if (gb->apu_output.buffer_position < gb->apu_output.buffer_size) {
|
|
||||||
gb->apu_output.buffer[gb->apu_output.buffer_position++] = filtered_output;
|
|
||||||
}
|
|
||||||
gb->apu_output.lock = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
|
static uint16_t new_sweep_sample_legnth(GB_gameboy_t *gb)
|
||||||
@ -504,56 +497,12 @@ void GB_apu_run(GB_gameboy_t *gb)
|
|||||||
if (gb->apu_output.sample_rate) {
|
if (gb->apu_output.sample_rate) {
|
||||||
gb->apu_output.cycles_since_render += cycles;
|
gb->apu_output.cycles_since_render += cycles;
|
||||||
|
|
||||||
if (gb->apu_output.sample_cycles > gb->apu_output.cycles_per_sample) {
|
if (gb->apu_output.sample_cycles >= gb->apu_output.cycles_per_sample) {
|
||||||
gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample;
|
gb->apu_output.sample_cycles -= gb->apu_output.cycles_per_sample;
|
||||||
render(gb, false, NULL);
|
render(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
|
||||||
{
|
|
||||||
if (gb->sgb) {
|
|
||||||
if (GB_sgb_render_jingle(gb, dest, count)) return;
|
|
||||||
}
|
|
||||||
gb->apu_output.copy_in_progress = true;
|
|
||||||
|
|
||||||
/* TODO: Rewrite this as a proper cyclic buffer. This is a workaround to avoid a very rare crashing race condition */
|
|
||||||
size_t buffer_position = gb->apu_output.buffer_position;
|
|
||||||
|
|
||||||
if (!gb->apu_output.stream_started) {
|
|
||||||
// Intentionally fail the first copy to sync the stream with the Gameboy.
|
|
||||||
gb->apu_output.stream_started = true;
|
|
||||||
gb->apu_output.buffer_position = 0;
|
|
||||||
buffer_position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > buffer_position) {
|
|
||||||
// GB_log(gb, "Audio underflow: %d\n", count - gb->apu_output.buffer_position);
|
|
||||||
GB_sample_t output;
|
|
||||||
render(gb, true, &output);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < count - buffer_position; i++) {
|
|
||||||
dest[buffer_position + i] = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer_position) {
|
|
||||||
if (gb->apu_output.buffer_size + (count - buffer_position) < count * 3) {
|
|
||||||
gb->apu_output.buffer_size += count - buffer_position;
|
|
||||||
gb->apu_output.buffer = realloc(gb->apu_output.buffer,
|
|
||||||
gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer));
|
|
||||||
gb->apu_output.stream_started = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count = buffer_position;
|
|
||||||
}
|
|
||||||
memcpy(dest, gb->apu_output.buffer, count * sizeof(*gb->apu_output.buffer));
|
|
||||||
memmove(gb->apu_output.buffer, gb->apu_output.buffer + count, (buffer_position - count) * sizeof(*gb->apu_output.buffer));
|
|
||||||
gb->apu_output.buffer_position -= count;
|
|
||||||
|
|
||||||
gb->apu_output.copy_in_progress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_apu_init(GB_gameboy_t *gb)
|
void GB_apu_init(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
memset(&gb->apu, 0, sizeof(gb->apu));
|
memset(&gb->apu, 0, sizeof(gb->apu));
|
||||||
@ -1009,26 +958,21 @@ void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value)
|
|||||||
gb->io_registers[reg] = value;
|
gb->io_registers[reg] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb)
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate)
|
||||||
{
|
{
|
||||||
return gb->apu_output.buffer_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate)
|
|
||||||
{
|
|
||||||
if (gb->apu_output.buffer) {
|
|
||||||
free(gb->apu_output.buffer);
|
|
||||||
}
|
|
||||||
gb->apu_output.buffer_size = sample_rate / 25; // 40ms delay
|
|
||||||
gb->apu_output.buffer = malloc(gb->apu_output.buffer_size * sizeof(*gb->apu_output.buffer));
|
|
||||||
gb->apu_output.sample_rate = sample_rate;
|
gb->apu_output.sample_rate = sample_rate;
|
||||||
gb->apu_output.buffer_position = 0;
|
|
||||||
if (sample_rate) {
|
if (sample_rate) {
|
||||||
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
gb->apu_output.highpass_rate = pow(0.999958, GB_get_clock_rate(gb) / (double)sample_rate);
|
||||||
}
|
}
|
||||||
GB_apu_update_cycles_per_sample(gb);
|
GB_apu_update_cycles_per_sample(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback)
|
||||||
|
{
|
||||||
|
gb->apu_output.sample_callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
|
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode)
|
||||||
{
|
{
|
||||||
gb->apu_output.highpass_mode = mode;
|
gb->apu_output.highpass_mode = mode;
|
||||||
|
18
Core/apu.h
18
Core/apu.h
@ -46,6 +46,8 @@ enum GB_CHANNELS {
|
|||||||
GB_N_CHANNELS
|
GB_N_CHANNELS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool global_enable;
|
bool global_enable;
|
||||||
@ -126,14 +128,6 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned sample_rate;
|
unsigned sample_rate;
|
||||||
|
|
||||||
GB_sample_t *buffer;
|
|
||||||
size_t buffer_size;
|
|
||||||
size_t buffer_position;
|
|
||||||
|
|
||||||
bool stream_started; /* detects first copy request to minimize lag */
|
|
||||||
volatile bool copy_in_progress;
|
|
||||||
volatile bool lock;
|
|
||||||
|
|
||||||
double sample_cycles; // In 8 MHz units
|
double sample_cycles; // In 8 MHz units
|
||||||
double cycles_per_sample;
|
double cycles_per_sample;
|
||||||
|
|
||||||
@ -147,13 +141,13 @@ typedef struct {
|
|||||||
GB_highpass_mode_t highpass_mode;
|
GB_highpass_mode_t highpass_mode;
|
||||||
double highpass_rate;
|
double highpass_rate;
|
||||||
GB_double_sample_t highpass_diff;
|
GB_double_sample_t highpass_diff;
|
||||||
|
|
||||||
|
GB_sample_callback_t sample_callback;
|
||||||
} GB_apu_output_t;
|
} GB_apu_output_t;
|
||||||
|
|
||||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate);
|
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
||||||
void GB_apu_copy_buffer(GB_gameboy_t *gb, GB_sample_t *dest, size_t count);
|
|
||||||
size_t GB_apu_get_current_buffer_length(GB_gameboy_t *gb);
|
|
||||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
||||||
|
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
||||||
#ifdef GB_INTERNAL
|
#ifdef GB_INTERNAL
|
||||||
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
||||||
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
||||||
|
@ -455,12 +455,12 @@ static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string,
|
|||||||
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
|
case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GB_log(gb, "Unknown register: %.*s\n", (unsigned int) length, string);
|
GB_log(gb, "Unknown register: %.*s\n", (unsigned) length, string);
|
||||||
*error = true;
|
*error = true;
|
||||||
return (lvalue_t){0,};
|
return (lvalue_t){0,};
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned int) length, string);
|
GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned) length, string);
|
||||||
*error = true;
|
*error = true;
|
||||||
return (lvalue_t){0,};
|
return (lvalue_t){0,};
|
||||||
}
|
}
|
||||||
@ -564,8 +564,8 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
// Search for lowest priority operator
|
// Search for lowest priority operator
|
||||||
signed int depth = 0;
|
signed int depth = 0;
|
||||||
unsigned int operator_index = -1;
|
unsigned operator_index = -1;
|
||||||
unsigned int operator_pos = 0;
|
unsigned operator_pos = 0;
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
if (string[i] == '(') depth++;
|
if (string[i] == '(') depth++;
|
||||||
else if (string[i] == ')') depth--;
|
else if (string[i] == ')') depth--;
|
||||||
@ -593,7 +593,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (operator_index != -1) {
|
if (operator_index != -1) {
|
||||||
unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string));
|
unsigned right_start = (unsigned)(operator_pos + strlen(operators[operator_index].string));
|
||||||
value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value);
|
value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value);
|
||||||
if (*error) goto exit;
|
if (*error) goto exit;
|
||||||
if (operators[operator_index].lvalue_operator) {
|
if (operators[operator_index].lvalue_operator) {
|
||||||
@ -661,7 +661,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned int) length, string);
|
GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned) length, string);
|
||||||
*error = true;
|
*error = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -675,7 +675,7 @@ value_t debugger_evaluate(GB_gameboy_t *gb, const char *string,
|
|||||||
}
|
}
|
||||||
uint16_t literal = (uint16_t) (strtol(string, &end, base));
|
uint16_t literal = (uint16_t) (strtol(string, &end, base));
|
||||||
if (end != string + length) {
|
if (end != string + length) {
|
||||||
GB_log(gb, "Failed to parse: %.*s\n", (unsigned int) length, string);
|
GB_log(gb, "Failed to parse: %.*s\n", (unsigned) length, string);
|
||||||
*error = true;
|
*error = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -880,13 +880,13 @@ static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||||||
condition += strlen(" if ");
|
condition += strlen(" if ");
|
||||||
/* Verify condition is sane (Todo: This might have side effects!) */
|
/* Verify condition is sane (Todo: This might have side effects!) */
|
||||||
bool error;
|
bool error;
|
||||||
debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, NULL, NULL);
|
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, NULL, NULL);
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint32_t key = BP_KEY(result);
|
uint32_t key = BP_KEY(result);
|
||||||
|
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
@ -949,7 +949,7 @@ static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const deb
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint32_t key = BP_KEY(result);
|
uint32_t key = BP_KEY(result);
|
||||||
|
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
@ -1065,13 +1065,13 @@ print_usage:
|
|||||||
/* To make $new and $old legal */
|
/* To make $new and $old legal */
|
||||||
uint16_t dummy = 0;
|
uint16_t dummy = 0;
|
||||||
uint8_t dummy2 = 0;
|
uint8_t dummy2 = 0;
|
||||||
debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, &dummy, &dummy2);
|
debugger_evaluate(gb, condition, (unsigned)strlen(condition), &error, &dummy, &dummy2);
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint32_t key = WP_KEY(result);
|
uint32_t key = WP_KEY(result);
|
||||||
|
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
@ -1132,7 +1132,7 @@ static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint32_t key = WP_KEY(result);
|
uint32_t key = WP_KEY(result);
|
||||||
|
|
||||||
if (error) return true;
|
if (error) return true;
|
||||||
@ -1234,7 +1234,7 @@ static bool _should_break(GB_gameboy_t *gb, value_t addr, bool jump_to)
|
|||||||
}
|
}
|
||||||
bool error;
|
bool error;
|
||||||
bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition,
|
bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition,
|
||||||
(unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value;
|
(unsigned)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value;
|
||||||
if (error) {
|
if (error) {
|
||||||
/* Should never happen */
|
/* Should never happen */
|
||||||
GB_log(gb, "An internal error has occured\n");
|
GB_log(gb, "An internal error has occured\n");
|
||||||
@ -1273,7 +1273,7 @@ static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debu
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t result = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
switch (modifiers[0]) {
|
switch (modifiers[0]) {
|
||||||
case 'a':
|
case 'a':
|
||||||
@ -1319,7 +1319,7 @@ static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const de
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint16_t count = 32;
|
uint16_t count = 32;
|
||||||
|
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
@ -1371,7 +1371,7 @@ static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool error;
|
bool error;
|
||||||
value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL);
|
value_t addr = debugger_evaluate(gb, arguments, (unsigned)strlen(arguments), &error, NULL, NULL);
|
||||||
uint16_t count = 5;
|
uint16_t count = 5;
|
||||||
|
|
||||||
if (modifiers) {
|
if (modifiers) {
|
||||||
@ -1468,7 +1468,7 @@ static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true));
|
GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true));
|
||||||
for (unsigned int i = gb->backtrace_size; i--;) {
|
for (unsigned i = gb->backtrace_size; i--;) {
|
||||||
GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true));
|
GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1937,7 +1937,7 @@ static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, u
|
|||||||
}
|
}
|
||||||
bool error;
|
bool error;
|
||||||
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
|
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
|
||||||
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value;
|
(unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value;
|
||||||
if (error) {
|
if (error) {
|
||||||
/* Should never happen */
|
/* Should never happen */
|
||||||
GB_log(gb, "An internal error has occured\n");
|
GB_log(gb, "An internal error has occured\n");
|
||||||
@ -1982,7 +1982,7 @@ static bool _GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, value_t addr)
|
|||||||
}
|
}
|
||||||
bool error;
|
bool error;
|
||||||
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
|
bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition,
|
||||||
(unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value;
|
(unsigned)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value;
|
||||||
if (error) {
|
if (error) {
|
||||||
/* Should never happen */
|
/* Should never happen */
|
||||||
GB_log(gb, "An internal error has occured\n");
|
GB_log(gb, "An internal error has occured\n");
|
||||||
@ -2169,7 +2169,7 @@ void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path)
|
|||||||
}
|
}
|
||||||
if (length == 0) continue;
|
if (length == 0) continue;
|
||||||
|
|
||||||
unsigned int bank, address;
|
unsigned bank, address;
|
||||||
char symbol[length];
|
char symbol[length];
|
||||||
|
|
||||||
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {
|
if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) {
|
||||||
|
@ -135,9 +135,6 @@ void GB_free(GB_gameboy_t *gb)
|
|||||||
if (gb->rom) {
|
if (gb->rom) {
|
||||||
free(gb->rom);
|
free(gb->rom);
|
||||||
}
|
}
|
||||||
if (gb->apu_output.buffer) {
|
|
||||||
free(gb->apu_output.buffer);
|
|
||||||
}
|
|
||||||
if (gb->breakpoints) {
|
if (gb->breakpoints) {
|
||||||
free(gb->breakpoints);
|
free(gb->breakpoints);
|
||||||
}
|
}
|
||||||
|
@ -555,7 +555,7 @@ struct GB_gameboy_internal_s {
|
|||||||
uint16_t addr_for_call_depth[0x200];
|
uint16_t addr_for_call_depth[0x200];
|
||||||
|
|
||||||
/* Backtrace */
|
/* Backtrace */
|
||||||
unsigned int backtrace_size;
|
unsigned backtrace_size;
|
||||||
uint16_t backtrace_sps[0x200];
|
uint16_t backtrace_sps[0x200];
|
||||||
struct {
|
struct {
|
||||||
uint16_t bank;
|
uint16_t bank;
|
||||||
|
@ -126,13 +126,13 @@ static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr)
|
|||||||
if (!gb->rom_size) {
|
if (!gb->rom_size) {
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
|
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000;
|
||||||
return gb->rom[effective_address & (gb->rom_size - 1)];
|
return gb->rom[effective_address & (gb->rom_size - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr)
|
static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
|
unsigned effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000;
|
||||||
return gb->rom[effective_address & (gb->rom_size - 1)];
|
return gb->rom[effective_address & (gb->rom_size - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
Core/sgb.c
25
Core/sgb.c
@ -1,6 +1,7 @@
|
|||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#define INTRO_ANIMATION_LENGTH 200
|
#define INTRO_ANIMATION_LENGTH 200
|
||||||
|
|
||||||
@ -461,8 +462,13 @@ static void render_boot_animation (GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void render_jingle(GB_gameboy_t *gb, size_t count);
|
||||||
void GB_sgb_render(GB_gameboy_t *gb)
|
void GB_sgb_render(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
|
if (gb->apu_output.sample_rate) {
|
||||||
|
render_jingle(gb, gb->apu_output.sample_rate / (GB_get_clock_rate(gb) / LCDC_PERIOD));
|
||||||
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
if (gb->sgb->intro_animation < INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
|
||||||
|
|
||||||
if (!gb->screen || !gb->rgb_encode_callback) return;
|
if (!gb->screen || !gb->rgb_encode_callback) return;
|
||||||
@ -689,7 +695,7 @@ static double random_double(void)
|
|||||||
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
|
return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
static void render_jingle(GB_gameboy_t *gb, size_t count)
|
||||||
{
|
{
|
||||||
const double frequencies[7] = {
|
const double frequencies[7] = {
|
||||||
466.16, // Bb4
|
466.16, // Bb4
|
||||||
@ -701,15 +707,17 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
|||||||
1567.98, // G6
|
1567.98, // G6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert(gb->apu_output.sample_callback);
|
||||||
|
|
||||||
if (gb->sgb->intro_animation < 0) {
|
if (gb->sgb->intro_animation < 0) {
|
||||||
|
GB_sample_t sample = {0, 0};
|
||||||
for (unsigned i = 0; i < count; i++) {
|
for (unsigned i = 0; i < count; i++) {
|
||||||
dest->left = dest->right = 0;
|
gb->apu_output.sample_callback(gb, &sample);
|
||||||
dest++;
|
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return false;
|
if (gb->sgb->intro_animation >= INTRO_ANIMATION_LENGTH) return;
|
||||||
|
|
||||||
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
|
signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
|
||||||
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
|
double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
|
||||||
@ -718,6 +726,7 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
|||||||
sweep_cutoff_ratio = 1;
|
sweep_cutoff_ratio = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GB_sample_t stereo;
|
||||||
for (unsigned i = 0; i < count; i++) {
|
for (unsigned i = 0; i < count; i++) {
|
||||||
double sample = 0;
|
double sample = 0;
|
||||||
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
|
for (signed f = 0; f < 7 && f < jingle_stage; f++) {
|
||||||
@ -738,10 +747,10 @@ bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count)
|
|||||||
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
|
sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
dest->left = dest->right = sample * 0x7000;
|
stereo.left = stereo.right = sample * 0x7000;
|
||||||
dest++;
|
gb->apu_output.sample_callback(gb, &stereo);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ struct GB_sgb_s {
|
|||||||
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
void GB_sgb_write(GB_gameboy_t *gb, uint8_t value);
|
||||||
void GB_sgb_render(GB_gameboy_t *gb);
|
void GB_sgb_render(GB_gameboy_t *gb);
|
||||||
void GB_sgb_load_default_data(GB_gameboy_t *gb);
|
void GB_sgb_load_default_data(GB_gameboy_t *gb);
|
||||||
bool GB_sgb_render_jingle(GB_gameboy_t *gb, GB_sample_t *dest, size_t count);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const unsigned int GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
|
static const unsigned GB_TAC_TRIGGER_BITS[] = {512, 8, 32, 128};
|
||||||
|
|
||||||
#ifndef DISABLE_TIMEKEEPING
|
#ifndef DISABLE_TIMEKEEPING
|
||||||
static int64_t get_nanoseconds(void)
|
static int64_t get_nanoseconds(void)
|
||||||
@ -251,8 +251,8 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac)
|
|||||||
/* Glitch only happens when old_tac is enabled. */
|
/* Glitch only happens when old_tac is enabled. */
|
||||||
if (!(old_tac & 4)) return;
|
if (!(old_tac & 4)) return;
|
||||||
|
|
||||||
unsigned int old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
|
unsigned old_clocks = GB_TAC_TRIGGER_BITS[old_tac & 3];
|
||||||
unsigned int new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
|
unsigned new_clocks = GB_TAC_TRIGGER_BITS[new_tac & 3];
|
||||||
|
|
||||||
/* The bit used for overflow testing must have been 1 */
|
/* The bit used for overflow testing must have been 1 */
|
||||||
if (gb->div_counter & old_clocks) {
|
if (gb->div_counter & old_clocks) {
|
||||||
|
19
SDL/main.c
19
SDL/main.c
@ -33,6 +33,7 @@ static double clock_mutliplier = 1.0;
|
|||||||
static char *filename = NULL;
|
static char *filename = NULL;
|
||||||
static typeof(free) *free_function = NULL;
|
static typeof(free) *free_function = NULL;
|
||||||
static char *battery_save_path_ptr;
|
static char *battery_save_path_ptr;
|
||||||
|
static bool skip_audio;
|
||||||
|
|
||||||
SDL_AudioDeviceID device_id;
|
SDL_AudioDeviceID device_id;
|
||||||
|
|
||||||
@ -336,6 +337,8 @@ static void vblank(GB_gameboy_t *gb)
|
|||||||
}
|
}
|
||||||
do_rewind = rewind_down;
|
do_rewind = rewind_down;
|
||||||
handle_events(gb);
|
handle_events(gb);
|
||||||
|
|
||||||
|
skip_audio = (SDL_GetQueuedAudioSize(device_id) / sizeof(GB_sample_t)) > have_aspec.freq / 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -355,16 +358,12 @@ static void debugger_interrupt(int ignore)
|
|||||||
GB_debugger_break(&gb);
|
GB_debugger_break(&gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||||
static void audio_callback(void *gb, Uint8 *stream, int len)
|
|
||||||
{
|
{
|
||||||
if (GB_is_inited(gb)) {
|
if (skip_audio) return;
|
||||||
GB_apu_copy_buffer(gb, (GB_sample_t *) stream, len / sizeof(GB_sample_t));
|
SDL_QueueAudio(device_id, sample, sizeof(*sample));
|
||||||
}
|
|
||||||
else {
|
|
||||||
memset(stream, 0, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool handle_pending_command(void)
|
static bool handle_pending_command(void)
|
||||||
{
|
{
|
||||||
@ -436,6 +435,7 @@ restart:
|
|||||||
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||||
GB_set_rewind_length(&gb, configuration.rewind_length);
|
GB_set_rewind_length(&gb, configuration.rewind_length);
|
||||||
GB_set_update_input_hint_callback(&gb, handle_events);
|
GB_set_update_input_hint_callback(&gb, handle_events);
|
||||||
|
GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_DestroyTexture(texture);
|
SDL_DestroyTexture(texture);
|
||||||
@ -612,9 +612,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
want_aspec.callback = audio_callback;
|
|
||||||
want_aspec.userdata = &gb;
|
|
||||||
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
|
|
||||||
/* Start Audio */
|
/* Start Audio */
|
||||||
|
@ -12,6 +12,7 @@ SOURCES_C := $(CORE_DIR)/Core/gb.c \
|
|||||||
$(CORE_DIR)/Core/sm83_cpu.c \
|
$(CORE_DIR)/Core/sm83_cpu.c \
|
||||||
$(CORE_DIR)/Core/joypad.c \
|
$(CORE_DIR)/Core/joypad.c \
|
||||||
$(CORE_DIR)/Core/save_state.c \
|
$(CORE_DIR)/Core/save_state.c \
|
||||||
|
$(CORE_DIR)/Core/random.c \
|
||||||
$(CORE_DIR)/libretro/agb_boot.c \
|
$(CORE_DIR)/libretro/agb_boot.c \
|
||||||
$(CORE_DIR)/libretro/cgb_boot.c \
|
$(CORE_DIR)/libretro/cgb_boot.c \
|
||||||
$(CORE_DIR)/libretro/dmg_boot.c \
|
$(CORE_DIR)/libretro/dmg_boot.c \
|
||||||
|
@ -150,34 +150,22 @@ static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void audio_callback(void *gb)
|
static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||||
{
|
{
|
||||||
size_t length = GB_apu_get_current_buffer_length(gb);
|
if ((audio_out == GB_1 && gb == &gameboy[0]) ||
|
||||||
|
(audio_out == GB_2 && gb == &gameboy[1])) {
|
||||||
while (length > sizeof(soundbuf) / 4)
|
audio_batch_cb((void*)sample, 1);
|
||||||
{
|
|
||||||
GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, 1024);
|
|
||||||
audio_batch_cb(soundbuf, 1024);
|
|
||||||
length -= 1024;
|
|
||||||
}
|
|
||||||
if (length) {
|
|
||||||
GB_apu_copy_buffer(gb, (GB_sample_t *) soundbuf, length);
|
|
||||||
audio_batch_cb(soundbuf, length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vblank1(GB_gameboy_t *gb)
|
static void vblank1(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
vblank1_occurred = true;
|
vblank1_occurred = true;
|
||||||
if (audio_out == GB_1)
|
|
||||||
audio_callback(gb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vblank2(GB_gameboy_t *gb)
|
static void vblank2(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
vblank2_occurred = true;
|
vblank2_occurred = true;
|
||||||
if (audio_out == GB_2)
|
|
||||||
audio_callback(gb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool bit_to_send1 = true, bit_to_send2 = true;
|
static bool bit_to_send1 = true, bit_to_send2 = true;
|
||||||
@ -383,6 +371,7 @@ static void init_for_current_model(unsigned id)
|
|||||||
GB_set_pixels_output(&gameboy[i],(unsigned int*)(frame_buf + i * ((model[i] == MODEL_SGB || model[i] == MODEL_SGB2) ? SGB_VIDEO_PIXELS : VIDEO_PIXELS)));
|
GB_set_pixels_output(&gameboy[i],(unsigned int*)(frame_buf + i * ((model[i] == MODEL_SGB || model[i] == MODEL_SGB2) ? SGB_VIDEO_PIXELS : VIDEO_PIXELS)));
|
||||||
GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
|
GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
|
||||||
GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY);
|
GB_set_sample_rate(&gameboy[i], AUDIO_FREQUENCY);
|
||||||
|
GB_apu_set_sample_callback(&gameboy[i], audio_callback);
|
||||||
|
|
||||||
/* todo: attempt to make these more generic */
|
/* todo: attempt to make these more generic */
|
||||||
GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);
|
GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);
|
||||||
|
Loading…
Reference in New Issue
Block a user