1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#include "main.h"
#include "config.h"
char *system_name = "bedrock-nds/1.0.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;
// The main 2D engine always starts on the bottom hardware screen.
bool main_on_bottom = true;
// Bake some Bedrock programs into the emulator.
u8 main_program[] = {
// #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"
};
// 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) {
screen_map_to_none(&br_sub->dev.screen);
} else {
screen_map_to_sub(&br_sub->dev.screen);
}
}
// TODO
void receive_keyboard_byte(u8 byte) {
if (br_main) {
input_receive_key(&br_main->dev.input, byte);
}
}
// TODO
void open_keyboard(void) {
if (main_on_bottom) {
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) {
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();
// 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)
// 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);
}
// Initialise functions used by devices.
init_nds_clock();
init_nds_screens();
init_nds_filesystem();
// Start with the main screen on the bottom.
lcdMainOnBottom();
// 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)) 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;
// 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) {
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) {
input_update_gamepad(&br_sub->dev.input);
input_update_touch(&br_sub->dev.input);
}
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);
goto rouse;
}
if (!br_main) {
printf("SHUTTING DOWN\n");
systemShutDown();
return 0;
}
}
}
|