diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-09-19 13:17:14 +1200 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-09-19 13:32:32 +1200 |
commit | bb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e (patch) | |
tree | b26d07ed58aaf7a5230fc3e28c103d616abfa9b8 /arm9/source/main.c | |
parent | 9612c307f00c4313d73fe0c3a86c05c8d8cd514e (diff) | |
download | bedrock-nds-bb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e.zip |
Massive rewrite
This commit rewrites the emulator halfway from scratch to make it
easier to change and maintain in the future. The emulator core was
rewritten to adhere to the released Bedrock specification (earlier
versions implemented an older prototype specification, which is no
longer relevant).
This commit also adds proper support for running multiple concurrent
Bedrock instances. This was previously supported in a limited manner
for the on-screen keyboard, but now works for any regular program as
well, with switching being performed by pressing the L or R bumper
buttons. This is disabled by default, as programs will still need to
be baked into the emulator and hand-loaded.
Diffstat (limited to 'arm9/source/main.c')
-rw-r--r-- | arm9/source/main.c | 168 |
1 files changed, 113 insertions, 55 deletions
diff --git a/arm9/source/main.c b/arm9/source/main.c index 56103da..9dc612c 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -1,122 +1,179 @@ -#include <stdio.h> -#include <time.h> -#include <nds.h> -#include "devices/clock.h" -#include "devices/math.h" -#include "devices/screen.h" -#include "bang.h" -#include "core.h" #include "main.h" +#include "config.h" -#define NUM_BR 5 -Bedrock br[NUM_BR] = {}; +char *system_name = "bedrock-nds/0.1.0"; +char *system_authors = "Ben Bridle"; + +// Reserve memory for all Bedrock instances up front. +Bedrock br[NUM_INSTANCES] = {}; +// The instance currently visible on the main screen. +u32 MAIN_INSTANCE = 0; + +// The NDS has two 2D engines, called the main engine and the sub engine. +// These can be freely allocated to either of the hardware screens, there +// is effectively no difference between the two engines for our purposes. +// The br_main pointer will point to the Bedrock instance currently using +// the main engine (which will always be the current user instance), and +// the br_sub pointer to the instance using the sub engine (which will +// always be the keyboard instance). Bedrock *br_main; Bedrock *br_sub; -bool main_on_bottom = TRUE; +// The main 2D engine always starts on the bottom hardware screen. +bool main_on_bottom = true; -char *system_name = "bedrock-nds/0.1.0-alpha"; -char *system_authors = "Ben Bridle"; +// Bake some Bedrock programs into the emulator. u8 main_program[] = { - #include "../include/cobalt.br.inc" + // #include "../include/alphabet-snake.br.inc" + // #include "../include/clock.br.inc" + #include "../include/cobalt-demo.br.inc" + // #include "../include/system-info.br.inc" }; u8 keyboard_program[] = { #include "../include/keyboard.br.inc" }; -// Change to the next screen layout. -void change_layout(void) { +// Swap the hardware screens. +void swap_screens(void) { lcdSwap(); main_on_bottom = !main_on_bottom; + // Hide the sub program if the main program is on the bottom screen. if (main_on_bottom) { - scr_unmake(&br_sub->scr); + screen_map_to_none(&br_sub->dev.screen); } else { - scr_make_sub(&br_sub->scr); + screen_map_to_sub(&br_sub->dev.screen); } } +// TODO void receive_keyboard_byte(u8 byte) { if (br_main) { - inp_receive_byte(&br_main->inp, byte); + input_receive_key(&br_main->dev.input, byte); } } -bool is_keyboard_open(void) { - return !main_on_bottom; -} - +// TODO void open_keyboard(void) { if (main_on_bottom) { - change_layout(); + swap_screens(); + // Deactivate the pointer on the main instance. + br_main->dev.input.pointer = false; + br_main->dev.input.wake = true; + // Connect the keyboard. + br_sub->dev.stream.local.output.connected = true; } } +// TODO void close_keyboard(void) { if (!main_on_bottom) { - change_layout(); + swap_screens(); + // Disconnect the keyboard. + br_sub->dev.stream.local.output.connected = false; } } +// Make a neighbouring user instance become the new active user instance. +void change_main_instance(int delta) { + screen_map_to_none(&br[MAIN_INSTANCE].dev.screen); + MAIN_INSTANCE = (MAIN_INSTANCE + delta) % USER_INSTANCES; + screen_map_to_main(&br[MAIN_INSTANCE].dev.screen); + br[MAIN_INSTANCE].dev.screen.wake = true; + br_main = &br[MAIN_INSTANCE]; +} + // Scan for input and handle emulator-specific keys. void receive_input(void) { + // This should be the only place in the entire codebase where the + // scanKeys function is called. scanKeys(); - if (keysDown() & KEY_SELECT) change_layout(); + // Swap screens when the select key is pressed. + if (keysDown() & KEY_SELECT) swap_screens(); + if (SWITCH_BETWEEN_INSTANCES) { + if (keysDown() & KEY_L) change_main_instance(-1); + if (keysDown() & KEY_R) change_main_instance(1); + } } +// ------ MAIN ----------------------------------------------------------------- + int main(void) { #define ALIVE(br) (br && br->alive) #define DEAD(br) (br && !br->alive) #define AWAKE(br) (br && br->alive && br->awake) #define ASLEEP(br) (br && br->alive && !br->awake) - // Set memory identifiers and system device strings. - for (int i=0; i<NUM_BR; i++) { - br[i].mem.id = i+1; - br[i].sys.name.mem = (u8*) &system_name[0]; - br[i].sys.authors.mem = (u8*) &system_authors[0]; + // Initialise all Bedrock instances. + for (int i=0; i<NUM_INSTANCES; i++) { + br_reset(&br[i]); + br[i].dev.memory.id = i+1; + readbuf_populate(&br[i].dev.system.name, (u8*) system_name); + readbuf_populate(&br[i].dev.system.authors, (u8*) system_authors); } - init_screens(); - init_clock(); - init_filesystem(); + // Initialise functions used by devices. + init_nds_clock(); + init_nds_screens(); + init_nds_filesystem(); + + // Start with the main screen on the bottom. lcdMainOnBottom(); - // Load program - start_br(&br[0], main_program, sizeof(main_program)); - start_br(&br[1], keyboard_program, sizeof(keyboard_program)); - br_main = &br[0]; - br_sub = &br[1]; - scr_make_main(&br_main->scr); + // Initialise the debug console. + // TODO: Remove. + // consoleDemoInit(); + + // Load program. + br_main = &br[MAIN_INSTANCE]; + br_sub = &br[KEY_INSTANCE]; + br_load(&br[MAIN_INSTANCE], main_program, sizeof(main_program)); + br_load(&br[KEY_INSTANCE], keyboard_program, sizeof(keyboard_program)); + + // We map the main instance to the main screen, but we don't map the + // sub instance because we don't want it to be visible just yet. + screen_map_to_main(&br_main->dev.screen); + + // ------ EVENT LOOP ------------------------------------------------------- while (1) { - if (AWAKE(br_main)) run_br(br_main); - if (AWAKE(br_sub)) run_br(br_sub); + if (AWAKE(br_main)) br_run(br_main); + if (AWAKE(br_sub)) br_run(br_sub); if (DEAD(br_main)) br_main = NULL; if (DEAD(br_sub)) br_sub = NULL; - bool main_flip = ASLEEP(br_main) && br_main->scr.dirty; - bool sub_flip = ASLEEP(br_sub) && br_sub->scr.dirty; - if (main_flip || sub_flip) swiWaitForVBlank(); - if (main_flip) { flip_buffer(br_main->scr.nds); br_main->scr.dirty = false; } - if (sub_flip) { flip_buffer(br_sub->scr.nds); br_sub->scr.dirty = false; } + // Check if screen buffers need to be flipped. + // Only flip buffers if the instance is asleep. + bool flip_main = ASLEEP(br_main) && br_main->dev.screen.dirty; + bool flip_sub = ASLEEP(br_sub) && br_sub->dev.screen.dirty; + // Wait for next frame to avoid running too fast. + if (flip_main || flip_sub) swiWaitForVBlank(); + if (flip_main) { + ndsscreen_flip_buffers(br_main->dev.screen.nds); + br_main->dev.screen.dirty = false; + } + if (flip_sub) { + ndsscreen_flip_buffers(br_sub->dev.screen.nds); + br_sub->dev.screen.dirty = false; + } rouse: receive_input(); + // Receive gamepad and touch input if main is on bottom screen. if (ALIVE(br_main) && main_on_bottom) { - inp_read_navigation(&br_main->inp); - inp_read_gamepad(&br_main->inp); - inp_read_touch(&br_main->inp); + input_update_gamepad(&br_main->dev.input); + input_update_touch(&br_main->dev.input); } + // Receive gamepad and touch input if sub is on bottom screen. if (ALIVE(br_sub) && !main_on_bottom) { - inp_read_navigation(&br_sub->inp); - inp_read_gamepad(&br_sub->inp); - inp_read_touch(&br_sub->inp); + input_update_gamepad(&br_sub->dev.input); + input_update_touch(&br_sub->dev.input); } - if (ASLEEP(br_main)) rouse_br(br_main); - if (ASLEEP(br_sub)) rouse_br(br_sub); + if (ASLEEP(br_main)) br_rouse(br_main); + if (ASLEEP(br_sub)) br_rouse(br_sub); + if (ASLEEP(br_main) && ASLEEP(br_sub)) { // Sleep until next keypad event or clock tick. swiIntrWait(1, IRQ_KEYS | IRQ_TIMER0); @@ -124,6 +181,7 @@ int main(void) { } if (!br_main) { + printf("SHUTTING DOWN\n"); systemShutDown(); return 0; } |