Audio and video are working
Boot ROMs can be loaded from the virtual file system
This commit is contained in:
parent
0692347002
commit
3ad106d9c5
@ -32,7 +32,7 @@ CONF ?= debug
|
|||||||
|
|
||||||
BIN := $(CORE_DIR)/build/wasm_bin
|
BIN := $(CORE_DIR)/build/wasm_bin
|
||||||
OBJ := $(CORE_DIR)/build/wasm_obj
|
OBJ := $(CORE_DIR)/build/wasm_obj
|
||||||
BOOTROMS_DIR ?= $(BIN)/BootROMs
|
BOOTROMS_DIR ?= $(CORE_DIR)/build/bin/BootROMs
|
||||||
|
|
||||||
ifdef DATA_DIR
|
ifdef DATA_DIR
|
||||||
CFLAGS += -DDATA_DIR="\"$(DATA_DIR)\""
|
CFLAGS += -DDATA_DIR="\"$(DATA_DIR)\""
|
||||||
@ -59,13 +59,15 @@ endif
|
|||||||
|
|
||||||
CFLAGS += -Werror -Wall -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
CFLAGS += -Werror -Wall -Wno-strict-aliasing -Wno-unknown-warning -Wno-unknown-warning-option -Wno-multichar -Wno-int-in-bool-context -std=gnu11 -D_GNU_SOURCE -DVERSION="$(VERSION)" -I. -D_USE_MATH_DEFINES
|
||||||
CFLAGS += -I$(CORE_DIR)
|
CFLAGS += -I$(CORE_DIR)
|
||||||
WASM_LDFLAGS := -lopenal
|
CFLAGS += -s WASM=1 -s USE_SDL=2 --preload-file $(BOOTROMS_DIR)@/BootROMs
|
||||||
|
# CFLAGS += -Wcast-align -Wover-aligned -s SAFE_HEAP=1 -s WARN_UNALIGNED=1
|
||||||
|
WASM_LDFLAGS :=
|
||||||
|
|
||||||
LDFLAGS += -lc -lm -ldl
|
LDFLAGS += -lc -lm -ldl
|
||||||
CFLAGS += -Wno-deprecated-declarations
|
CFLAGS += -Wno-deprecated-declarations
|
||||||
|
|
||||||
ifeq ($(CONF),debug)
|
ifeq ($(CONF),debug)
|
||||||
CFLAGS += -g
|
CFLAGS += -g4 --profiling-funcs
|
||||||
else ifeq ($(CONF), release)
|
else ifeq ($(CONF), release)
|
||||||
CFLAGS += -O3 -DNDEBUG
|
CFLAGS += -O3 -DNDEBUG
|
||||||
else
|
else
|
||||||
@ -74,7 +76,13 @@ endif
|
|||||||
|
|
||||||
# Define our targets
|
# Define our targets
|
||||||
|
|
||||||
wasm: $(BIN)/SameBoy.js
|
bootroms: $(BOOTROMS_DIR)/agb_boot.bin \
|
||||||
|
$(BOOTROMS_DIR)/cgb_boot.bin \
|
||||||
|
$(BOOTROMS_DIR)/dmg_boot.bin \
|
||||||
|
$(BOOTROMS_DIR)/sgb_boot.bin \
|
||||||
|
$(BOOTROMS_DIR)/sgb2_boot.bin
|
||||||
|
|
||||||
|
wasm: bootroms $(BIN)/SameBoy.js
|
||||||
all: wasm
|
all: wasm
|
||||||
|
|
||||||
# Get a list of our source files and their respective object file targets
|
# Get a list of our source files and their respective object file targets
|
||||||
@ -115,10 +123,13 @@ $(OBJ)/%.c.o: %.c
|
|||||||
$(BIN)/SameBoy.js: $(CORE_OBJECTS) $(WASM_OBJECTS)
|
$(BIN)/SameBoy.js: $(CORE_OBJECTS) $(WASM_OBJECTS)
|
||||||
-@$(MKDIR) -p $(dir $@)
|
-@$(MKDIR) -p $(dir $@)
|
||||||
cp -r web/* $(BIN)
|
cp -r web/* $(BIN)
|
||||||
$(CC) -s WASM=1 $^ -o $@ $(LDFLAGS) $(WASM_LDFLAGS)
|
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) $(WASM_LDFLAGS)
|
||||||
ifeq ($(CONF), release)
|
ifeq ($(CONF), release)
|
||||||
strip $@
|
strip $@
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
$(CORE_DIR)/build/bin/BootROMs/%_boot.bin:
|
||||||
|
$(MAKE) -C $(CORE_DIR) $(patsubst $(CORE_DIR)/%,%,$@)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(WASM_OBJECTS) $(BIN)/SameBoy.js $(BIN)/SameBoy.wasm
|
rm -f $(WASM_OBJECTS) $(BIN)/SameBoy.js $(BIN)/SameBoy.wasm
|
||||||
|
314
wasm/main.c
314
wasm/main.c
@ -1,9 +1,48 @@
|
|||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
|
|
||||||
unsigned audio_sample_rate = 0;
|
const char *resource_folder(void)
|
||||||
|
{
|
||||||
|
#ifdef DATA_DIR
|
||||||
|
return DATA_DIR;
|
||||||
|
#else
|
||||||
|
static const char *ret = NULL;
|
||||||
|
if (!ret) {
|
||||||
|
ret = SDL_GetBasePath();
|
||||||
|
if (!ret) {
|
||||||
|
ret = "./";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
char *resource_path(const char *filename)
|
||||||
|
{
|
||||||
|
static char path[1024];
|
||||||
|
snprintf(path, sizeof(path), "%s%s", resource_folder(), filename);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* concat(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
fprintf(stderr, "Failed to allocate memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(result, s1);
|
||||||
|
strcat(result, s2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
GB_gameboy_t gb;
|
GB_gameboy_t gb;
|
||||||
|
|
||||||
#define VIDEO_WIDTH 160
|
#define VIDEO_WIDTH 160
|
||||||
@ -16,15 +55,98 @@ GB_gameboy_t gb;
|
|||||||
|
|
||||||
#define FRAME_RATE 0 // let the browser schedule (usually 60 FPS), if absolutely needed define as (0x400000 / 70224.0)
|
#define FRAME_RATE 0 // let the browser schedule (usually 60 FPS), if absolutely needed define as (0x400000 / 70224.0)
|
||||||
|
|
||||||
static GB_model_t model;
|
SDL_Window *window;
|
||||||
// static GB_model_t auto_model = GB_MODEL_CGB_C;
|
SDL_Renderer *renderer;
|
||||||
|
SDL_Texture *texture;
|
||||||
|
SDL_PixelFormat *pixel_format;
|
||||||
|
SDL_AudioDeviceID device_id;
|
||||||
|
|
||||||
|
static SDL_AudioSpec want_aspec, have_aspec;
|
||||||
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
|
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
|
||||||
static uint32_t *active_pixel_buffer = pixel_buffer_1;
|
static uint32_t *active_pixel_buffer = pixel_buffer_1;
|
||||||
static uint32_t *previous_pixel_buffer = pixel_buffer_2;
|
static uint32_t *previous_pixel_buffer = pixel_buffer_2;
|
||||||
|
|
||||||
signed short soundbuf[1024 * 2];
|
signed short soundbuf[1024 * 2];
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JOYPAD_AXISES_X,
|
||||||
|
JOYPAD_AXISES_Y,
|
||||||
|
JOYPAD_AXISES_MAX
|
||||||
|
} joypad_axis_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SDL_Scancode keys[9];
|
||||||
|
GB_color_correction_mode_t color_correction_mode;
|
||||||
|
bool blend_frames;
|
||||||
|
|
||||||
|
GB_highpass_mode_t highpass_mode;
|
||||||
|
|
||||||
|
char filter[32];
|
||||||
|
enum {
|
||||||
|
MODEL_DMG,
|
||||||
|
MODEL_CGB,
|
||||||
|
MODEL_AGB,
|
||||||
|
MODEL_SGB,
|
||||||
|
MODEL_MAX,
|
||||||
|
} model;
|
||||||
|
|
||||||
|
/* v0.11 */
|
||||||
|
uint32_t rewind_length;
|
||||||
|
SDL_Scancode keys_2[32]; /* Rewind and underclock, + padding for the future */
|
||||||
|
uint8_t joypad_configuration[32]; /* 12 Keys + padding for the future*/;
|
||||||
|
uint8_t joypad_axises[JOYPAD_AXISES_MAX];
|
||||||
|
|
||||||
|
/* v0.12 */
|
||||||
|
enum {
|
||||||
|
SGB_NTSC,
|
||||||
|
SGB_PAL,
|
||||||
|
SGB_2,
|
||||||
|
SGB_MAX
|
||||||
|
} sgb_revision;
|
||||||
|
} configuration_t;
|
||||||
|
|
||||||
|
configuration_t configuration =
|
||||||
|
{
|
||||||
|
.keys = {
|
||||||
|
SDL_SCANCODE_RIGHT,
|
||||||
|
SDL_SCANCODE_LEFT,
|
||||||
|
SDL_SCANCODE_UP,
|
||||||
|
SDL_SCANCODE_DOWN,
|
||||||
|
SDL_SCANCODE_X,
|
||||||
|
SDL_SCANCODE_Z,
|
||||||
|
SDL_SCANCODE_BACKSPACE,
|
||||||
|
SDL_SCANCODE_RETURN,
|
||||||
|
SDL_SCANCODE_SPACE
|
||||||
|
},
|
||||||
|
.keys_2 = {
|
||||||
|
SDL_SCANCODE_TAB,
|
||||||
|
SDL_SCANCODE_LSHIFT,
|
||||||
|
},
|
||||||
|
.joypad_configuration = {
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
8,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
-1,
|
||||||
|
5,
|
||||||
|
},
|
||||||
|
.joypad_axises = {
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
.color_correction_mode = GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
||||||
|
.highpass_mode = GB_HIGHPASS_ACCURATE,
|
||||||
|
.blend_frames = true,
|
||||||
|
.rewind_length = 60 * 2,
|
||||||
|
.model = MODEL_CGB
|
||||||
|
};
|
||||||
|
|
||||||
unsigned query_sample_rate_of_audiocontexts() {
|
unsigned query_sample_rate_of_audiocontexts() {
|
||||||
return EM_ASM_INT({
|
return EM_ASM_INT({
|
||||||
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||||
@ -35,20 +157,42 @@ unsigned query_sample_rate_of_audiocontexts() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void audio_callback(void *gb, Uint8 *stream, int len)
|
||||||
|
{
|
||||||
|
if (GB_is_inited(gb)) {
|
||||||
|
GB_apu_copy_buffer(gb, (GB_sample_t *) stream, len / sizeof(GB_sample_t));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memset(stream, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_texture(void *pixels, void *previous)
|
||||||
|
{
|
||||||
|
if (renderer) {
|
||||||
|
if (pixels) {
|
||||||
|
SDL_UpdateTexture(texture, NULL, pixels, 160 * sizeof (uint32_t));
|
||||||
|
}
|
||||||
|
SDL_RenderClear(renderer);
|
||||||
|
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_events(GB_gameboy_t *gb) {
|
static void handle_events(GB_gameboy_t *gb) {
|
||||||
GB_set_key_state(gb, GB_KEY_START, true);
|
GB_set_key_state(gb, GB_KEY_START, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb) {
|
static void vblank(GB_gameboy_t *gb) {
|
||||||
if (1 == 1) {
|
if (1 == 0) {
|
||||||
// render_texture(active_pixel_buffer, previous_pixel_buffer);
|
render_texture(active_pixel_buffer, previous_pixel_buffer);
|
||||||
uint32_t *temp = active_pixel_buffer;
|
uint32_t *temp = active_pixel_buffer;
|
||||||
active_pixel_buffer = previous_pixel_buffer;
|
active_pixel_buffer = previous_pixel_buffer;
|
||||||
previous_pixel_buffer = temp;
|
previous_pixel_buffer = temp;
|
||||||
GB_set_pixels_output(gb, active_pixel_buffer);
|
GB_set_pixels_output(gb, active_pixel_buffer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// render_texture(active_pixel_buffer, NULL);
|
render_texture(active_pixel_buffer, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_events(gb);
|
handle_events(gb);
|
||||||
@ -56,42 +200,186 @@ static void vblank(GB_gameboy_t *gb) {
|
|||||||
|
|
||||||
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
|
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
|
||||||
{
|
{
|
||||||
return (r << 16) | (g << 8) | b;
|
return SDL_MapRGB(pixel_format, r, g, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
GB_model_t model;
|
||||||
|
|
||||||
|
model = (GB_model_t [])
|
||||||
|
{
|
||||||
|
[MODEL_DMG] = GB_MODEL_DMG_B,
|
||||||
|
[MODEL_CGB] = GB_MODEL_CGB_E,
|
||||||
|
[MODEL_AGB] = GB_MODEL_AGB,
|
||||||
|
[MODEL_SGB] = (GB_model_t [])
|
||||||
|
{
|
||||||
|
[SGB_NTSC] = GB_MODEL_SGB_NTSC,
|
||||||
|
[SGB_PAL] = GB_MODEL_SGB_PAL,
|
||||||
|
[SGB_2] = GB_MODEL_SGB2,
|
||||||
|
}[configuration.sgb_revision],
|
||||||
|
}[configuration.model];
|
||||||
|
|
||||||
|
fprintf(stderr, "Initializing ...\n");
|
||||||
|
|
||||||
void run() {
|
|
||||||
if (GB_is_inited(&gb)) {
|
if (GB_is_inited(&gb)) {
|
||||||
|
fprintf(stderr, "Already initialized, switching model ...\n");
|
||||||
|
|
||||||
GB_switch_model_and_reset(&gb, model);
|
GB_switch_model_and_reset(&gb, model);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
fprintf(stderr, "Initializing new GB ...\n");
|
||||||
|
|
||||||
GB_init(&gb, model);
|
GB_init(&gb, model);
|
||||||
|
|
||||||
|
GB_set_input_callback(&gb, NULL);
|
||||||
|
GB_set_async_input_callback(&gb, NULL);
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_pixels_output(&gb, active_pixel_buffer);
|
GB_set_pixels_output(&gb, active_pixel_buffer);
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
GB_set_sample_rate(&gb, audio_sample_rate);
|
GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_DISABLED); // TODO
|
GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
||||||
GB_set_highpass_filter_mode(&gb, GB_HIGHPASS_OFF); // TODO
|
GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||||
GB_set_rewind_length(&gb, 0);
|
GB_set_rewind_length(&gb, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_debugger_execute_command(&gb, "registers");
|
SDL_DestroyTexture(texture);
|
||||||
|
texture = SDL_CreateTexture(
|
||||||
|
renderer,
|
||||||
|
SDL_GetWindowPixelFormat(window),
|
||||||
|
SDL_TEXTUREACCESS_STREAMING,
|
||||||
|
GB_get_screen_width(&gb),
|
||||||
|
GB_get_screen_height(&gb)
|
||||||
|
);
|
||||||
|
|
||||||
|
SDL_SetWindowMinimumSize(window, GB_get_screen_width(&gb), GB_get_screen_height(&gb));
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
const char * const boot_roms[] = {
|
||||||
|
"dmg_boot.bin",
|
||||||
|
"cgb_boot.bin",
|
||||||
|
"agb_boot.bin",
|
||||||
|
"sgb_boot.bin"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *boot_rom = boot_roms[configuration.model];
|
||||||
|
if (configuration.model == GB_MODEL_SGB && configuration.sgb_revision == SGB_2) {
|
||||||
|
boot_rom = "sgb2_boot.bin";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *boot_rom_path = resource_path(concat("BootROMs/", boot_rom));
|
||||||
|
|
||||||
|
fprintf(stderr, "Loading boot ROM: %s\n", boot_rom_path);
|
||||||
|
|
||||||
|
error = GB_load_boot_rom(&gb, boot_rom_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
GB_run_frame(&gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
#define str(x) #x
|
#define str(x) #x
|
||||||
#define xstr(x) str(x)
|
#define xstr(x) str(x)
|
||||||
|
pixel_format = (SDL_PixelFormat *) malloc(sizeof(SDL_PixelFormat));
|
||||||
|
|
||||||
|
if (!pixel_format) {
|
||||||
|
fprintf(stderr, "Failed to allocate memory\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// emscripten_sample_gamepad_data();
|
||||||
|
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) != 0) {
|
||||||
|
fprintf(stderr, "SDL_Init Error: %s\n", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "SameBoy v" xstr(VERSION) "\n");
|
fprintf(stderr, "SameBoy v" xstr(VERSION) "\n");
|
||||||
|
|
||||||
audio_sample_rate = query_sample_rate_of_audiocontexts();
|
window = SDL_CreateWindow(
|
||||||
|
"SameBoy v" xstr(VERSION),
|
||||||
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
VIDEO_WIDTH,
|
||||||
|
VIDEO_HEIGHT,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!window) {
|
||||||
|
printf("Could not create window: %s\n", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetWindowMinimumSize(window, VIDEO_WIDTH, VIDEO_HEIGHT);
|
||||||
|
|
||||||
|
renderer = SDL_CreateRenderer(
|
||||||
|
window,
|
||||||
|
-1,
|
||||||
|
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!renderer) {
|
||||||
|
fprintf(stderr, "SDL_CreateRenderer Error: %s\n", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *screen = SDL_CreateRGBSurface(
|
||||||
|
0,
|
||||||
|
VIDEO_WIDTH,
|
||||||
|
VIDEO_HEIGHT,
|
||||||
|
32,
|
||||||
|
0, 0, 0, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!screen) {
|
||||||
|
SDL_Log("SDL_CreateRGBSurface() failed: %s", SDL_GetError());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel_format = screen->format;
|
||||||
|
|
||||||
|
texture = SDL_CreateTextureFromSurface(renderer, screen);
|
||||||
|
|
||||||
|
if (!texture) {
|
||||||
|
fprintf(stderr, "SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned audio_sample_rate = query_sample_rate_of_audiocontexts();
|
||||||
fprintf(stderr, "Sample rate: %u\n", audio_sample_rate);
|
fprintf(stderr, "Sample rate: %u\n", audio_sample_rate);
|
||||||
|
|
||||||
|
memset(&want_aspec, 0, sizeof(want_aspec));
|
||||||
|
want_aspec.freq = audio_sample_rate;
|
||||||
|
want_aspec.format = AUDIO_S16SYS;
|
||||||
|
want_aspec.channels = 2;
|
||||||
|
want_aspec.samples = 512;
|
||||||
|
|
||||||
|
want_aspec.callback = audio_callback;
|
||||||
|
want_aspec.userdata = &gb;
|
||||||
|
device_id = SDL_OpenAudioDevice(NULL, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
|
|
||||||
|
if (device_id == 0) {
|
||||||
|
fprintf(stderr, "Failed to open audio: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(device_id, 0);
|
||||||
|
|
||||||
emscripten_set_main_loop(
|
emscripten_set_main_loop(
|
||||||
run, // our main loop
|
run, // our main loop
|
||||||
FRAME_RATE,
|
FRAME_RATE,
|
||||||
true // infinite loop
|
true // infinite loop
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fprintf(stderr, "Quitting ...\n");
|
||||||
|
|
||||||
|
SDL_FreeSurface(screen);
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,52 @@
|
|||||||
<title>SameBoy</title>
|
<title>SameBoy</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script>
|
||||||
|
var Module = {
|
||||||
|
canvas: document.querySelector('#canvas')
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="SameBoy.js"></script>
|
<script src="SameBoy.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
const loadROM = f => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = (file => {
|
||||||
|
return e => {
|
||||||
|
Module.load_rom(romName, e.target.result);
|
||||||
|
};
|
||||||
|
})(f);
|
||||||
|
|
||||||
|
reader.readAsArrayBuffer(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileSelect = (evt, files) => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
load_rom(files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragOver = evt => {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('dragover', handleDragOver, false);
|
||||||
|
|
||||||
|
window.addEventListener('drop', e => {
|
||||||
|
handleFileSelect(e, e.dataTransfer.files);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
document.getElementById('file').addEventListener('change', e => {
|
||||||
|
handleFileSelect(e, e.target.files);
|
||||||
|
}, false);
|
||||||
|
|
||||||
Module.onRuntimeInitialized = _ => {
|
Module.onRuntimeInitialized = _ => {
|
||||||
console.log(Module)
|
console.log(Module)
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user