Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a "web API" and other additions for running inside a VSCode extension. #31

Merged
merged 27 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
807b97d
start implementing a web api callable from browser
floooh Nov 15, 2023
b630416
add wasm-vscode-debug build config
floooh Nov 16, 2023
4f0b6d6
cmdline arg and web api function to disable speaker icon
floooh Nov 16, 2023
fbc5a9c
extend web api
floooh Nov 18, 2023
1e96199
kc85.c: more webapi and external debug adapter stuff wip
floooh Nov 18, 2023
cf7524c
kc85.c: more webapi wip
floooh Nov 18, 2023
362a757
implement proper stop-reason for webapi event callback
floooh Nov 18, 2023
c2db9c1
kc85.c: implement webapi_dbg_cpu_state request
floooh Nov 19, 2023
be634c1
kc85.c: implement webapi_ready()
floooh Nov 19, 2023
8b836b8
add explicit webapi debugger connect/disconnect functions
floooh Nov 20, 2023
f64a931
webapi, kc85: add entry and exit debugger stop-reasons
floooh Nov 21, 2023
a59c3b4
kc85.c: fix webapi stop reason detection
floooh Nov 21, 2023
0cbf712
webapi: on quickload, if debugger is currently stopped, unstuck it
floooh Nov 21, 2023
227defe
kc85.c: implement webapi disassembly request
floooh Nov 25, 2023
7875095
webapi: function naming fixes
floooh Nov 25, 2023
4e58b11
webapi: implement read memory
floooh Dec 4, 2023
0682b27
fs.c, shell.html: remove some noisy log messages
floooh Dec 19, 2023
9603c08
webapi: add reset and reboot events
floooh Dec 19, 2023
27fa856
fix kc85.c without dbg ui
floooh Dec 20, 2023
77085e3
kc85: fix compilation
floooh Dec 21, 2023
785ccff
bombjack, pengo, pacman: fix sargs crash
floooh Dec 21, 2023
afc7c27
start adding webapi to c64
floooh Dec 21, 2023
b0ba3f3
new webapi_load() function with generic container file header
floooh Dec 23, 2023
84c8214
c64: add webapi_load support
floooh Dec 23, 2023
e195d85
c64.c: use BASIC key buffer to start PRG and TAP files
floooh Dec 28, 2023
35c2a35
cpc.c: remote debugging support wip
floooh Dec 28, 2023
57ac173
cpc.c: fix exit breakpoint detection
floooh Dec 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ fips_begin_lib(common)
fs.c fs.h
gfx.c gfx.h
keybuf.c keybuf.h
prof.c prof.h)
prof.c prof.h
webapi.c webapi.h)
sokol_shader(shaders.glsl ${slang})
if (FIPS_OSX)
fips_files(sokol.m)
Expand Down Expand Up @@ -65,4 +66,8 @@ fips_end_lib()
# a separate library with just keybuf (for the ASCII emulators)
fips_begin_lib(keybuf)
fips_files(keybuf.c keybuf.h)
fips_end_lib()

