Skip to content

Commit d67d1bd

Browse files
authored
feat: Update nes/gbc/sms/genesis emulators to use shared memory (#99)
* Add `shared_memory` component for sharing memory between the emulators more easily * Refactored all emulators to use new shared memory for majority of state and data. This means each emulator component now has less than 5k of D/IRAM in use that is not from shared memory. * Update to have better clear screen function that wont have any race conditions and wont destroy buffers. Update video task to have simple clear screen function if passed nullptr. * Update cmake lists to use newer version of cmake * Update to hid roms from rom list if their emulator is not enabled for easier / faster iteration and testing when adding / modifying emulators * Update espp to latest Shared memory means that it is not easier to add more emulators (such as msx and doom) since the emulators no longer fill up the internal ram. This does require in some cases modifying the emulators to leverage the shared memory, but this is a one-time operation and actually helps ensure the emulator maintains high performance by keeping its state within internal memory as much as possible, instead of in offboard PSRAM. Build and run `main` on box-3 and ensure all emulators work for multiple sessions of different roms in sequence. Also ensure that saving state and loading state works for each emulator.
1 parent 007d80a commit d67d1bd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2945
-2463
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# The following lines of boilerplate have to be in your project's CMakeLists
22
# in this exact order for cmake to work correctly
3-
cmake_minimum_required(VERSION 3.5)
3+
cmake_minimum_required(VERSION 3.15)
44

55
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
66

components/box-emu/include/box-emu.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class BoxEmu : public espp::BaseComponent {
151151
/////////////////////////////////////////////////////////////////////////////
152152

153153
bool initialize_video();
154+
void clear_screen();
154155
void display_size(size_t width, size_t height);
155156
void native_size(size_t width, size_t height, int pitch = -1);
156157
void palette(const uint16_t *palette, size_t size = 256);

components/box-emu/src/box-emu.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,11 @@ bool BoxEmu::initialize_video() {
467467
return true;
468468
}
469469

470+
void BoxEmu::clear_screen() {
471+
static int buffer = 0;
472+
xQueueSend(video_queue_, &buffer, 10);
473+
}
474+
470475
void BoxEmu::display_size(size_t width, size_t height) {
471476
display_width_ = width;
472477
display_height_ = height;
@@ -717,13 +722,29 @@ bool BoxEmu::video_task_callback(std::mutex &m, std::condition_variable& cv, boo
717722
return false;
718723
}
719724
static constexpr int num_lines_to_write = num_rows_in_framebuffer;
720-
auto &box = Bsp::get();
725+
static auto &box = Bsp::get();
726+
static const uint16_t *vram0 = (uint16_t*)box.vram0();
727+
static const uint16_t *vram1 = (uint16_t*)box.vram1();
721728
static uint16_t vram_index = 0; // has to be static so that it persists between calls
722729
const int _x_offset = x_offset();
723730
const int _y_offset = y_offset();
724731
const uint16_t* _palette = palette();
725-
uint16_t *vram0 = (uint16_t*)box.vram0();
726-
uint16_t *vram1 = (uint16_t*)box.vram1();
732+
// special case: if _frame_ptr is null, then we ignore all palette, scaling,
733+
// etc. and simply fill the screen with 0
734+
if (_frame_ptr == nullptr) {
735+
for (int y=0; y<lcd_height(); y+= num_lines_to_write) {
736+
Pixel* _buf = (Pixel*)((uint32_t)vram0 * (vram_index ^ 0x01) + (uint32_t)vram1 * vram_index);
737+
vram_index = vram_index ^ 0x01;
738+
int num_lines = std::min<int>(num_lines_to_write, lcd_height()-y);
739+
// memset the buffer to 0
740+
memset(_buf, 0, lcd_width() * num_lines * sizeof(Pixel));
741+
box.write_lcd_frame(0, y + _y_offset, lcd_width(), num_lines, (uint8_t*)&_buf[0]);
742+
}
743+
744+
// now return
745+
return false;
746+
}
747+
727748
if (is_native()) {
728749
if (has_palette()) {
729750
for (int y=0; y<display_height_; y+= num_lines_to_write) {

components/gbc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ idf_component_register(
33
SRC_DIRS "src" "gnuboy/src"
44
PRIV_INCLUDE_DIRS "gnuboy/include"
55
# LDFRAGMENTS "linker.lf"
6-
REQUIRES "box-emu" "statistics"
6+
REQUIRES "box-emu" "statistics" "shared_memory"
77
)
88

99
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-misleading-indentation -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers)

components/gbc/gnuboy/include/gnuboy/cpu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ struct cpu
2525
int snd;
2626
};
2727

28-
extern struct cpu cpu;
28+
extern struct cpu *cpu;
2929

3030

3131
void cpu_timers(int cnt);

components/gbc/gnuboy/include/gnuboy/cpuregs.h

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,33 @@
1414
#define W(r) ((r).w[LO])
1515
#define DW(r) ((r).d)
1616

17-
#define A HB(cpu.af)
18-
#define F LB(cpu.af)
19-
#define B HB(cpu.bc)
20-
#define C LB(cpu.bc)
21-
#define D HB(cpu.de)
22-
#define E LB(cpu.de)
23-
#define H HB(cpu.hl)
24-
#define L LB(cpu.hl)
25-
26-
#define AF W(cpu.af)
27-
#define BC W(cpu.bc)
28-
#define DE W(cpu.de)
29-
#define HL W(cpu.hl)
30-
31-
#define PC W(cpu.pc)
32-
#define SP W(cpu.sp)
33-
34-
#define xAF DW(cpu.af)
35-
#define xBC DW(cpu.bc)
36-
#define xDE DW(cpu.de)
37-
#define xHL DW(cpu.hl)
38-
39-
#define xPC DW(cpu.pc)
40-
#define xSP DW(cpu.sp)
41-
42-
#define IMA cpu.ima
43-
#define IME cpu.ime
17+
#define A HB(cpu->af)
18+
#define F LB(cpu->af)
19+
#define B HB(cpu->bc)
20+
#define C LB(cpu->bc)
21+
#define D HB(cpu->de)
22+
#define E LB(cpu->de)
23+
#define H HB(cpu->hl)
24+
#define L LB(cpu->hl)
25+
26+
#define AF W(cpu->af)
27+
#define BC W(cpu->bc)
28+
#define DE W(cpu->de)
29+
#define HL W(cpu->hl)
30+
31+
#define PC W(cpu->pc)
32+
#define SP W(cpu->sp)
33+
34+
#define xAF DW(cpu->af)
35+
#define xBC DW(cpu->bc)
36+
#define xDE DW(cpu->de)
37+
#define xHL DW(cpu->hl)
38+
39+
#define xPC DW(cpu->pc)
40+
#define xSP DW(cpu->sp)
41+
42+
#define IMA cpu->ima
43+
#define IME cpu->ime
4444
#define IF R_IF
4545
#define IE R_IE
4646

components/gbc/gnuboy/include/gnuboy/lcd.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct vissprite
1313
//byte pad[2]; //6
1414
};
1515

16-
struct scan
16+
struct gbc_scan
1717
{
1818
int bg[64];
1919
int wnd[64];
@@ -45,7 +45,7 @@ struct lcd
4545
};
4646

4747
extern struct lcd lcd;
48-
extern struct scan scan;
48+
extern struct gbc_scan *scan;
4949

5050

5151
void lcd_begin();

components/gbc/gnuboy/include/gnuboy/loader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ int sram_save();
2222
void state_load(int n);
2323
void state_save(int n);
2424

25-
25+
extern unsigned char *gbc_filebuf;
2626

2727
#endif

components/gbc/gnuboy/src/cpu.c

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@
1515
#include "gnuboy/asm.h"
1616
#endif
1717

18-
19-
struct cpu cpu;
20-
21-
22-
23-
2418
#define ZFLAG(n) ( (n) ? 0 : FZ )
2519
#define HFLAG(n) ( (n) ? 0 : FH )
2620
#define CFLAG(n) ( (n) ? 0 : FC )
@@ -231,7 +225,7 @@ label: op(b); break;
231225
#define RET ( POP(PC) )
232226

233227
#define EI ( IMA = 1 )
234-
#define DI ( cpu.halt = IMA = IME = 0 )
228+
#define DI ( cpu->halt = IMA = IME = 0 )
235229

236230

237231

@@ -251,13 +245,13 @@ label: op(b); break;
251245

252246
void cpu_reset()
253247
{
254-
cpu.speed = 0;
255-
cpu.halt = 0;
256-
cpu.div = 0;
257-
cpu.tim = 0;
248+
cpu->speed = 0;
249+
cpu->halt = 0;
250+
cpu->div = 0;
251+
cpu->tim = 0;
258252
/* set lcdc ahead of cpu by 19us; see A */
259253
/* FIXME: leave value at 0, use lcdc_trans() to actually send lcdc ahead */
260-
cpu.lcdc = 40;
254+
cpu->lcdc = 40;
261255

262256
IME = 0;
263257
IMA = 0;
@@ -281,11 +275,11 @@ void cpu_reset()
281275
handle differences in place */
282276
void div_advance(int cnt)
283277
{
284-
cpu.div += (cnt<<1);
285-
if (cpu.div >= 256)
278+
cpu->div += (cnt<<1);
279+
if (cpu->div >= 256)
286280
{
287-
R_DIV += (cpu.div >> 8);
288-
cpu.div &= 0xff;
281+
R_DIV += (cpu->div >> 8);
282+
cpu->div &= 0xff;
289283
}
290284
}
291285

@@ -302,12 +296,12 @@ void timer_advance(int cnt)
302296
if (!(R_TAC & 0x04)) return;
303297

304298
unit = ((-R_TAC) & 3) << 1;
305-
cpu.tim += (cnt<<unit);
299+
cpu->tim += (cnt<<unit);
306300

307-
if (cpu.tim >= 512)
301+
if (cpu->tim >= 512)
308302
{
309-
tima = R_TIMA + (cpu.tim >> 9);
310-
cpu.tim &= 0x1ff;
303+
tima = R_TIMA + (cpu->tim >> 9);
304+
cpu->tim &= 0x1ff;
311305
if (tima >= 256)
312306
{
313307
hw_interrupt(IF_TIMER, IF_TIMER);
@@ -325,21 +319,21 @@ void timer_advance(int cnt)
325319
*/
326320
inline void lcdc_advance(int cnt)
327321
{
328-
cpu.lcdc -= cnt;
329-
if (cpu.lcdc <= 0) lcdc_trans();
322+
cpu->lcdc -= cnt;
323+
if (cpu->lcdc <= 0) lcdc_trans();
330324
}
331325

332326
/* cnt - time to emulate, expressed in 2MHz units */
333327
inline void sound_advance(int cnt)
334328
{
335-
cpu.snd += cnt;
329+
cpu->snd += cnt;
336330
}
337331

338332
/* cnt - time to emulate, expressed in 2MHz units */
339333
void cpu_timers(int cnt)
340334
{
341-
div_advance(cnt << cpu.speed);
342-
timer_advance(cnt << cpu.speed);
335+
div_advance(cnt << cpu->speed);
336+
timer_advance(cnt << cpu->speed);
343337
lcdc_advance(cnt);
344338
sound_advance(cnt);
345339
}
@@ -356,16 +350,16 @@ int cpu_idle(int max)
356350
int cnt, unit;
357351

358352

359-
if (!(cpu.halt && IME)) return 0;
353+
if (!(cpu->halt && IME)) return 0;
360354
if (R_IF & R_IE)
361355
{
362-
cpu.halt = 0;
356+
cpu->halt = 0;
363357
return 0;
364358
}
365359

366360
/* Make sure we don't miss lcdc status events! */
367-
if ((R_IE & (IF_VBLANK | IF_STAT)) && (max > cpu.lcdc))
368-
max = cpu.lcdc;
361+
if ((R_IE & (IF_VBLANK | IF_STAT)) && (max > cpu->lcdc))
362+
max = cpu->lcdc;
369363

370364
/* If timer interrupt cannot happen, this is very simple! */
371365
if (!((R_IE & IF_TIMER) && (R_TAC & 0x04)))
@@ -376,7 +370,7 @@ int cpu_idle(int max)
376370

377371
/* Figure out when the next timer interrupt will happen */
378372
unit = ((-R_TAC) & 3) << 1;
379-
cnt = (511 - cpu.tim + (1<<unit)) >> unit;
373+
cnt = (511 - cpu->tim + (1<<unit)) >> unit;
380374
cnt += (255 - R_TIMA) << (9 - unit);
381375

382376
if (max < cnt)
@@ -909,15 +903,15 @@ int cpu_emulate(int cycles)
909903
PC++;
910904
if (R_KEY1 & 1)
911905
{
912-
cpu.speed = cpu.speed ^ 1;
913-
R_KEY1 = (R_KEY1 & 0x7E) | (cpu.speed << 7);
906+
cpu->speed = cpu->speed ^ 1;
907+
R_KEY1 = (R_KEY1 & 0x7E) | (cpu->speed << 7);
914908
break;
915909
}
916910
/* NOTE - we do not implement dmg STOP whatsoever */
917911
break;
918912

919913
case 0x76: /* HALT */
920-
cpu.halt = 1;
914+
cpu->halt = 1;
921915
break;
922916

923917
case 0xCB: /* CB prefix */
@@ -956,7 +950,7 @@ int cpu_emulate(int cycles)
956950
clen <<= 1;
957951
div_advance(clen);
958952
timer_advance(clen);
959-
clen >>= cpu.speed;
953+
clen >>= cpu->speed;
960954
lcdc_advance(clen);
961955
sound_advance(clen);
962956

0 commit comments

Comments
 (0)