2022-10-08 13:25:39 -04:00
|
|
|
#include "shared.h"
|
|
|
|
|
|
2020-05-17 12:49:52 -04:00
|
|
|
#include <nofrendo.h>
|
2020-02-13 17:21:07 -05:00
|
|
|
|
2021-10-24 16:02:30 -04:00
|
|
|
static int overscan = true;
|
|
|
|
|
static int autocrop = 0;
|
|
|
|
|
static int palette = 0;
|
2024-02-22 00:04:03 -05:00
|
|
|
static bool slowFrame = false;
|
2024-02-29 23:37:08 -05:00
|
|
|
static bool nsfPlayer = false;
|
2020-06-03 11:20:01 -04:00
|
|
|
static nes_t *nes;
|
2020-11-30 21:24:16 -05:00
|
|
|
|
2024-12-03 15:42:11 -05:00
|
|
|
static rg_app_t *app;
|
2024-02-28 16:35:41 -05:00
|
|
|
static rg_surface_t *updates[2];
|
|
|
|
|
static rg_surface_t *currentUpdate;
|
|
|
|
|
|
2021-03-04 13:50:18 -05:00
|
|
|
static const char *SETTING_AUTOCROP = "autocrop";
|
2021-03-16 05:46:55 -04:00
|
|
|
static const char *SETTING_OVERSCAN = "overscan";
|
|
|
|
|
static const char *SETTING_PALETTE = "palette";
|
|
|
|
|
static const char *SETTING_SPRITELIMIT = "spritelimit";
|
2020-02-16 01:09:20 -05:00
|
|
|
// --- MAIN
|
2020-02-13 17:21:07 -05:00
|
|
|
|
2020-03-11 21:44:17 -04:00
|
|
|
|
2022-09-13 19:16:50 -04:00
|
|
|
static void event_handler(int event, void *arg)
|
2020-04-05 02:47:34 -04:00
|
|
|
{
|
2024-02-14 05:30:07 -05:00
|
|
|
if (event == RG_EVENT_REDRAW)
|
|
|
|
|
{
|
2024-02-29 23:37:08 -05:00
|
|
|
if (nsfPlayer)
|
|
|
|
|
rg_display_clear(C_BLACK);
|
2024-03-08 17:51:08 -05:00
|
|
|
else if (nes)
|
|
|
|
|
(nes->blit_func)(NULL);
|
2024-02-14 05:30:07 -05:00
|
|
|
}
|
2020-04-05 02:47:34 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-14 21:50:28 -04:00
|
|
|
static bool screenshot_handler(const char *filename, int width, int height)
|
2020-03-20 20:27:23 -04:00
|
|
|
{
|
2024-03-08 17:51:08 -05:00
|
|
|
return rg_surface_save_image_file(currentUpdate, filename, width, height);
|
2021-04-14 21:50:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool save_state_handler(const char *filename)
|
|
|
|
|
{
|
|
|
|
|
return state_save(filename) == 0;
|
2020-03-11 21:44:17 -04:00
|
|
|
}
|
|
|
|
|
|
2021-04-14 21:50:28 -04:00
|
|
|
static bool load_state_handler(const char *filename)
|
2020-03-11 21:44:17 -04:00
|
|
|
{
|
2021-04-14 21:50:28 -04:00
|
|
|
if (state_load(filename) != 0)
|
2020-12-22 14:08:05 -05:00
|
|
|
{
|
2021-03-14 12:42:47 -04:00
|
|
|
nes_reset(true);
|
2020-12-22 14:08:05 -05:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
2020-03-11 21:44:17 -04:00
|
|
|
}
|
|
|
|
|
|
2021-02-12 15:36:53 -05:00
|
|
|
static bool reset_handler(bool hard)
|
2021-02-11 11:37:20 -05:00
|
|
|
{
|
2021-03-14 12:42:47 -04:00
|
|
|
nes_reset(hard);
|
2021-02-11 11:37:20 -05:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-13 15:51:57 -04:00
|
|
|
static void build_palette(int n)
|
|
|
|
|
{
|
|
|
|
|
uint16_t *pal = nofrendo_buildpalette(n, 16);
|
2021-09-30 18:56:16 -04:00
|
|
|
for (int i = 0; i < 256; i++)
|
2021-10-13 20:25:42 -04:00
|
|
|
{
|
|
|
|
|
uint16_t color = (pal[i] >> 8) | ((pal[i]) << 8);
|
2024-02-28 16:35:41 -05:00
|
|
|
updates[0]->palette[i] = color;
|
|
|
|
|
updates[1]->palette[i] = color;
|
2021-10-13 20:25:42 -04:00
|
|
|
}
|
2021-08-13 15:51:57 -04:00
|
|
|
free(pal);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
static rg_gui_event_t sprite_limit_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
2020-03-25 01:47:12 -04:00
|
|
|
{
|
2021-03-16 05:46:55 -04:00
|
|
|
bool spritelimit = ppu_getopt(PPU_LIMIT_SPRITES);
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2021-03-16 05:46:55 -04:00
|
|
|
if (event == RG_DIALOG_PREV || event == RG_DIALOG_NEXT)
|
|
|
|
|
{
|
|
|
|
|
spritelimit = !spritelimit;
|
2021-12-08 16:56:23 -05:00
|
|
|
rg_settings_set_number(NS_APP, SETTING_SPRITELIMIT, spritelimit);
|
2021-03-16 05:46:55 -04:00
|
|
|
ppu_setopt(PPU_LIMIT_SPRITES, spritelimit);
|
2020-12-22 14:08:05 -05:00
|
|
|
}
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2024-11-16 19:04:50 +01:00
|
|
|
strcpy(option->value, spritelimit ? _("On") : _("Off"));
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
return RG_DIALOG_VOID;
|
2020-03-25 01:47:12 -04:00
|
|
|
}
|
|
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
static rg_gui_event_t overscan_update_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
2020-03-25 01:47:12 -04:00
|
|
|
{
|
2021-03-16 05:46:55 -04:00
|
|
|
if (event == RG_DIALOG_PREV || event == RG_DIALOG_NEXT)
|
|
|
|
|
{
|
2020-12-22 14:08:05 -05:00
|
|
|
overscan = !overscan;
|
2021-12-08 16:56:23 -05:00
|
|
|
rg_settings_set_number(NS_APP, SETTING_OVERSCAN, overscan);
|
2024-02-14 05:30:07 -05:00
|
|
|
return RG_DIALOG_REDRAW;
|
2020-12-22 14:08:05 -05:00
|
|
|
}
|
2020-05-24 12:30:15 -04:00
|
|
|
|
2024-11-16 19:04:50 +01:00
|
|
|
strcpy(option->value, overscan ? _("Auto") : _("Off"));
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
return RG_DIALOG_VOID;
|
2020-05-24 12:30:15 -04:00
|
|
|
}
|
|
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
static rg_gui_event_t autocrop_update_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
2020-05-24 12:30:15 -04:00
|
|
|
{
|
2020-12-22 14:08:05 -05:00
|
|
|
int val = autocrop;
|
|
|
|
|
int max = 2;
|
2020-06-01 15:02:49 -04:00
|
|
|
|
2020-12-22 14:08:05 -05:00
|
|
|
if (event == RG_DIALOG_PREV) val = val > 0 ? val - 1 : max;
|
|
|
|
|
if (event == RG_DIALOG_NEXT) val = val < max ? val + 1 : 0;
|
2020-06-01 15:02:49 -04:00
|
|
|
|
2021-03-16 05:46:55 -04:00
|
|
|
if (val != autocrop)
|
|
|
|
|
{
|
2020-12-22 14:08:05 -05:00
|
|
|
autocrop = val;
|
2021-12-08 16:56:23 -05:00
|
|
|
rg_settings_set_number(NS_APP, SETTING_AUTOCROP, val);
|
2024-02-14 05:30:07 -05:00
|
|
|
return RG_DIALOG_REDRAW;
|
2020-12-22 14:08:05 -05:00
|
|
|
}
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2024-11-16 19:04:50 +01:00
|
|
|
if (val == 0) strcpy(option->value, _("Never"));
|
|
|
|
|
if (val == 1) strcpy(option->value, _("Auto"));
|
|
|
|
|
if (val == 2) strcpy(option->value, _("Always"));
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
return RG_DIALOG_VOID;
|
2020-03-25 01:47:12 -04:00
|
|
|
}
|
|
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
static rg_gui_event_t palette_update_cb(rg_gui_option_t *option, rg_gui_event_t event)
|
2020-03-25 01:47:12 -04:00
|
|
|
{
|
2021-08-13 15:51:57 -04:00
|
|
|
int pal = palette;
|
|
|
|
|
int max = NES_PALETTE_COUNT - 1;
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2020-12-22 14:08:05 -05:00
|
|
|
if (event == RG_DIALOG_PREV) pal = pal > 0 ? pal - 1 : max;
|
|
|
|
|
if (event == RG_DIALOG_NEXT) pal = pal < max ? pal + 1 : 0;
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2021-08-13 15:51:57 -04:00
|
|
|
if (pal != palette)
|
2021-03-16 05:46:55 -04:00
|
|
|
{
|
2021-08-13 15:51:57 -04:00
|
|
|
palette = pal;
|
2021-12-08 16:56:23 -05:00
|
|
|
rg_settings_set_number(NS_APP, SETTING_PALETTE, pal);
|
2021-08-13 15:51:57 -04:00
|
|
|
build_palette(pal);
|
2024-02-14 05:30:07 -05:00
|
|
|
return RG_DIALOG_REDRAW;
|
2020-12-22 14:08:05 -05:00
|
|
|
}
|
2020-03-25 01:47:12 -04:00
|
|
|
|
2024-12-17 19:12:08 -05:00
|
|
|
if (pal == NES_PALETTE_NOFRENDO) strcpy(option->value, _("Nofrendo"));
|
2024-11-16 19:04:50 +01:00
|
|
|
if (pal == NES_PALETTE_COMPOSITE) strcpy(option->value, _("Composite"));
|
|
|
|
|
if (pal == NES_PALETTE_NESCLASSIC) strcpy(option->value, _("NES Classic"));
|
|
|
|
|
if (pal == NES_PALETTE_NTSC) strcpy(option->value, _("NTSC"));
|
|
|
|
|
if (pal == NES_PALETTE_PVM) strcpy(option->value, _("PVM"));
|
|
|
|
|
if (pal == NES_PALETTE_SMOOTH) strcpy(option->value, _("Smooth"));
|
2020-12-22 14:08:05 -05:00
|
|
|
|
2021-12-08 18:44:10 -05:00
|
|
|
return RG_DIALOG_VOID;
|
2021-09-26 17:34:15 -04:00
|
|
|
}
|
|
|
|
|
|
2020-02-04 19:02:11 -05:00
|
|
|
|
2022-02-04 13:50:24 -05:00
|
|
|
static void blit_screen(uint8 *bmp)
|
2020-02-13 17:21:07 -05:00
|
|
|
{
|
2024-03-08 17:51:08 -05:00
|
|
|
slowFrame = bmp && !rg_display_sync(false);
|
2021-02-16 15:26:17 -05:00
|
|
|
// A rolling average should be used for autocrop == 1, it causes jitter in some games...
|
2021-10-24 16:02:30 -04:00
|
|
|
// int crop_h = (autocrop == 2) || (autocrop == 1 && nes->ppu->left_bg_counter > 210) ? 8 : 0;
|
2024-03-08 17:51:08 -05:00
|
|
|
int crop_v = (overscan) ? nes->overscan : 0;
|
2024-03-09 16:27:09 -05:00
|
|
|
int crop_h = (autocrop) ? 8 : 0;
|
2024-03-08 17:51:08 -05:00
|
|
|
// crop_h = (autocrop == 2) || (autocrop == 1 && nes->ppu->left_bg_counter > 210) ? 8 : 0;
|
|
|
|
|
currentUpdate->width = NES_SCREEN_WIDTH - crop_h * 2;
|
|
|
|
|
currentUpdate->height = NES_SCREEN_HEIGHT - crop_v * 2;
|
2024-03-09 16:27:09 -05:00
|
|
|
currentUpdate->offset = crop_v * currentUpdate->stride + crop_h + 8;
|
rg_display: New partial update system
The previous partial update system worked as follows:
1. Compare the previous and current framebuffers line by line
2. For each line find the first different pixel and the last one
3. Do a second pass and merge line differences into rectangles
4. Adjust the rectangles until they're no longer on boundaries that would break scaling or filtering
5. Prepare/render the region within a rectangle (do pixel conversion, scaling, filtering, etc)
5. Send the block to the SPI display
This worked reasonably well but:
- Step 4 was always a problem and we often ended up with artifacts despite many attempts at fixing it
- Mid-frame palette changes were completely broken
- The need for two framebuffers is an issue in some emulators
- It was fairly slow. Better than a full redraw, but still slow
The new system works as follows:
1. Prepare/render a few lines to be sent to the display, as if we were doing a full update
2. Do a checksum of the block and compare it to the checksum of the same block from the previous frame
3. If it matches, drop the block and advance the window. Otherwise store the new checksum and send the SPI buffer
This has many advantages:
- We don't have to care about boundaries since they're always the same
- In many cases it's faster than doing a diff, even though we have to render the full lines
- It can work with a single framebuffer
- It uses less memory
- We can invalidate lines that rg_display_write writes to, avoiding accidental leftovers
The main drawback is that it's currently less granular. If a single pixel changes in a block, we have to
send it fully to the display. Whereas previously we narrowed down the diff to a smaller region.
But I intend to experiment with block sizes and shapes, there's no reason we couldn't split the frame
into smaller blocks. It was just more convenient to start with the current block size (4 full lines).
2024-02-15 00:47:43 -05:00
|
|
|
rg_display_submit(currentUpdate, 0);
|
2020-02-13 17:21:07 -05:00
|
|
|
}
|
|
|
|
|
|
2021-12-10 20:47:40 -05:00
|
|
|
static void nsf_draw_overlay(void)
|
|
|
|
|
{
|
|
|
|
|
extern int nsf_current_song;
|
|
|
|
|
char song[32];
|
2022-04-04 15:28:21 -04:00
|
|
|
const nsfheader_t *header = (nsfheader_t *)nes->cart->data_ptr;
|
2021-12-10 20:47:40 -05:00
|
|
|
const rg_gui_option_t options[] = {
|
2024-11-16 19:04:50 +01:00
|
|
|
{0, _("Name"), (char*)header->name, RG_DIALOG_FLAG_NORMAL, NULL},
|
|
|
|
|
{0, _("Artist"), (char*)header->artist, RG_DIALOG_FLAG_NORMAL, NULL},
|
|
|
|
|
{0, _("Copyright"), (char*)header->copyright, RG_DIALOG_FLAG_NORMAL, NULL},
|
|
|
|
|
{0, _("Playing"), (char*)song, RG_DIALOG_FLAG_NORMAL, NULL},
|
2024-02-11 18:12:47 -05:00
|
|
|
RG_DIALOG_END,
|
2021-12-10 20:47:40 -05:00
|
|
|
};
|
|
|
|
|
snprintf(song, sizeof(song), "%d / %d", nsf_current_song, header->total_songs);
|
2025-11-07 16:43:39 -05:00
|
|
|
rg_gui_draw_dialog("NSF Player", options, 4, -1);
|
2021-12-10 20:47:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-12-17 13:03:08 -05:00
|
|
|
static void options_handler(rg_gui_option_t *dest)
|
|
|
|
|
{
|
|
|
|
|
*dest++ = (rg_gui_option_t){0, _("Palette"), "-", RG_DIALOG_FLAG_NORMAL, &palette_update_cb};
|
|
|
|
|
*dest++ = (rg_gui_option_t){0, _("Overscan"), "-", RG_DIALOG_FLAG_NORMAL, &overscan_update_cb};
|
|
|
|
|
*dest++ = (rg_gui_option_t){0, _("Crop sides"), "-", RG_DIALOG_FLAG_NORMAL, &autocrop_update_cb};
|
|
|
|
|
*dest++ = (rg_gui_option_t){0, _("Sprite limit"), "-", RG_DIALOG_FLAG_NORMAL, &sprite_limit_cb};
|
|
|
|
|
*dest++ = (rg_gui_option_t)RG_DIALOG_END;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-08 13:25:39 -04:00
|
|
|
void nes_main(void)
|
2020-02-13 17:21:07 -05:00
|
|
|
{
|
2021-12-08 16:56:23 -05:00
|
|
|
const rg_handlers_t handlers = {
|
2021-02-12 15:36:53 -05:00
|
|
|
.loadState = &load_state_handler,
|
|
|
|
|
.saveState = &save_state_handler,
|
|
|
|
|
.reset = &reset_handler,
|
2022-09-13 19:16:50 -04:00
|
|
|
.event = &event_handler,
|
2021-04-14 21:50:28 -04:00
|
|
|
.screenshot = &screenshot_handler,
|
2024-12-17 13:03:08 -05:00
|
|
|
.options = &options_handler,
|
2021-02-11 11:37:20 -05:00
|
|
|
};
|
|
|
|
|
|
2024-12-17 13:03:08 -05:00
|
|
|
app = rg_system_reinit(AUDIO_SAMPLE_RATE, &handlers, NULL);
|
2020-12-22 14:08:05 -05:00
|
|
|
|
2021-12-08 16:56:23 -05:00
|
|
|
overscan = rg_settings_get_number(NS_APP, SETTING_OVERSCAN, 1);
|
|
|
|
|
autocrop = rg_settings_get_number(NS_APP, SETTING_AUTOCROP, 0);
|
2024-12-17 19:12:08 -05:00
|
|
|
palette = rg_settings_get_number(NS_APP, SETTING_PALETTE, NES_PALETTE_PVM);
|
2021-03-16 05:46:55 -04:00
|
|
|
|
2024-02-28 16:35:41 -05:00
|
|
|
updates[0] = rg_surface_create(NES_SCREEN_PITCH, NES_SCREEN_HEIGHT, RG_PIXEL_PAL565_BE, MEM_FAST);
|
|
|
|
|
updates[1] = rg_surface_create(NES_SCREEN_PITCH, NES_SCREEN_HEIGHT, RG_PIXEL_PAL565_BE, MEM_FAST);
|
|
|
|
|
currentUpdate = updates[0];
|
|
|
|
|
|
2024-07-16 16:23:39 -04:00
|
|
|
nes = nes_init(SYS_DETECT, app->sampleRate, true, RG_BASE_PATH_BIOS "/fds_bios.bin");
|
2021-07-25 17:51:10 -04:00
|
|
|
if (!nes)
|
2021-03-13 15:33:05 -05:00
|
|
|
RG_PANIC("Init failed.");
|
2024-07-22 16:29:50 -04:00
|
|
|
|
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
|
|
if (rg_extension_match(app->romPath, "zip"))
|
|
|
|
|
{
|
|
|
|
|
void *data;
|
|
|
|
|
size_t size;
|
2024-07-26 13:22:47 -04:00
|
|
|
if (!rg_storage_unzip_file(app->romPath, NULL, &data, &size, RG_FILE_ALIGN_8KB))
|
2024-07-22 16:29:50 -04:00
|
|
|
RG_PANIC("ROM file unzipping failed!");
|
|
|
|
|
ret = nes_insertcart(rom_loadmem(data, size));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = nes_loadfile(app->romPath);
|
2021-03-13 15:33:05 -05:00
|
|
|
}
|
2020-12-22 14:08:05 -05:00
|
|
|
|
2021-08-29 15:08:22 -04:00
|
|
|
if (ret == -1)
|
|
|
|
|
RG_PANIC("ROM load failed.");
|
|
|
|
|
else if (ret == -2)
|
|
|
|
|
RG_PANIC("Unsupported mapper.");
|
|
|
|
|
else if (ret == -3)
|
|
|
|
|
RG_PANIC("BIOS file required.");
|
|
|
|
|
else if (ret < 0)
|
2021-03-13 15:33:05 -05:00
|
|
|
RG_PANIC("Unsupported ROM.");
|
|
|
|
|
|
2022-02-04 13:50:24 -05:00
|
|
|
nes->blit_func = blit_screen;
|
2021-08-13 15:51:57 -04:00
|
|
|
|
2024-07-22 17:43:42 -04:00
|
|
|
nsfPlayer = nes->cart->type == ROM_TYPE_NSF;
|
2024-02-29 23:37:08 -05:00
|
|
|
|
2021-12-08 16:56:23 -05:00
|
|
|
ppu_setopt(PPU_LIMIT_SPRITES, rg_settings_get_number(NS_APP, SETTING_SPRITELIMIT, 1));
|
2021-10-24 16:02:30 -04:00
|
|
|
|
2021-08-13 15:51:57 -04:00
|
|
|
build_palette(palette);
|
|
|
|
|
|
|
|
|
|
// This is necessary for successful state restoration
|
|
|
|
|
// I have not yet investigated why that is...
|
|
|
|
|
nes_emulate(false);
|
|
|
|
|
nes_emulate(false);
|
|
|
|
|
|
2021-12-02 15:52:29 -05:00
|
|
|
if (app->bootFlags & RG_BOOT_RESUME)
|
2021-08-13 15:51:57 -04:00
|
|
|
{
|
2022-07-19 16:15:43 -04:00
|
|
|
rg_emu_load_state(app->saveSlot);
|
2021-08-13 15:51:57 -04:00
|
|
|
}
|
|
|
|
|
|
2024-11-30 13:45:01 -05:00
|
|
|
rg_system_set_tick_rate(nes->refresh_rate);
|
|
|
|
|
|
2021-08-13 15:51:57 -04:00
|
|
|
int skipFrames = 0;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
2022-10-08 13:25:39 -04:00
|
|
|
uint32_t joystick = rg_input_read_gamepad();
|
2021-08-13 15:51:57 -04:00
|
|
|
|
2022-10-08 13:25:39 -04:00
|
|
|
if (joystick & (RG_KEY_MENU|RG_KEY_OPTION))
|
2021-08-13 15:51:57 -04:00
|
|
|
{
|
2022-10-08 13:25:39 -04:00
|
|
|
if (joystick & RG_KEY_MENU)
|
2022-04-17 17:19:09 -04:00
|
|
|
rg_gui_game_menu();
|
|
|
|
|
else
|
|
|
|
|
rg_gui_options_menu();
|
2021-08-13 15:51:57 -04:00
|
|
|
}
|
|
|
|
|
|
2022-09-04 21:14:21 -04:00
|
|
|
int64_t startTime = rg_system_timer();
|
2022-04-20 17:22:39 -04:00
|
|
|
bool drawFrame = !skipFrames && !nsfPlayer;
|
2022-04-17 17:19:09 -04:00
|
|
|
int buttons = 0;
|
|
|
|
|
|
2022-10-08 13:25:39 -04:00
|
|
|
if (joystick & RG_KEY_START) buttons |= NES_PAD_START;
|
|
|
|
|
if (joystick & RG_KEY_SELECT) buttons |= NES_PAD_SELECT;
|
|
|
|
|
if (joystick & RG_KEY_UP) buttons |= NES_PAD_UP;
|
|
|
|
|
if (joystick & RG_KEY_RIGHT) buttons |= NES_PAD_RIGHT;
|
|
|
|
|
if (joystick & RG_KEY_DOWN) buttons |= NES_PAD_DOWN;
|
|
|
|
|
if (joystick & RG_KEY_LEFT) buttons |= NES_PAD_LEFT;
|
|
|
|
|
if (joystick & RG_KEY_A) buttons |= NES_PAD_A;
|
|
|
|
|
if (joystick & RG_KEY_B) buttons |= NES_PAD_B;
|
2022-04-17 17:19:09 -04:00
|
|
|
|
2024-02-14 05:30:07 -05:00
|
|
|
if (drawFrame)
|
|
|
|
|
{
|
2024-02-28 16:35:41 -05:00
|
|
|
currentUpdate = updates[currentUpdate == updates[0]];
|
2024-03-08 17:51:08 -05:00
|
|
|
nes_setvidbuf(currentUpdate->data);
|
2024-02-14 05:30:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_update(0, buttons);
|
2022-04-17 17:19:09 -04:00
|
|
|
nes_emulate(drawFrame);
|
2021-08-13 15:51:57 -04:00
|
|
|
|
2024-02-22 00:04:03 -05:00
|
|
|
// Tick before submitting audio/syncing
|
|
|
|
|
rg_system_tick(rg_system_timer() - startTime);
|
|
|
|
|
|
|
|
|
|
// Audio is used to pace emulation :)
|
|
|
|
|
rg_audio_submit((void*)nes->apu->buffer, nes->apu->samples_per_frame);
|
2021-08-13 15:51:57 -04:00
|
|
|
|
|
|
|
|
if (skipFrames == 0)
|
|
|
|
|
{
|
2022-09-04 21:14:21 -04:00
|
|
|
int frameTime = 1000000 / (nes->refresh_rate * app->speed);
|
2024-02-22 00:04:03 -05:00
|
|
|
int elapsed = rg_system_timer() - startTime;
|
|
|
|
|
if (nsfPlayer)
|
|
|
|
|
skipFrames = 10, nsf_draw_overlay();
|
|
|
|
|
else if (app->frameskip > 0)
|
|
|
|
|
skipFrames = app->frameskip;
|
|
|
|
|
else if (elapsed > frameTime + 1500) // Allow some jitter
|
2024-02-22 19:45:41 -05:00
|
|
|
skipFrames = 1; // (elapsed / frameTime)
|
2024-02-22 00:04:03 -05:00
|
|
|
else if (drawFrame && slowFrame)
|
2021-08-13 15:51:57 -04:00
|
|
|
skipFrames = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (skipFrames > 0)
|
|
|
|
|
{
|
|
|
|
|
skipFrames--;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-13 15:33:05 -05:00
|
|
|
|
|
|
|
|
RG_PANIC("Nofrendo died!");
|
2020-01-31 16:13:27 -05:00
|
|
|
}
|