From 45af43263c465d8fd50f61c0d63c94662026c669 Mon Sep 17 00:00:00 2001 From: scemino Date: Sun, 26 Feb 2023 10:39:00 +0100 Subject: [PATCH 01/10] nes: start NES implementation --- examples/sokol/CMakeLists.txt | 9 +++++ examples/sokol/nes.c | 76 +++++++++++++++++++++++++++++++++++ fips.yml | 2 +- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 examples/sokol/nes.c diff --git a/examples/sokol/CMakeLists.txt b/examples/sokol/CMakeLists.txt index 31238e72..4c6f44f0 100644 --- a/examples/sokol/CMakeLists.txt +++ b/examples/sokol/CMakeLists.txt @@ -231,3 +231,12 @@ fips_begin_app(lc80 windowed) endif() fips_deps(roms ui) fips_end_app() + +fips_ide_group(examples/nes) +fips_begin_app(nes windowed) + fips_files(nes.c) + if (FIPS_IOS) + fips_files(ios-info.plist) + endif() + fips_deps(roms common) +fips_end_app() \ No newline at end of file diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c new file mode 100644 index 00000000..a53b3b1f --- /dev/null +++ b/examples/sokol/nes.c @@ -0,0 +1,76 @@ +/* + nes.c + + NES. +*/ +#define CHIPS_IMPL +#include "chips/chips_common.h" +#include "common.h" +#include "chips/clk.h" +#include "chips/m6502.h" +#include "chips/r2c02.h" +#include "systems/nes.h" + +static struct { + nes_t nes; + uint32_t frame_time_us; + uint32_t ticks; + double emu_time_ms; +} state; + +static void draw_status_bar(void); + +static void app_init(void) { + nes_init(&state.nes); + gfx_init(&(gfx_desc_t){ + .display_info = nes_display_info(&state.nes), + }); + clock_init(); + prof_init(); +} + +static void app_frame(void) { + state.frame_time_us = clock_frame_time(); + const uint64_t emu_start_time = stm_now(); + state.emu_time_ms = stm_ms(stm_since(emu_start_time)); + draw_status_bar(); + gfx_draw(nes_display_info(&state.nes)); +} + +static void app_cleanup(void) { + nes_discard(&state.nes); + gfx_shutdown(); + sargs_shutdown(); +} + +static void draw_status_bar(void) { + prof_push(PROF_EMU, (float)state.emu_time_ms); + prof_stats_t emu_stats = prof_stats(PROF_EMU); + + const uint32_t text_color = 0xFFFFFFFF; + + const float w = sapp_widthf(); + const float h = sapp_heightf(); + sdtx_canvas(w, h); + sdtx_origin(1.0f, (h / 8.0f) - 3.5f); + + sdtx_font(0); + sdtx_color1i(text_color); + sdtx_pos(0.0f, 1.5f); + sdtx_printf("frame:%.2fms emu:%.2fms (min:%.2fms max:%.2fms) ticks:%d", (float)state.frame_time_us * 0.001f, emu_stats.avg_val, emu_stats.min_val, emu_stats.max_val, state.ticks); +} + +sapp_desc sokol_main(int argc, char* argv[]) { + sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); + const chips_display_info_t info = nes_display_info(0); + return (sapp_desc) { + .init_cb = app_init, + .frame_cb = app_frame, + .cleanup_cb = app_cleanup, + .width = info.screen.width, + .height = info.screen.height, + .window_title = "NES", + .icon.sokol_default = true, + .logger.func = slog_func, + }; +} diff --git a/fips.yml b/fips.yml index b3e2006f..de262eee 100644 --- a/fips.yml +++ b/fips.yml @@ -8,7 +8,7 @@ imports: fips-imgui: git: https://github.com/fips-libs/fips-imgui.git chips: - git: https://github.com/floooh/chips + git: https://github.com/scemino/chips sokol: git: https://github.com/floooh/sokol sokol-tools-bin: From 9c04a2c376656cbf659ba4f9f06b991fc04de44f Mon Sep 17 00:00:00 2001 From: scemino Date: Wed, 1 Mar 2023 22:14:38 +0100 Subject: [PATCH 02/10] nes: add UI --- examples/sokol/CMakeLists.txt | 11 ++- examples/sokol/nes-ui-impl.cc | 24 +++++++ examples/sokol/nes.c | 125 +++++++++++++++++++++++++++++----- 3 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 examples/sokol/nes-ui-impl.cc diff --git a/examples/sokol/CMakeLists.txt b/examples/sokol/CMakeLists.txt index 4c6f44f0..5a998110 100644 --- a/examples/sokol/CMakeLists.txt +++ b/examples/sokol/CMakeLists.txt @@ -239,4 +239,13 @@ fips_begin_app(nes windowed) fips_files(ios-info.plist) endif() fips_deps(roms common) -fips_end_app() \ No newline at end of file +fips_end_app() + +fips_begin_app(nes-ui windowed) + fips_files(nes.c nes-ui-impl.cc) + if (FIPS_IOS) + fips_files(ios-info.plist) + endif() + fips_deps(roms common ui) +fips_end_app() +target_compile_definitions(nes-ui PRIVATE CHIPS_USE_UI) diff --git a/examples/sokol/nes-ui-impl.cc b/examples/sokol/nes-ui-impl.cc new file mode 100644 index 00000000..325cb364 --- /dev/null +++ b/examples/sokol/nes-ui-impl.cc @@ -0,0 +1,24 @@ +/* + nes-ui.c + UI implementation for nes.c +*/ +#include "chips/chips_common.h" +#include "chips/m6502.h" +#include "chips/r2c02.h" +#include "chips/clk.h" +#include "chips/mem.h" +#include "systems/nes.h" +#define UI_DASM_USE_M6502 +#define UI_DBG_USE_M6502 +#define CHIPS_UTIL_IMPL +#include "util/m6502dasm.h" +#define CHIPS_UI_IMPL +#include "imgui.h" +#include "ui/ui_util.h" +#include "ui/ui_chip.h" +#include "ui/ui_memedit.h" +#include "ui/ui_memmap.h" +#include "ui/ui_dasm.h" +#include "ui/ui_dbg.h" +#include "ui/ui_m6502.h" +#include "ui/ui_nes.h" diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index a53b3b1f..72639e39 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -3,44 +3,104 @@ NES. */ +#include #define CHIPS_IMPL #include "chips/chips_common.h" #include "common.h" #include "chips/clk.h" +#include "chips/mem.h" #include "chips/m6502.h" #include "chips/r2c02.h" #include "systems/nes.h" +#if defined(CHIPS_USE_UI) + #define UI_DBG_USE_M6502 + #include "ui.h" + #include "ui/ui_chip.h" + #include "ui/ui_memedit.h" + #include "ui/ui_memmap.h" + #include "ui/ui_dasm.h" + #include "ui/ui_dbg.h" + #include "ui/ui_m6502.h" + #include "ui/ui_nes.h" +#endif static struct { - nes_t nes; - uint32_t frame_time_us; - uint32_t ticks; - double emu_time_ms; + nes_t nes; + uint32_t frame_time_us; + uint32_t ticks; + double emu_time_ms; + #if defined(CHIPS_USE_UI) + ui_nes_t ui; + #endif } state; +#ifdef CHIPS_USE_UI +static void ui_draw_cb(void); +#endif + static void draw_status_bar(void); static void app_init(void) { - nes_init(&state.nes); - gfx_init(&(gfx_desc_t){ - .display_info = nes_display_info(&state.nes), - }); - clock_init(); - prof_init(); + nes_init(&state.nes, &(nes_desc_t) { + #if defined(CHIPS_USE_UI) + .debug = ui_nes_get_debug(&state.ui) + #endif + }); + gfx_init(&(gfx_desc_t){ + #ifdef CHIPS_USE_UI + .draw_extra_cb = ui_draw, + #endif + .display_info = nes_display_info(&state.nes), + }); + clock_init(); + prof_init(); + fs_init(); + +#ifdef CHIPS_USE_UI + ui_init(ui_draw_cb); + ui_nes_init(&state.ui, &(ui_nes_desc_t){ + .nes = &state.nes, + .dbg_texture = { + .create_cb = gfx_create_texture, + .update_cb = gfx_update_texture, + .destroy_cb = gfx_destroy_texture, + }, + .dbg_keys = { + .cont = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F5), .name = "F5" }, + .stop = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F5), .name = "F5" }, + .step_over = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F6), .name = "F6" }, + .step_into = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F7), .name = "F7" }, + .step_tick = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F8), .name = "F8" }, + .toggle_breakpoint = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F9), .name = "F9" } + } + }); +#endif + + if (sargs_exists("file")) { + fs_start_load_file(FS_SLOT_IMAGE, sargs_value("file")); + } } +static void handle_file_loading(void); + static void app_frame(void) { state.frame_time_us = clock_frame_time(); const uint64_t emu_start_time = stm_now(); + state.ticks = nes_exec(&state.nes, state.frame_time_us); state.emu_time_ms = stm_ms(stm_since(emu_start_time)); draw_status_bar(); gfx_draw(nes_display_info(&state.nes)); + handle_file_loading(); } static void app_cleanup(void) { - nes_discard(&state.nes); - gfx_shutdown(); - sargs_shutdown(); + nes_discard(&state.nes); + #ifdef CHIPS_USE_UI + ui_nes_discard(&state.ui); + ui_discard(); + #endif + gfx_shutdown(); + sargs_shutdown(); } static void draw_status_bar(void) { @@ -60,15 +120,48 @@ static void draw_status_bar(void) { sdtx_printf("frame:%.2fms emu:%.2fms (min:%.2fms max:%.2fms) ticks:%d", (float)state.frame_time_us * 0.001f, emu_stats.avg_val, emu_stats.min_val, emu_stats.max_val, state.ticks); } +static void handle_file_loading(void) { + fs_dowork(); + if (fs_success(FS_SLOT_IMAGE)) { + bool load_success = false; + if (fs_ext(FS_SLOT_IMAGE, "nes")) { + load_success = nes_insert_cart(&state.nes, fs_data(FS_SLOT_IMAGE)); + } + fs_reset(FS_SLOT_IMAGE); + } +} + +void app_input(const sapp_event* event) { + // accept dropped files also when ImGui grabs input + if (event->type == SAPP_EVENTTYPE_FILES_DROPPED) { + fs_start_load_dropped_file(FS_SLOT_IMAGE); + } + #ifdef CHIPS_USE_UI + if (ui_input(event)) { + // input was handled by UI + return; + } + #endif +} + +#if defined(CHIPS_USE_UI) +static void ui_draw_cb(void) { + ui_nes_draw(&state.ui); +} +#endif + sapp_desc sokol_main(int argc, char* argv[]) { sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); - const chips_display_info_t info = nes_display_info(0); + //const chips_display_info_t info = nes_display_info(0); return (sapp_desc) { .init_cb = app_init, + .event_cb = app_input, .frame_cb = app_frame, .cleanup_cb = app_cleanup, - .width = info.screen.width, - .height = info.screen.height, + // .width = info.screen.width, + // .height = info.screen.height, + .width = 800, + .height = 600, .window_title = "NES", .icon.sokol_default = true, .logger.func = slog_func, From 4bedde1ea1aff5349d768cfebac958950fcdcde2 Mon Sep 17 00:00:00 2001 From: scemino Date: Sun, 5 Mar 2023 15:44:15 +0100 Subject: [PATCH 03/10] nes: add NES input handling --- examples/sokol/nes.c | 50 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index 72639e39..b33609d0 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -108,7 +108,6 @@ static void draw_status_bar(void) { prof_stats_t emu_stats = prof_stats(PROF_EMU); const uint32_t text_color = 0xFFFFFFFF; - const float w = sapp_widthf(); const float h = sapp_heightf(); sdtx_canvas(w, h); @@ -122,11 +121,21 @@ static void draw_status_bar(void) { static void handle_file_loading(void) { fs_dowork(); - if (fs_success(FS_SLOT_IMAGE)) { + const uint32_t load_delay_frames = 120; + if (fs_success(FS_SLOT_IMAGE) && clock_frame_count_60hz() > load_delay_frames) { + bool load_success = false; if (fs_ext(FS_SLOT_IMAGE, "nes")) { load_success = nes_insert_cart(&state.nes, fs_data(FS_SLOT_IMAGE)); } + if (load_success) { + if (clock_frame_count_60hz() > (load_delay_frames + 10)) { + gfx_flash_success(); + } + } + else { + gfx_flash_error(); + } fs_reset(FS_SLOT_IMAGE); } } @@ -136,12 +145,40 @@ void app_input(const sapp_event* event) { if (event->type == SAPP_EVENTTYPE_FILES_DROPPED) { fs_start_load_dropped_file(FS_SLOT_IMAGE); } - #ifdef CHIPS_USE_UI +#ifdef CHIPS_USE_UI if (ui_input(event)) { - // input was handled by UI - return; + // input was handled by UI + return; + } +#endif + switch (event->type) { + case SAPP_EVENTTYPE_KEY_DOWN: + case SAPP_EVENTTYPE_KEY_UP: { + int c; + switch (event->key_code) { + case SAPP_KEYCODE_LEFT: c = 0x01; break; + case SAPP_KEYCODE_RIGHT: c = 0x02; break; + case SAPP_KEYCODE_DOWN: c = 0x03; break; + case SAPP_KEYCODE_UP: c = 0x04; break; + case SAPP_KEYCODE_ENTER: c = 0x05; break; + case SAPP_KEYCODE_F: c = 0x06; break; + case SAPP_KEYCODE_D: c = 0x07; break; + case SAPP_KEYCODE_S: c = 0x08; break; + default: c = 0x00; break; + } + if (c) { + if (event->type == SAPP_EVENTTYPE_KEY_DOWN) { + nes_key_down(&state.nes, c); + } + else { + nes_key_up(&state.nes, c); + } + } + break; + default: + break; + } } - #endif } #if defined(CHIPS_USE_UI) @@ -164,6 +201,7 @@ sapp_desc sokol_main(int argc, char* argv[]) { .height = 600, .window_title = "NES", .icon.sokol_default = true, + .enable_dragndrop = true, .logger.func = slog_func, }; } From da6f7e0abde16adb0428475c61758c2d25f6163a Mon Sep 17 00:00:00 2001 From: scemino Date: Wed, 15 Mar 2023 21:45:36 +0100 Subject: [PATCH 04/10] nes: add nes test with nes system --- examples/sokol/nes.c | 4 ---- tests/CMakeLists.txt | 7 ++++++ tests/nes-test1.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 tests/nes-test1.c diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index b33609d0..e5c65791 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -8,7 +8,6 @@ #include "chips/chips_common.h" #include "common.h" #include "chips/clk.h" -#include "chips/mem.h" #include "chips/m6502.h" #include "chips/r2c02.h" #include "systems/nes.h" @@ -189,14 +188,11 @@ static void ui_draw_cb(void) { sapp_desc sokol_main(int argc, char* argv[]) { sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv }); - //const chips_display_info_t info = nes_display_info(0); return (sapp_desc) { .init_cb = app_init, .event_cb = app_input, .frame_cb = app_frame, .cleanup_cb = app_cleanup, - // .width = info.screen.width, - // .height = info.screen.height, .width = 800, .height = 600, .window_title = "NES", diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9bc0889f..daa60a45 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -65,6 +65,13 @@ fips_begin_app(m6502-perfect cmdline) ) fips_end_app() +fips_begin_app(nes-test1 cmdline) + fips_files(nes-test1.c) + fips_dir(nestest) + fips_generate(FROM nestest.log.txt TYPE nestestlog HEADER nestestlog.h) + fipsutil_embed(dump.yml dump.h) +fips_end_app() + fips_begin_app(z80-fuse cmdline) fips_files(z80-fuse.c) fips_dir(fuse) diff --git a/tests/nes-test1.c b/tests/nes-test1.c new file mode 100644 index 00000000..88cd0198 --- /dev/null +++ b/tests/nes-test1.c @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// nes-test1.c +// +// Tests NES state after documented instructions. +//------------------------------------------------------------------------------ +#define CHIPS_IMPL +#include "chips/chips_common.h" +#include "chips/clk.h" +#include "chips/m6502.h" +#include "chips/r2c02.h" +#include "systems/nes.h" +#include +#include "nestest/dump.h" +#include "nestest/nestestlog.h" +#include "test.h" + +int main() { + test_begin("NES TEST (NES)"); + test_no_verbose(); + + /* initialize the NES */ + nes_t nes; + nes_init(&nes, &(nes_desc_t){}); + nes_insert_cart(&nes, (chips_range_t){ + .ptr = dump_nestest_nes, + .size = sizeof(dump_nestest_nes), + }); + /* patch the test start address into the RESET vector */ + nes.cart.rom[0x3FFC] = 0x00; + dump_nestest_nes[0x3FFD] = 0xC0; + /* set RESET vector and run through RESET sequence */ + uint64_t pins = nes.pins; + for (int i = 0; i < 7; i++) { + pins = _nes_tick(&nes, pins); + } + nes.cpu.P &= ~M6502_ZF; + + /* run the test */ + int num_tests = sizeof(state_table) / sizeof(cpu_state); + for (int i = 0; i < num_tests; i++) { + cpu_state* state = &state_table[i]; + test(state->desc); + T(nes.cpu.PC == state->PC); + T(nes.cpu.A == state->A); + T(nes.cpu.X == state->X); + T(nes.cpu.Y == state->Y); + T((nes.cpu.P & ~(M6502_XF|M6502_BF)) == (state->P & ~(M6502_XF|M6502_BF))); + T(nes.cpu.S == state->S); + if (test_failed()) { + printf("### NESTEST failed at pos %d, PC=0x%04X: %s\n", i, nes.cpu.PC, state->desc); + } + do { + pins = _nes_tick(&nes, pins); + } while (0 == (pins & M6502_SYNC)); + } + return test_end(); +} From 2957f4b64173643dd4c25b0ac032aca10e76e7df Mon Sep 17 00:00:00 2001 From: scemino Date: Wed, 15 Mar 2023 21:54:09 +0100 Subject: [PATCH 05/10] nes: add nes test with nes system --- tests/nes-test1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/nes-test1.c b/tests/nes-test1.c index 88cd0198..fe58b97e 100644 --- a/tests/nes-test1.c +++ b/tests/nes-test1.c @@ -27,7 +27,8 @@ int main() { }); /* patch the test start address into the RESET vector */ nes.cart.rom[0x3FFC] = 0x00; - dump_nestest_nes[0x3FFD] = 0xC0; + dump_nestest_nes[0x3FFD] = 0xc0; + nes.cart.rom[0x3FFD] = 0xC0; /* set RESET vector and run through RESET sequence */ uint64_t pins = nes.pins; for (int i = 0; i < 7; i++) { From fd80383bada068b1c997bec1ffe39b1b7fd9128b Mon Sep 17 00:00:00 2001 From: scemino Date: Fri, 24 Mar 2023 21:45:51 +0100 Subject: [PATCH 06/10] nes: add nes audio --- examples/sokol/nes-ui-impl.cc | 1 + examples/sokol/nes.c | 16 ++++++++++++++++ fips-files/verbs/webpage.py | 2 ++ 3 files changed, 19 insertions(+) diff --git a/examples/sokol/nes-ui-impl.cc b/examples/sokol/nes-ui-impl.cc index 325cb364..77d84124 100644 --- a/examples/sokol/nes-ui-impl.cc +++ b/examples/sokol/nes-ui-impl.cc @@ -14,6 +14,7 @@ #include "util/m6502dasm.h" #define CHIPS_UI_IMPL #include "imgui.h" +#include "ui/ui_audio.h" #include "ui/ui_util.h" #include "ui/ui_chip.h" #include "ui/ui_memedit.h" diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index e5c65791..0b6ea23f 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -4,6 +4,7 @@ NES. */ #include +#include #define CHIPS_IMPL #include "chips/chips_common.h" #include "common.h" @@ -14,6 +15,7 @@ #if defined(CHIPS_USE_UI) #define UI_DBG_USE_M6502 #include "ui.h" + #include "ui/ui_audio.h" #include "ui/ui_chip.h" #include "ui/ui_memedit.h" #include "ui/ui_memmap.h" @@ -39,8 +41,18 @@ static void ui_draw_cb(void); static void draw_status_bar(void); +// audio-streaming callback +static void push_audio(const float* samples, int num_samples, void* user_data) { + (void)user_data; + saudio_push(samples, num_samples); +} + static void app_init(void) { nes_init(&state.nes, &(nes_desc_t) { + .audio = { + .callback = { .func = push_audio }, + .sample_rate = saudio_sample_rate(), + }, #if defined(CHIPS_USE_UI) .debug = ui_nes_get_debug(&state.ui) #endif @@ -53,6 +65,9 @@ static void app_init(void) { }); clock_init(); prof_init(); + saudio_setup(&(saudio_desc){ + .logger.func = slog_func, + }); fs_init(); #ifdef CHIPS_USE_UI @@ -98,6 +113,7 @@ static void app_cleanup(void) { ui_nes_discard(&state.ui); ui_discard(); #endif + saudio_shutdown(); gfx_shutdown(); sargs_shutdown(); } diff --git a/fips-files/verbs/webpage.py b/fips-files/verbs/webpage.py index 4a4dcec0..99c9983e 100644 --- a/fips-files/verbs/webpage.py +++ b/fips-files/verbs/webpage.py @@ -25,6 +25,7 @@ 'bombjack', 'bombjack-ui', 'pacman', 'pacman-ui', 'pengo', 'pengo-ui', + 'nes', 'nes-ui', 'lc80', 'ext' ] @@ -50,6 +51,7 @@ { 'type':'emu', 'title':'Robotron Z1013', 'system':'z1013', 'url':'z1013.html', 'img':'z1013/z1013.jpg', 'note':'' }, { 'type':'emu', 'title':'Robotron Z9001', 'system':'z9001', 'url':'z9001.html', 'img':'z9001/z9001.jpg', 'note':'(with BASIC and RAM modules)' }, { 'type':'emu', 'title':'Robotron KC87', 'system':'z9001', 'url':'z9001.html?type=kc87', 'img':'z9001/kc87.jpg', 'note':'BASIC[Enter]' }, + { 'type':'emu', 'title':'NES', 'system':'nes', 'url':'nes.html', 'img':'nes/nes.jpg', 'note':'' }, { 'type':'demo', 'title':'FORTH (KC85/4)', 'system':'kc854', 'url':'kc854.html?mod=m026&mod_image=kc85/forth.853&input=SWITCH%208%20C1%0AFORTH%0A', 'img':'kc85/forth.jpg', 'note':'' }, { 'type':'demo', 'title':'FORTH (Z1013)', 'system':'z1013', 'url':'z1013.html?type=z1013_64&file=z1013/z1013_forth.z80', 'img':'z1013/z1013_forth.jpg', 'note':''}, { 'type':'demo', 'title':'BASIC (Z1013)', 'system':'z1013', 'url':'z1013.html?type=z1013_64&file=z1013/kc_basic.z80', 'img':'z1013/z1013_basic.jpg', 'note':''}, From 34f8dcde9272809ef43af12f64f2db7f02dc6582 Mon Sep 17 00:00:00 2001 From: scemino Date: Sat, 25 Mar 2023 22:15:12 +0100 Subject: [PATCH 07/10] nes: add pad/cart status --- examples/sokol/nes.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index 0b6ea23f..b7e0444f 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -123,11 +123,43 @@ static void draw_status_bar(void) { prof_stats_t emu_stats = prof_stats(PROF_EMU); const uint32_t text_color = 0xFFFFFFFF; + const uint32_t cart_active = 0xFF00EE00; + const uint32_t cart_inactive = 0xFF006600; + const uint32_t pad_active = 0xFFFFEE00; + const uint32_t pad_inactive = 0xFF886600; + const float w = sapp_widthf(); const float h = sapp_heightf(); sdtx_canvas(w, h); sdtx_origin(1.0f, (h / 8.0f) - 3.5f); + sdtx_puts("PAD: "); + sdtx_font(1); + const uint8_t padmask = nes_pad_mask(&state.nes); + sdtx_color1i((padmask & NES_PAD_LEFT) ? pad_active : pad_inactive); + sdtx_putc(0x88); // arrow left + sdtx_color1i((padmask & NES_PAD_RIGHT) ? pad_active : pad_inactive); + sdtx_putc(0x89); // arrow right + sdtx_color1i((padmask & NES_PAD_UP) ? pad_active : pad_inactive); + sdtx_putc(0x8B); // arrow up + sdtx_color1i((padmask & NES_PAD_DOWN) ? pad_active : pad_inactive); + sdtx_putc(0x8A); // arrow down + sdtx_color1i((padmask & NES_PAD_START) ? pad_active : pad_inactive); + sdtx_putc(0x87); // btn + sdtx_color1i((padmask & NES_PAD_SEL) ? pad_active : pad_inactive); + sdtx_putc(0x87); // btn + sdtx_color1i((padmask & NES_PAD_B) ? pad_active : pad_inactive); + sdtx_putc(0x87); // btn + sdtx_color1i((padmask & NES_PAD_A) ? pad_active : pad_inactive); + sdtx_putc(0x87); // btn + sdtx_font(0); + + // cartridge inserted LED + sdtx_color1i(text_color); + sdtx_puts(" CART: "); + sdtx_color1i(nes_cartridge_inserted(&state.nes) ? cart_active : cart_inactive); + sdtx_putc(0xCF); // filled circle + sdtx_font(0); sdtx_color1i(text_color); sdtx_pos(0.0f, 1.5f); @@ -138,7 +170,7 @@ static void handle_file_loading(void) { fs_dowork(); const uint32_t load_delay_frames = 120; if (fs_success(FS_SLOT_IMAGE) && clock_frame_count_60hz() > load_delay_frames) { - + bool load_success = false; if (fs_ext(FS_SLOT_IMAGE, "nes")) { load_success = nes_insert_cart(&state.nes, fs_data(FS_SLOT_IMAGE)); From e4f55a418c9c03171bda34a6565eef1861dad7cb Mon Sep 17 00:00:00 2001 From: scemino Date: Sat, 25 Mar 2023 22:45:44 +0100 Subject: [PATCH 08/10] nes: add NES snapshots --- examples/sokol/nes-ui-impl.cc | 1 + examples/sokol/nes.c | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/examples/sokol/nes-ui-impl.cc b/examples/sokol/nes-ui-impl.cc index 77d84124..47140e6a 100644 --- a/examples/sokol/nes-ui-impl.cc +++ b/examples/sokol/nes-ui-impl.cc @@ -22,4 +22,5 @@ #include "ui/ui_dasm.h" #include "ui/ui_dbg.h" #include "ui/ui_m6502.h" +#include "ui/ui_snapshot.h" #include "ui/ui_nes.h" diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index b7e0444f..81706ddb 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -22,9 +22,15 @@ #include "ui/ui_dasm.h" #include "ui/ui_dbg.h" #include "ui/ui_m6502.h" + #include "ui/ui_snapshot.h" #include "ui/ui_nes.h" #endif +typedef struct { + uint32_t version; + nes_t nes; +} nes_snapshot_t; + static struct { nes_t nes; uint32_t frame_time_us; @@ -32,11 +38,14 @@ static struct { double emu_time_ms; #if defined(CHIPS_USE_UI) ui_nes_t ui; + nes_snapshot_t snapshots[UI_SNAPSHOT_MAX_SLOTS]; #endif } state; #ifdef CHIPS_USE_UI static void ui_draw_cb(void); +static bool ui_load_snapshot(size_t slot_index); +static void ui_save_snapshot(size_t slot_index); #endif static void draw_status_bar(void); @@ -79,6 +88,13 @@ static void app_init(void) { .update_cb = gfx_update_texture, .destroy_cb = gfx_destroy_texture, }, + .snapshot = { + .load_cb = ui_load_snapshot, + .save_cb = ui_save_snapshot, + .empty_slot_screenshot = { + .texture = gfx_shared_empty_snapshot_texture(), + } + }, .dbg_keys = { .cont = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F5), .name = "F5" }, .stop = { .keycode = simgui_map_keycode(SAPP_KEYCODE_F5), .name = "F5" }, @@ -232,6 +248,32 @@ void app_input(const sapp_event* event) { static void ui_draw_cb(void) { ui_nes_draw(&state.ui); } + +static void ui_update_snapshot_screenshot(size_t slot) { + ui_snapshot_screenshot_t screenshot = { + .texture = gfx_create_screenshot_texture(nes_display_info(&state.snapshots[slot].nes)) + }; + ui_snapshot_screenshot_t prev_screenshot = ui_snapshot_set_screenshot(&state.ui.snapshot, slot, screenshot); + if (prev_screenshot.texture) { + gfx_destroy_texture(prev_screenshot.texture); + } +} + +static bool ui_load_snapshot(size_t slot) { + bool success = false; + if ((slot < UI_SNAPSHOT_MAX_SLOTS) && (state.ui.snapshot.slots[slot].valid)) { + success = nes_load_snapshot(&state.nes, state.snapshots[slot].version, &state.snapshots[slot].nes); + } + return success; +} + +static void ui_save_snapshot(size_t slot) { + if (slot < UI_SNAPSHOT_MAX_SLOTS) { + state.snapshots[slot].version = nes_save_snapshot(&state.nes, &state.snapshots[slot].nes); + ui_update_snapshot_screenshot(slot); + fs_save_snapshot("nes", slot, (chips_range_t){ .ptr = &state.snapshots[slot], sizeof(nes_snapshot_t) }); + } +} #endif sapp_desc sokol_main(int argc, char* argv[]) { From 7807b6a35739a3ed94d658da0d0157ff28c66c19 Mon Sep 17 00:00:00 2001 From: scemino Date: Tue, 9 Apr 2024 21:01:36 +0200 Subject: [PATCH 09/10] nes: update pass_action --- examples/sokol/nes-ui-impl.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/sokol/nes-ui-impl.cc b/examples/sokol/nes-ui-impl.cc index 47140e6a..173f534a 100644 --- a/examples/sokol/nes-ui-impl.cc +++ b/examples/sokol/nes-ui-impl.cc @@ -1,6 +1,5 @@ /* - nes-ui.c - UI implementation for nes.c + UI implementation for nes.c, this must live in a .cc file. */ #include "chips/chips_common.h" #include "chips/m6502.h" From e11477a6434e5aa5a95754d3bfcdbc1ef3266c93 Mon Sep 17 00:00:00 2001 From: scemino Date: Tue, 9 Apr 2024 21:15:28 +0200 Subject: [PATCH 10/10] nes: use ui methods --- examples/sokol/nes.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/sokol/nes.c b/examples/sokol/nes.c index 81706ddb..37ea140b 100644 --- a/examples/sokol/nes.c +++ b/examples/sokol/nes.c @@ -84,15 +84,15 @@ static void app_init(void) { ui_nes_init(&state.ui, &(ui_nes_desc_t){ .nes = &state.nes, .dbg_texture = { - .create_cb = gfx_create_texture, - .update_cb = gfx_update_texture, - .destroy_cb = gfx_destroy_texture, + .create_cb = ui_create_texture, + .update_cb = ui_update_texture, + .destroy_cb = ui_destroy_texture, }, .snapshot = { .load_cb = ui_load_snapshot, .save_cb = ui_save_snapshot, .empty_slot_screenshot = { - .texture = gfx_shared_empty_snapshot_texture(), + .texture = ui_shared_empty_snapshot_texture(), } }, .dbg_keys = { @@ -251,11 +251,11 @@ static void ui_draw_cb(void) { static void ui_update_snapshot_screenshot(size_t slot) { ui_snapshot_screenshot_t screenshot = { - .texture = gfx_create_screenshot_texture(nes_display_info(&state.snapshots[slot].nes)) + .texture = ui_create_screenshot_texture(nes_display_info(&state.snapshots[slot].nes)) }; ui_snapshot_screenshot_t prev_screenshot = ui_snapshot_set_screenshot(&state.ui.snapshot, slot, screenshot); if (prev_screenshot.texture) { - gfx_destroy_texture(prev_screenshot.texture); + ui_destroy_texture(prev_screenshot.texture); } }