fips_begin_lib(webapi)
fips_files(webapi.c webapi.h)
fips_end_lib()
1 change: 1 addition & 0 deletions examples/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
#include "fs.h"
#include "gfx.h"
#include "keybuf.h"
#include "webapi.h"
#include <ctype.h> // isupper, islower, toupper, tolower
2 changes: 0 additions & 2 deletions examples/common/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,6 @@ EM_JS(void, fs_js_load_snapshot, (const char* system_name_cstr, int snapshot_ind
const db_name = 'chips';
const db_store_name = 'store';
const system_name = UTF8ToString(system_name_cstr);
console.log('fs_js_load_snapshot: called with', system_name, snapshot_index);
let open_request;
try {
open_request = window.indexedDB.open(db_name, 1);
Expand Down Expand Up @@ -501,7 +500,6 @@ EM_JS(void, fs_js_load_snapshot, (const char* system_name_cstr, int snapshot_ind
HEAPU8.set(get_request.result, ptr);
_fs_emsc_load_snapshot_callback(context, ptr, num_bytes);
} else {
console.log('fs_js_load_snapshot:', key, 'does not exist');
_fs_emsc_load_snapshot_callback(context, 0, 0);
}
};
Expand Down
9 changes: 8 additions & 1 deletion examples/common/gfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

typedef struct {
bool valid;
bool disable_speaker_icon;
gfx_border_t border;
struct {
sg_image img; // framebuffer texture, RGBA8 or R8 if paletted
Expand Down Expand Up @@ -151,6 +152,11 @@ void gfx_flash_error(void) {
state.flash_error_count = 20;
}

void gfx_disable_speaker_icon(void) {
assert(state.valid);
state.disable_speaker_icon = true;
}

sg_image gfx_create_icon_texture(const uint8_t* packed_pixels, int width, int height, int stride) {
const size_t pixel_data_size = width * height * sizeof(uint32_t);
uint32_t* pixels = malloc(pixel_data_size);
Expand Down Expand Up @@ -256,6 +262,7 @@ void gfx_init(const gfx_desc_t* desc) {
});

state.valid = true;
state.disable_speaker_icon = desc->disable_speaker_icon;
state.border = desc->border;
state.display.portrait = desc->display_info.portrait;
state.draw_extra_cb = desc->draw_extra_cb;
Expand Down Expand Up @@ -413,7 +420,7 @@ void gfx_draw(chips_display_info_t display_info) {
}

// if audio is off, draw speaker icon via sokol-gl
if (saudio_suspended()) {
if (!state.disable_speaker_icon && saudio_suspended()) {
const float x0 = display.width - (float)state.icon.dim.width - 10.0f;
const float x1 = x0 + (float)state.icon.dim.width;
const float y0 = 10.0f;
Expand Down
2 changes: 2 additions & 0 deletions examples/common/gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef struct {
} gfx_border_t;

typedef struct {
bool disable_speaker_icon;
gfx_border_t border;
chips_display_info_t display_info;
chips_dim_t pixel_aspect; // optional pixel aspect ratio, default is 1:1
Expand All @@ -32,6 +33,7 @@ void gfx_draw(chips_display_info_t display_info);
void gfx_shutdown(void);
void gfx_flash_success(void);
void gfx_flash_error(void);
void gfx_disable_speaker_icon(void);
sg_image gfx_create_icon_texture(const uint8_t* packed_pixels, int width, int height, int stride);

#ifdef __cplusplus
Expand Down
8 changes: 2 additions & 6 deletions examples/common/shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,8 @@
canvas.addEventListener("webglcontextlost", function(e) { alert('FIXME: WebGL context lost, please reload the page'); e.preventDefault(); }, false);
return canvas;
})(),
setStatus: function(text) {
console.log("status: " + text);
},
monitorRunDependencies: function(left) {
console.log("monitor run deps: " + left);
}
setStatus: function(text) { },
monitorRunDependencies: function(left) { },
};
window.onerror = function() {
console.log("onerror: " + event);
Expand Down
223 changes: 223 additions & 0 deletions examples/common/webapi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
#include "webapi.h"
#include <assert.h>
#include <string.h>
#if defined(__EMSCRIPTEN__)
#include <emscripten/emscripten.h>
#endif
#include "gfx.h"

static struct {
bool dbg_connect_requested;
} before_init_state;

static struct {
bool inited;
webapi_interface_t funcs;
} state;

void webapi_init(const webapi_desc_t* desc) {
assert(desc);
state.inited = true;
state.funcs = desc->funcs;
if (before_init_state.dbg_connect_requested && state.funcs.dbg_connect) {
state.funcs.dbg_connect();
}
}

#if defined(__EMSCRIPTEN__)

EM_JS(void, webapi_js_event_stopped, (int stop_reason, uint16_t addr), {
console.log("webapi_js_event_stopped()");
if (Module["webapi_onStopped"]) {
Module["webapi_onStopped"](stop_reason, addr);
} else {
console.log("no Module.webapi_onStopped function");
}
});

EM_JS(void, webapi_js_event_continued, (), {
console.log("webapi_js_event_continued()");
if (Module["webapi_onContinued"]) {
Module["webapi_onContinued"]();
} else {
console.log("no Module.webapi_onContinued function");
}
});

EM_JS(void, webapi_js_event_reboot, (), {
console.log("webapi_js_event_reboot()");
if (Module["webapi_onReboot"]) {
Module["webapi_onReboot"]();
} else {
console.log("no Module.webapi_onReboot function");
}
});

EM_JS(void, webapi_js_event_reset, (), {
console.log("webapi_js_event_reset()");
if (Module["webapi_onReset"]) {
Module["webapi_onReset"]();
} else {
console.log("no Module.webapi_onReset function");
}
});

EMSCRIPTEN_KEEPALIVE void webapi_dbg_connect(void) {
if (state.inited) {
if (state.funcs.dbg_connect) {
state.funcs.dbg_connect();
}
} else {
before_init_state.dbg_connect_requested = true;
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_disconnect(void) {
if (state.inited && state.funcs.dbg_disconnect) {
state.funcs.dbg_disconnect();
}
}

EMSCRIPTEN_KEEPALIVE void* webapi_alloc(int size) {
return malloc((size_t)size);
}

EMSCRIPTEN_KEEPALIVE void webapi_free(void* ptr) {
if (ptr) {
free(ptr);
}
}

EMSCRIPTEN_KEEPALIVE void webapi_boot(void) {
if (state.inited && state.funcs.boot) {
state.funcs.boot();
}
}

EMSCRIPTEN_KEEPALIVE void webapi_reset(void) {
if (state.inited && state.funcs.reset) {
state.funcs.reset();
}
}

EMSCRIPTEN_KEEPALIVE bool webapi_ready(void) {
if (state.inited && state.funcs.ready) {
return state.funcs.ready();
} else {
return false;
}
}

EMSCRIPTEN_KEEPALIVE bool webapi_load(void* ptr, int size) {
if (state.inited && state.funcs.load && ptr && ((size_t)size > sizeof(webapi_fileheader_t))) {
const webapi_fileheader_t* hdr = (webapi_fileheader_t*)ptr;
if ((hdr->magic[0] != 'C') || (hdr->magic[1] != 'H') || (hdr->magic[2] != 'I') || (hdr->magic[3] != 'P')) {
return false;
}
return state.funcs.load((chips_range_t){ .ptr = ptr, .size = (size_t)size });
}
return false;
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_add_breakpoint(uint16_t addr) {
if (state.inited && state.funcs.dbg_add_breakpoint) {
state.funcs.dbg_add_breakpoint(addr);
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_remove_breakpoint(uint16_t addr) {
if (state.inited && state.funcs.dbg_remove_breakpoint) {
state.funcs.dbg_remove_breakpoint(addr);
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_break(void) {
if (state.inited && state.funcs.dbg_break) {
state.funcs.dbg_break();
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_continue(void) {
if (state.inited && state.funcs.dbg_continue) {
state.funcs.dbg_continue();
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_step_next(void) {
if (state.inited && state.funcs.dbg_step_next) {
state.funcs.dbg_step_next();
}
}

EMSCRIPTEN_KEEPALIVE void webapi_dbg_step_into(void) {
if (state.inited && state.funcs.dbg_step_into) {
state.funcs.dbg_step_into();
}
}

// return emulator state as JSON-formatted string pointer into WASM heap
EMSCRIPTEN_KEEPALIVE uint16_t* webapi_dbg_cpu_state(void) {
static webapi_cpu_state_t res;
if (state.inited && state.funcs.dbg_cpu_state) {
res = state.funcs.dbg_cpu_state();
} else {
memset(&res, 0, sizeof(res));
}
return &res.items[0];
}

// request a disassembly, returns ptr to heap-allocated array of 'num_lines' webapi_dasm_line_t structs which must be freed with webapi_free()
// NOTE: may return 0!
EMSCRIPTEN_KEEPALIVE webapi_dasm_line_t* webapi_dbg_request_disassembly(uint16_t addr, int offset_lines, int num_lines) {
if (num_lines <= 0) {
return 0;
}
if (state.inited && state.funcs.dbg_request_disassembly) {
webapi_dasm_line_t* out_lines = calloc((size_t)num_lines, sizeof(webapi_dasm_line_t));
state.funcs.dbg_request_disassembly(addr, offset_lines, num_lines, out_lines);
return out_lines;
} else {
return 0;
}
}

// reads a memory chunk, returns heap-allocated buffer which must be freed with webapi_free()
// NOTE: may return 0!
EMSCRIPTEN_KEEPALIVE uint8_t* webapi_dbg_read_memory(uint16_t addr, int num_bytes) {
if (state.inited && state.funcs.dbg_read_memory) {
uint8_t* ptr = calloc((size_t)num_bytes, 1);
state.funcs.dbg_read_memory(addr, num_bytes, ptr);
return ptr;
} else {
return 0;
}
}

#endif // __EMSCRIPTEN__

// stop_reason is UI_DBG_STOP_REASON_xxx
void webapi_event_stopped(int stop_reason, uint16_t addr) {
#if defined(__EMSCRIPTEN__)
webapi_js_event_stopped(stop_reason, addr);
#else
(void)stop_reason; (void)addr;
#endif
}

void webapi_event_continued(void) {
#if defined(__EMSCRIPTEN__)
webapi_js_event_continued();
#endif
}

void webapi_event_reboot(void) {
#if defined(__EMSCRIPTEN__)
webapi_js_event_reboot();
#endif
}

void webapi_event_reset(void) {
#if defined(__EMSCRIPTEN__)
webapi_js_event_reset();
#endif
}
Loading
Loading