diff --git a/Makefile b/Makefile index ee1e56e..de6492c 100644 --- a/Makefile +++ b/Makefile @@ -126,7 +126,7 @@ quicklook: $(BIN)/SameBoy.qlgenerator sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin -all: cocoa sdl tester libretro +all: cocoa sdl tester libretro wasm # Get a list of our source files and their respective object file targets @@ -151,7 +151,7 @@ TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES)) # Automatic dependency generation -ifneq ($(filter-out clean bootroms libretro %.bin, $(MAKECMDGOALS)),) +ifneq ($(filter-out clean bootroms libretro wasm %.bin, $(MAKECMDGOALS)),) -include $(CORE_OBJECTS:.o=.dep) ifneq ($(filter $(MAKECMDGOALS),sdl),) -include $(SDL_OBJECTS:.o=.dep) @@ -177,12 +177,12 @@ $(OBJ)/Core/%.c.o: Core/%.c $(OBJ)/%.c.o: %.c -@$(MKDIR) -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ - + # HexFiend requires more flags $(OBJ)/HexFiend/%.m.o: HexFiend/%.m -@$(MKDIR) -p $(dir $@) $(CC) $(CFLAGS) $(OCFLAGS) -c $< -o $@ -fno-objc-arc -include HexFiend/HexFiend_2_Framework_Prefix.pch - + $(OBJ)/%.m.o: %.m -@$(MKDIR) -p $(dir $@) $(CC) $(CFLAGS) $(OCFLAGS) -c $< -o $@ @@ -220,7 +220,7 @@ endif $(BIN)/SameBoy.app/Contents/Resources/Base.lproj/%.nib: Cocoa/%.xib ibtool --compile $@ $^ - + # Quick Look generator $(BIN)/SameBoy.qlgenerator: $(BIN)/SameBoy.qlgenerator/Contents/MacOS/SameBoyQL \ @@ -245,7 +245,7 @@ endif $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs/cgb_boot_fast.bin -@$(MKDIR) -p $(dir $@) cp -f $^ $@ - + # SDL Port # Unix versions build only one binary @@ -272,7 +272,7 @@ $(OBJ)/%.o: %.rc else $(OBJ)/%.res: %.rc -@$(MKDIR) -p $(dir $@) - rc /fo $@ /dVERSION=\"$(VERSION)\" $^ + rc /fo $@ /dVERSION=\"$(VERSION)\" $^ %.o: %.res cvtres /OUT:"$@" $^ @@ -299,11 +299,11 @@ $(BIN)/tester/sameboy_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS) $(BIN)/SDL/%.bin $(BIN)/tester/%.bin: $(BOOTROMS_DIR)/%.bin -@$(MKDIR) -p $(dir $@) cp -f $^ $@ - + $(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin -@$(MKDIR) -p $(dir $@) cp -f $^ $@ - + $(BIN)/SDL/LICENSE: LICENSE -@$(MKDIR) -p $(dir $@) cp -f $^ $@ @@ -311,7 +311,7 @@ $(BIN)/SDL/LICENSE: LICENSE $(BIN)/SDL/registers.sym: Misc/registers.sym -@$(MKDIR) -p $(dir $@) cp -f $^ $@ - + $(BIN)/SDL/background.bmp: SDL/background.bmp -@$(MKDIR) -p $(dir $@) cp -f $^ $@ @@ -346,9 +346,13 @@ $(BIN)/BootROMs/%.bin: BootROMs/%.asm $(OBJ)/BootROMs/SameBoyLogo.rle # Libretro Core (uses its own build system) libretro: $(MAKE) -C libretro - + +# WASM core (uses its own build system) +wasm: + $(MAKE) -C wasm + # Clean clean: rm -rf build -.PHONY: libretro +.PHONY: libretro wasm diff --git a/wasm/Makefile b/wasm/Makefile new file mode 100644 index 0000000..fa52237 --- /dev/null +++ b/wasm/Makefile @@ -0,0 +1,124 @@ +# Make hacks +.INTERMEDIATE: + +# Set target, configuration, version and destination folders + +PLATFORM := $(shell uname -s) +ifneq ($(findstring MINGW,$(PLATFORM)),) +PLATFORM := windows32 +USE_WINDRES := true +endif + +ifneq ($(findstring MSYS,$(PLATFORM)),) +PLATFORM := windows32 +endif + +ifeq ($(PLATFORM),windows32) +_ := $(shell chcp 65001) +endif + +DEFAULT := wasm +default: $(DEFAULT) + +ifeq ($(MAKECMDGOALS),) +MAKECMDGOALS := $(DEFAULT) +endif + +CORE_DIR += .. + +VERSION := 0.11.1 +export VERSION +CONF ?= debug + +BIN := $(CORE_DIR)/build/wasm_bin +OBJ := $(CORE_DIR)/build/wasm_obj +BOOTROMS_DIR ?= $(BIN)/BootROMs + +ifdef DATA_DIR +CFLAGS += -DDATA_DIR="\"$(DATA_DIR)\"" +endif + +# Set tools + +CC := emcc + +ifeq ($(PLATFORM),windows32) +# To force use of the Unix version instead of the Windows version +MKDIR := $(shell which mkdir) +else +MKDIR := mkdir +endif + +ifeq ($(CONF),native_release) +override CONF := release +LDFLAGS += -march=native -mtune=native +CFLAGS += -march=native -mtune=native +endif + +# Set compilation and linkage flags based on target, platform and configuration + +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) +WASM_LDFLAGS := -lopenal + +LDFLAGS += -lc -lm -ldl +CFLAGS += -Wno-deprecated-declarations + +ifeq ($(CONF),debug) +CFLAGS += -g +else ifeq ($(CONF), release) +CFLAGS += -O3 -DNDEBUG +else +$(error Invalid value for CONF: $(CONF). Use "debug", "release" or "native_release") +endif + +# Define our targets + +wasm: $(BIN)/SameBoy.js +all: wasm + +# Get a list of our source files and their respective object file targets + +CORE_SOURCES_RAW := $(shell ls $(CORE_DIR)/Core/*.c) +CORE_SOURCES := $(shell realpath --relative-to=$(CORE_DIR) $(CORE_SOURCES_RAW)) +CORE_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(CORE_SOURCES)) + +WASM_SOURCES := $(shell ls *.c) +WASM_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(WASM_SOURCES)) + +# Automatic dependency generation + +ifneq ($(filter-out clean %.bin, $(MAKECMDGOALS)),) +-include $(CORE_OBJECTS:.o=.dep) +ifneq ($(filter $(MAKECMDGOALS),wasm),) +-include $(WASM_OBJECTS:.o=.dep) +endif +endif + +$(OBJ)/%.dep: % + -@$(MKDIR) -p $(dir $@) + $(CC) $(CFLAGS) -MT $(OBJ)/$^.o -M $^ -c -o $@ + +$(CORE_DIR)/build/bin/BootROMs/%_boot.bin: + $(MAKE) -C $(CORE_DIR) $(patsubst $(CORE_DIR)/%,%,$@) + +# Compilation rules + +$(OBJ)/Core/%.c.o: $(CORE_DIR)/Core/%.c + -@$(MKDIR) -p $(dir $@) + $(CC) $(CFLAGS) -DGB_INTERNAL -c $< -o $@ + +$(OBJ)/%.c.o: %.c + -@$(MKDIR) -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +$(BIN)/SameBoy.js: $(CORE_OBJECTS) $(WASM_OBJECTS) + -@$(MKDIR) -p $(dir $@) + cp -r web/* $(BIN) + $(CC) -s WASM=1 $^ -o $@ $(LDFLAGS) $(WASM_LDFLAGS) +ifeq ($(CONF), release) + strip $@ +endif + +clean: + rm -f $(WASM_OBJECTS) $(BIN)/SameBoy.js $(BIN)/SameBoy.wasm diff --git a/wasm/main.c b/wasm/main.c new file mode 100644 index 0000000..f006972 --- /dev/null +++ b/wasm/main.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +unsigned audio_sample_rate = 0; +GB_gameboy_t gb; + +#define VIDEO_WIDTH 160 +#define VIDEO_HEIGHT 144 +#define VIDEO_PIXELS (VIDEO_WIDTH * VIDEO_HEIGHT) + +#define SGB_VIDEO_WIDTH 256 +#define SGB_VIDEO_HEIGHT 224 +#define SGB_VIDEO_PIXELS (SGB_VIDEO_WIDTH * SGB_VIDEO_HEIGHT) + +#define FRAME_RATE 0 // let the browser schedule (usually 60 FPS), if absolutely needed define as (0x400000 / 70224.0) + +static GB_model_t model; +// static GB_model_t auto_model = GB_MODEL_CGB_C; + +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 *previous_pixel_buffer = pixel_buffer_2; + +signed short soundbuf[1024 * 2]; + +unsigned query_sample_rate_of_audiocontexts() { + return EM_ASM_INT({ + var AudioContext = window.AudioContext || window.webkitAudioContext; + var ctx = new AudioContext(); + var sr = ctx.sampleRate; + ctx.close(); + return sr; + }); +} + +static void handle_events(GB_gameboy_t *gb) { + GB_set_key_state(gb, GB_KEY_START, true); +} + +static void vblank(GB_gameboy_t *gb) { + if (1 == 1) { + // render_texture(active_pixel_buffer, previous_pixel_buffer); + uint32_t *temp = active_pixel_buffer; + active_pixel_buffer = previous_pixel_buffer; + previous_pixel_buffer = temp; + GB_set_pixels_output(gb, active_pixel_buffer); + } + else { + // render_texture(active_pixel_buffer, NULL); + } + + handle_events(gb); +} + +static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) +{ + return (r << 16) | (g << 8) | b; +} + + +void run() { + if (GB_is_inited(&gb)) { + GB_switch_model_and_reset(&gb, model); + } + else { + GB_init(&gb, model); + + GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank); + GB_set_pixels_output(&gb, active_pixel_buffer); + GB_set_rgb_encode_callback(&gb, rgb_encode); + GB_set_sample_rate(&gb, audio_sample_rate); + GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_DISABLED); // TODO + GB_set_highpass_filter_mode(&gb, GB_HIGHPASS_OFF); // TODO + GB_set_rewind_length(&gb, 0); + } + + GB_debugger_execute_command(&gb, "registers"); +} + +int main(int argc, char **argv) +{ +#define str(x) #x +#define xstr(x) str(x) + fprintf(stderr, "SameBoy v" xstr(VERSION) "\n"); + + audio_sample_rate = query_sample_rate_of_audiocontexts(); + + fprintf(stderr, "Sample rate: %u\n", audio_sample_rate); + + emscripten_set_main_loop( + run, // our main loop + FRAME_RATE, + true // infinite loop + ); +} diff --git a/wasm/web/index.html b/wasm/web/index.html new file mode 100644 index 0000000..ce2169c --- /dev/null +++ b/wasm/web/index.html @@ -0,0 +1,15 @@ + + + + + SameBoy + + + + + +