# run gb_emu in webasm
https://web.dev/drawing-to-canvas-in-emscripten/
有了前面這個構想,現在的webasm不像2018那麼舊了,剛剛看了怎麼console 部分上面還有一塊區域,原來wasm 也整合了sdl2,剛好我們的gb_emu 也是用sdl2引擎那麼我們能不能直接移植上去呢我們來看一下。
```cpp=
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Point center = {.x = 100, .y = 100};
const int radius = 100;
void redraw() {
SDL_SetRenderDrawColor(renderer, /* RGBA: black */ 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer);
filledCircleRGBA(renderer, center.x, center.y, radius,
/* RGBA: green */ 0x00, 0x80, 0x00, 0xFF);
SDL_RenderPresent(renderer);
}
uint32_t ticksForNextKeyDown = 0;
bool handle_events() {
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
return false;
}
if (event.type == SDL_KEYDOWN) {
uint32_t ticksNow = SDL_GetTicks();
if (SDL_TICKS_PASSED(ticksNow, ticksForNextKeyDown)) {
// Throttle keydown events for 10ms.
ticksForNextKeyDown = ticksNow + 10;
switch (event.key.keysym.sym) {
case SDLK_UP:
center.y -= 1;
break;
case SDLK_DOWN:
center.y += 1;
break;
case SDLK_RIGHT:
center.x += 1;
break;
case SDLK_LEFT:
center.x -= 1;
break;
}
redraw();
}
}
return true;
}
void run_main_loop() {
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop([]() { handle_events(); }, 0, true);
#else
while (handle_events())
;
#endif
}
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_CreateWindowAndRenderer(300, 300, 0, &window, &renderer);
redraw();
run_main_loop();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
```
立刻有一個example 來達成用鍵盤來操控html上的圖形
```bash
emcc --bind foo.cpp -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2
```
![](https://i.imgur.com/kgByesW.png)
那麼來移植
```bash=
emcc --bind main.c ./lib/emu.c ./lib/cart.c -I ./include -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2 --preload-file LG.gb
```
```bash
emcc --bind main.c ./lib/cpu_fetch.c ./lib/interrupts.c ./lib/ppu_pipeline.c ./lib/timer.c ./lib/dma.c ./lib/bus.c ./lib/cart.c ./lib/cpu_proc.c ./lib/emu.c ./lib/io.c ./lib/ppu_sm.c ./lib/ui.c ./lib/cpu_util.c ./lib/gamepad.c ./lib/lcd.c ./lib/ram.c ./lib/cpu.c ./lib/dbg.c ./lib/instructions.c ./lib/ppu.c ./lib/stack.c -I ./include -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2 --preload-file LG.gb
```
![](https://i.imgur.com/7FwXmHt.png)
這邊可以看到可以load卡帶進前端了。
![](https://i.imgur.com/K6IV7yK.png)
讀取inst
稍微改動了大部分結構,有空再補,到這邊我們直接就gb_emu run 在 web上了,不過記憶體有點洩漏,可能要花更多一點時間排除
```c=
emcc --bind main.c ./lib/cpu_fetch.c ./lib/interrupts.c ./lib/ppu_pipeline.c ./lib/timer.c ./lib/dma.c ./lib/bus.c ./lib/cart.c ./lib/cpu_proc.c ./lib/emu.c ./lib/io.c ./lib/ppu_sm.c ./lib/ui.c ./lib/cpu_util.c ./lib/gamepad.c ./lib/lcd.c ./lib/ram.c ./lib/cpu.c ./lib/dbg.c ./lib/instructions.c ./lib/ppu.c ./lib/stack.c -I ./include -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2 --preload-file LG.gb -sALLOW_MEMORY_GROWTH -s ASYNCIFY
```
```c=
#include "../include/common.h"
// emcc --bind main.c ./lib/emu.c ./lib/cart.c ./lib/cart.c ./lib/cpu_proc.c ./lib/emu.c ./lib/io.c ./lib/ppu_sm.c ./lib/ui.c ./lib/cpu_util.c ./lib/gamepad.c ./lib/lcd.c ./lib/ram.c ./lib/cpu.c ./lib/dbg.c ./lib/instructions.c ./lib/ppu.c ./lib/stack.c -I ./include -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2 --preload-file LG.gb
// emcc --bind main.c ./lib/emu.c ./lib/cart.c ./lib/cart.c ./lib/cpu_proc.c ./lib/emu.c ./lib/io.c ./lib/ppu_sm.c ./lib/ui.c ./lib/cpu_util.c ./lib/gamepad.c ./lib/lcd.c ./lib/ram.c ./lib/cpu.c ./lib/dbg.c ./lib/instructions.c ./lib/ppu.c ./lib/stack.c -I ./include -o foo.html -s USE_SDL=2 -s USE_SDL_GFX=2 --preload-file LG.gb
// TODO Add Windows Alternative...
/*
Emu components:
|Cart|
|CPU|
|Address Bus|
|PPU|
|Timer|
*/
static emu_context ctx;
emu_context *emu_get_context()
{
return &ctx;
}
void *cpu_run(void *p)
{
timer_init();
cpu_init();
ppu_init();
ctx.running = true;
ctx.paused = false;
ctx.too = false;
ctx.ticks = 0;
// cpu_set_flags(ctx, ctx.regs.a, 1, (val & 0x0F) == 0x0F, -1);
// fp = fopen("regist.txt", "w");
while (ctx.running)
{
if (ctx.too)
break;
if (ctx.paused)
{
delay(10);
continue;
}
if (!cpu_step())
{
printf("CPU Stopped\n");
return 0;
}
// if (count2 >= 2000000 && count2 <= 4000000)
// continue;
}
return 0;
}
int emu_run()
{
// if (argc < 2)
// {
// printf("Usage: emu <rom_file>\n");
// return -1;
// }
if (!cart_load("LG.gb"))
// if (!cart_load(argv[1]))
{
printf("Failed to load ROM file: %s\n", "LG.gb");
return -2;
}
printf("Cart loaded..\n");
ui_init();
timer_init();
cpu_init();
ppu_init();
ctx.running = true;
ctx.paused = false;
ctx.too = false;
ctx.ticks = 0;
u32 prev_frame = 0;
int count2 = 0;
// cpu_set_flags(ctx, ctx.regs.a, 1, (val & 0x0F) == 0x0F, -1);
// fp = fopen("regist.txt", "w");
while (ctx.running)
{
if (ctx.too)
break;
if (ctx.paused)
{
delay(10);
continue;
}
if (!cpu_step())
{
printf("CPU Stopped\n");
return 0;
}
count2++;
if (count2 >=1000)
{
// usleep(100);
ui_handle_events();
// printf("CPU Stopped\n");
if (prev_frame != ppu_get_context()->current_frame)
{
// printf("CPU Stopped\n");
ui_update();
}
prev_frame = ppu_get_context()->current_frame;
count2 =0;}
// continue;
}
// pthread_t t1;
// if (pthread_create(&t1, NULL, cpu_run, NULL))
// {
// fprintf(stderr, "FAILED TO START MAIN CPU THREAD!\n");
// return -1;
// }
// u32 prev_frame = 0;
// while (!ctx.die)
// {
// while (ctx.too)
// ;
// usleep(100);
// ui_handle_events();
// if (prev_frame != ppu_get_context()->current_frame)
// {
// ui_update();
// }
// prev_frame = ppu_get_context()->current_frame;
// // printf("%d\n",prev_frame);
// }
return 0;
}
void emu_cycles(int cpu_cycles)
{
for (int i = 0; i < cpu_cycles; i++)
{
for (int n = 0; n < 4; n++)
{
ctx.ticks++;
timer_tick();
ppu_tick();
}
dma_tick();
}
}
```
主要還是pthread_create 改成固定呼叫,javascrpit 是單執行續,似乎有出套件可以變多執行緒
![](https://i.imgur.com/Hyya7nl.png)
![](https://i.imgur.com/WtOTTA5.gif)