uxn/src/uxnemu.c

588 lines
16 KiB
C
Raw Normal View History

2021-02-08 17:18:01 -05:00
#include <stdio.h>
2021-03-26 17:01:51 -04:00
#include <time.h>
#include <unistd.h>
2021-05-12 21:28:45 -04:00
#include "uxn.h"
2021-06-27 13:53:54 -04:00
#pragma GCC diagnostic push
#pragma clang diagnostic push
2021-06-27 13:53:54 -04:00
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma clang diagnostic ignored "-Wtypedef-redefinition"
2021-06-27 13:53:54 -04:00
#include <SDL.h>
#include "devices/system.h"
2023-08-08 17:13:07 -04:00
#include "devices/console.h"
2021-12-28 16:37:26 -05:00
#include "devices/screen.h"
2021-12-28 16:47:35 -05:00
#include "devices/audio.h"
#include "devices/file.h"
#include "devices/controller.h"
2021-12-27 00:02:24 -05:00
#include "devices/mouse.h"
2022-01-07 13:02:28 -05:00
#include "devices/datetime.h"
#if defined(_WIN32) && defined(_WIN32_WINNT) && _WIN32_WINNT > 0x0602
2022-04-09 07:22:24 -04:00
#include <processthreadsapi.h>
#elif defined(_WIN32)
#include <windows.h>
#include <string.h>
2022-04-09 07:22:24 -04:00
#endif
#ifndef __plan9__
#define USED(x) (void)(x)
#endif
2021-06-27 13:53:54 -04:00
#pragma GCC diagnostic pop
#pragma clang diagnostic pop
2021-02-08 17:18:01 -05:00
/*
2023-01-02 09:40:23 -05:00
Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick
2021-02-08 17:18:01 -05:00
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE.
*/
2023-07-26 00:43:26 -04:00
#define PAD 2
#define PAD2 4
2021-09-21 18:41:59 -04:00
#define WIDTH 64 * 8
#define HEIGHT 40 * 8
#define TIMEOUT_MS 334
2021-08-04 23:30:57 -04:00
2023-06-07 11:03:28 -04:00
static SDL_Window *emu_window;
static SDL_Texture *emu_texture;
static SDL_Renderer *emu_renderer;
2023-07-26 11:37:58 -04:00
static SDL_Rect emu_viewport;
static SDL_AudioDeviceID audio_id;
2022-04-09 07:22:24 -04:00
static SDL_Thread *stdin_thread;
2021-12-28 16:37:26 -05:00
/* devices */
2021-12-28 16:37:26 -05:00
2023-07-26 11:37:58 -04:00
static int window_created = 0;
2023-10-25 17:16:23 -04:00
static int fullscreen = 0, borderless = 0;
2023-06-07 11:03:28 -04:00
static Uint32 stdin_event, audio0_event, zoom = 1;
static Uint64 exec_deadline, deadline_interval, ms_interval;
2021-02-18 21:16:39 -05:00
2023-07-26 00:43:26 -04:00
static int
clamp(int v, int min, int max)
{
return v < min ? min : v > max ? max
: v;
}
2023-07-26 11:37:58 -04:00
2023-01-02 10:01:55 -05:00
static Uint8
audio_dei(int instance, Uint8 *d, Uint8 port)
{
Uint8 *addr;
Uint16 vu;
2023-01-02 10:01:55 -05:00
if(!audio_id) return d[port];
switch(port) {
case 0x4: return audio_get_vu(instance);
case 0x2:
addr = d + 2;
vu = audio_get_position(instance);
POKE2(addr, vu); /* fall through */
2023-01-02 10:01:55 -05:00
default: return d[port];
}
}
static void
audio_deo(int instance, Uint8 *d, Uint8 port, Uxn *u)
{
if(!audio_id) return;
if(port == 0xf) {
SDL_LockAudioDevice(audio_id);
audio_start(instance, d, u);
SDL_UnlockAudioDevice(audio_id);
SDL_PauseAudioDevice(audio_id, 0);
}
}
Uint8
emu_dei(Uxn *u, Uint8 addr)
2023-01-01 17:18:27 -05:00
{
Uint8 p = addr & 0x0f, d = addr & 0xf0;
switch(d) {
case 0x00: return system_dei(u, addr);
case 0x20: return screen_dei(u, addr);
2023-01-02 10:01:55 -05:00
case 0x30: return audio_dei(0, &u->dev[d], p);
case 0x40: return audio_dei(1, &u->dev[d], p);
case 0x50: return audio_dei(2, &u->dev[d], p);
case 0x60: return audio_dei(3, &u->dev[d], p);
case 0xc0: return datetime_dei(u, addr);
2023-01-01 17:18:27 -05:00
}
return u->dev[addr];
}
void
2023-10-30 17:14:02 -04:00
emu_deo(Uxn *u, Uint8 addr, Uint8 value)
2023-01-01 17:18:27 -05:00
{
Uint8 p = addr & 0x0f, d = addr & 0xf0;
2023-10-30 17:14:02 -04:00
u->dev[addr] = value;
2023-01-01 17:18:27 -05:00
switch(d) {
case 0x00:
system_deo(u, &u->dev[d], p);
if(p > 0x7 && p < 0xe)
2023-05-04 20:43:44 -04:00
screen_palette(&u->dev[0x8]);
2023-01-01 17:18:27 -05:00
break;
case 0x10: console_deo(&u->dev[d], p); break;
case 0x20: screen_deo(u->ram, &u->dev[d], p); break;
2023-01-02 10:01:55 -05:00
case 0x30: audio_deo(0, &u->dev[d], p, u); break;
case 0x40: audio_deo(1, &u->dev[d], p, u); break;
case 0x50: audio_deo(2, &u->dev[d], p, u); break;
case 0x60: audio_deo(3, &u->dev[d], p, u); break;
2023-01-01 17:18:27 -05:00
case 0xa0: file_deo(0, u->ram, &u->dev[d], p); break;
case 0xb0: file_deo(1, u->ram, &u->dev[d], p); break;
}
}
2023-07-24 12:48:49 -04:00
/* Handlers */
2021-09-21 18:41:59 -04:00
static void
audio_callback(void *u, Uint8 *stream, int len)
{
int instance, running = 0;
2021-04-25 10:12:45 -04:00
Sint16 *samples = (Sint16 *)stream;
USED(u);
2021-04-25 10:12:45 -04:00
SDL_memset(stream, 0, len);
for(instance = 0; instance < POLYPHONY; instance++)
running += audio_render(instance, samples, samples + len / 2);
if(!running)
SDL_PauseAudioDevice(audio_id, 1);
}
2021-09-21 18:41:59 -04:00
void
audio_finished_handler(int instance)
2021-09-21 18:41:59 -04:00
{
SDL_Event event;
event.type = audio0_event + instance;
2021-09-21 18:41:59 -04:00
SDL_PushEvent(&event);
}
static int
stdin_handler(void *p)
{
SDL_Event event;
USED(p);
2021-09-21 18:41:59 -04:00
event.type = stdin_event;
while(read(0, &event.cbutton.button, 1) > 0 && SDL_PushEvent(&event) >= 0)
;
2021-09-21 18:41:59 -04:00
return 0;
}
2021-09-21 16:21:48 -04:00
static void
set_window_size(SDL_Window *window, int w, int h)
{
2021-09-22 13:57:43 -04:00
SDL_Point win, win_old;
2021-09-21 18:56:42 -04:00
SDL_GetWindowPosition(window, &win.x, &win.y);
SDL_GetWindowSize(window, &win_old.x, &win_old.y);
if(w == win_old.x && h == win_old.y) return;
SDL_RenderClear(emu_renderer);
2023-04-26 15:04:44 -04:00
/* SDL_SetWindowPosition(window, (win.x + win_old.x / 2) - w / 2, (win.y + win_old.y / 2) - h / 2); */
2021-09-21 16:21:48 -04:00
SDL_SetWindowSize(window, w, h);
}
static void
set_zoom(Uint8 z, int win)
{
if(z >= 1) {
zoom = z;
if(win)
2023-07-26 00:43:26 -04:00
set_window_size(emu_window, (uxn_screen.width + PAD2) * zoom, (uxn_screen.height + PAD2) * zoom);
}
}
static void
2023-10-25 17:16:23 -04:00
set_fullscreen(int value, int win)
{
Uint32 flags = 0; /* windowed mode; SDL2 has no constant for this */
2023-10-25 18:25:32 -04:00
fullscreen = value;
if(fullscreen) {
flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
}
2023-10-25 11:24:37 -04:00
if(win)
SDL_SetWindowFullscreen(emu_window, flags);
}
2023-10-25 17:16:23 -04:00
static void
set_borderless(int value)
{
2023-10-25 18:23:56 -04:00
if(fullscreen) return;
2023-10-25 17:16:23 -04:00
borderless = value;
SDL_SetWindowBordered(emu_window, !value);
}
2023-10-25 18:23:56 -04:00
static void
set_debugger(Uxn *u, int value)
{
u->dev[0x0e] = value;
screen_fill(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
screen_redraw(u);
}
/* emulator primitives */
int
2023-07-24 12:48:49 -04:00
emu_resize(int width, int height)
{
if(!window_created)
return 0;
2023-06-07 11:03:28 -04:00
if(emu_texture != NULL)
SDL_DestroyTexture(emu_texture);
2023-07-26 00:43:26 -04:00
SDL_RenderSetLogicalSize(emu_renderer, width + PAD2, height + PAD2);
2023-07-24 12:48:49 -04:00
emu_texture = SDL_CreateTexture(emu_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STATIC, width, height);
2023-06-07 11:03:28 -04:00
if(emu_texture == NULL || SDL_SetTextureBlendMode(emu_texture, SDL_BLENDMODE_NONE))
return system_error("SDL_SetTextureBlendMode", SDL_GetError());
2023-06-07 11:03:28 -04:00
if(SDL_UpdateTexture(emu_texture, NULL, uxn_screen.pixels, sizeof(Uint32)) != 0)
2023-04-17 14:03:11 -04:00
return system_error("SDL_UpdateTexture", SDL_GetError());
2023-07-26 11:37:58 -04:00
emu_viewport.x = PAD;
emu_viewport.y = PAD;
emu_viewport.w = uxn_screen.width;
emu_viewport.h = uxn_screen.height;
2023-07-26 00:43:26 -04:00
set_window_size(emu_window, (width + PAD2) * zoom, (height + PAD2) * zoom);
2021-09-22 13:57:43 -04:00
return 1;
}
2021-06-28 17:42:36 -04:00
static void
2023-08-08 13:27:41 -04:00
emu_redraw(Uxn *u)
2021-02-08 18:46:52 -05:00
{
2023-08-16 16:46:07 -04:00
screen_redraw(u);
2023-06-07 11:03:28 -04:00
if(SDL_UpdateTexture(emu_texture, NULL, uxn_screen.pixels, uxn_screen.width * sizeof(Uint32)) != 0)
2023-04-17 14:03:11 -04:00
system_error("SDL_UpdateTexture", SDL_GetError());
2023-07-26 00:43:26 -04:00
SDL_RenderClear(emu_renderer);
2023-07-26 11:37:58 -04:00
SDL_RenderCopy(emu_renderer, emu_texture, NULL, &emu_viewport);
2023-06-07 11:03:28 -04:00
SDL_RenderPresent(emu_renderer);
2021-02-08 18:46:52 -05:00
}
2021-06-28 17:42:36 -04:00
static int
emu_init(void)
2021-02-08 18:46:52 -05:00
{
SDL_AudioSpec as;
SDL_zero(as);
as.freq = SAMPLE_FREQUENCY;
as.format = AUDIO_S16SYS;
as.channels = 2;
as.callback = audio_callback;
as.samples = 512;
as.userdata = NULL;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
2023-04-17 14:03:11 -04:00
return system_error("sdl", SDL_GetError());
2021-12-28 20:38:55 -05:00
audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
if(!audio_id)
2023-04-17 14:03:11 -04:00
system_error("sdl_audio", SDL_GetError());
2021-12-29 12:33:23 -05:00
if(SDL_NumJoysticks() > 0 && SDL_JoystickOpen(0) == NULL)
2023-04-17 14:03:11 -04:00
system_error("sdl_joystick", SDL_GetError());
2021-09-21 18:41:59 -04:00
stdin_event = SDL_RegisterEvents(1);
audio0_event = SDL_RegisterEvents(POLYPHONY);
2022-04-09 07:22:24 -04:00
SDL_DetachThread(stdin_thread = SDL_CreateThread(stdin_handler, "stdin", NULL));
SDL_StartTextInput();
2021-02-18 18:11:02 -05:00
SDL_ShowCursor(SDL_DISABLE);
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
SDL_SetRenderDrawColor(emu_renderer, 0x00, 0x00, 0x00, 0xff);
ms_interval = SDL_GetPerformanceFrequency() / 1000;
deadline_interval = ms_interval * TIMEOUT_MS;
2023-08-15 21:44:16 -04:00
exec_deadline = SDL_GetPerformanceCounter() + deadline_interval;
screen_resize(WIDTH, HEIGHT);
2021-02-08 18:46:52 -05:00
return 1;
}
2021-12-27 16:59:22 -05:00
static void
2023-08-15 22:19:06 -04:00
emu_restart(Uxn *u, char *rom, int soft)
2021-12-27 16:59:22 -05:00
{
screen_resize(WIDTH, HEIGHT);
2023-08-13 21:48:32 -04:00
screen_fill(uxn_screen.bg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
screen_fill(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0);
2023-08-15 22:19:06 -04:00
system_reboot(u, rom, soft);
SDL_SetWindowTitle(emu_window, boot_rom);
2023-08-13 21:48:32 -04:00
}
2021-12-27 16:59:22 -05:00
static void
capture_screen(void)
{
const Uint32 format = SDL_PIXELFORMAT_RGB24;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
2023-06-07 11:03:28 -04:00
/* SDL_PIXELFORMAT_RGB24 */
Uint32 Rmask = 0x000000FF;
Uint32 Gmask = 0x0000FF00;
Uint32 Bmask = 0x00FF0000;
#else
2023-06-07 11:03:28 -04:00
/* SDL_PIXELFORMAT_BGR24 */
Uint32 Rmask = 0x00FF0000;
Uint32 Gmask = 0x0000FF00;
Uint32 Bmask = 0x000000FF;
#endif
2021-12-27 16:59:22 -05:00
time_t t = time(NULL);
char fname[64];
int w, h;
SDL_Surface *surface;
2023-06-07 11:03:28 -04:00
SDL_GetRendererOutputSize(emu_renderer, &w, &h);
2023-07-10 11:35:35 -04:00
if((surface = SDL_CreateRGBSurface(0, w, h, 24, Rmask, Gmask, Bmask, 0)) == NULL)
return;
2023-06-07 11:03:28 -04:00
SDL_RenderReadPixels(emu_renderer, NULL, format, surface->pixels, surface->pitch);
2021-12-27 16:59:22 -05:00
strftime(fname, sizeof(fname), "screenshot-%Y%m%d-%H%M%S.bmp", localtime(&t));
if(SDL_SaveBMP(surface, fname) == 0) {
2023-07-10 11:35:35 -04:00
fprintf(stderr, "Saved %s\n", fname);
fflush(stderr);
}
2021-12-27 16:59:22 -05:00
SDL_FreeSurface(surface);
}
2021-12-27 13:04:24 -05:00
static Uint8
get_button(SDL_Event *event)
{
switch(event->key.keysym.sym) {
case SDLK_LCTRL: return 0x01;
case SDLK_LALT: return 0x02;
case SDLK_LSHIFT: return 0x04;
2021-12-27 16:42:36 -05:00
case SDLK_HOME: return 0x08;
case SDLK_UP: return 0x10;
case SDLK_DOWN: return 0x20;
case SDLK_LEFT: return 0x40;
case SDLK_RIGHT: return 0x80;
}
return 0x00;
}
2021-12-27 13:04:24 -05:00
static Uint8
2021-12-27 14:44:57 -05:00
get_button_joystick(SDL_Event *event)
{
return 0x01 << (event->jbutton.button & 0x3);
}
static Uint8
get_vector_joystick(SDL_Event *event)
{
if(event->jaxis.value < -3200)
return 1;
if(event->jaxis.value > 3200)
return 2;
return 0;
2021-12-27 13:04:24 -05:00
}
static Uint8
get_key(SDL_Event *event)
{
int sym = event->key.keysym.sym;
SDL_Keymod mods = SDL_GetModState();
if(sym < 0x20 || sym == SDLK_DELETE)
return sym;
if(mods & KMOD_CTRL) {
if(sym < SDLK_a)
return sym;
else if(sym <= SDLK_z)
return sym - (mods & KMOD_SHIFT) * 0x20;
}
return 0x00;
}
2021-09-21 18:41:59 -04:00
static int
handle_events(Uxn *u)
2021-02-08 17:18:01 -05:00
{
SDL_Event event;
while(SDL_PollEvent(&event)) {
/* Window */
if(event.type == SDL_QUIT)
2023-06-09 12:29:25 -04:00
return 0;
else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_EXPOSED)
2023-08-08 13:27:41 -04:00
emu_redraw(u);
else if(event.type == SDL_DROPFILE) {
2023-08-15 22:19:06 -04:00
emu_restart(u, event.drop.file, 0);
SDL_free(event.drop.file);
}
/* Audio */
else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY) {
Uint8 *addr = &u->dev[0x30 + 0x10 * (event.type - audio0_event)];
uxn_eval(u, PEEK2(addr));
}
/* Mouse */
else if(event.type == SDL_MOUSEMOTION)
2023-07-26 11:37:58 -04:00
mouse_pos(u, &u->dev[0x90], clamp(event.motion.x - PAD, 0, uxn_screen.width - 1), clamp(event.motion.y - PAD, 0, uxn_screen.height - 1));
else if(event.type == SDL_MOUSEBUTTONUP)
2023-01-01 16:34:20 -05:00
mouse_up(u, &u->dev[0x90], SDL_BUTTON(event.button.button));
else if(event.type == SDL_MOUSEBUTTONDOWN)
2023-01-01 16:34:20 -05:00
mouse_down(u, &u->dev[0x90], SDL_BUTTON(event.button.button));
else if(event.type == SDL_MOUSEWHEEL)
2023-01-01 16:34:20 -05:00
mouse_scroll(u, &u->dev[0x90], event.wheel.x, event.wheel.y);
/* Controller */
else if(event.type == SDL_TEXTINPUT)
2023-01-01 16:34:20 -05:00
controller_key(u, &u->dev[0x80], event.text.text[0]);
else if(event.type == SDL_KEYDOWN) {
int ksym;
if(get_key(&event))
2023-01-01 16:34:20 -05:00
controller_key(u, &u->dev[0x80], get_key(&event));
else if(get_button(&event))
2023-01-01 16:34:20 -05:00
controller_down(u, &u->dev[0x80], get_button(&event));
2023-07-24 12:48:49 -04:00
else if(event.key.keysym.sym == SDLK_F1)
set_zoom(zoom == 3 ? 1 : zoom + 1, 1);
2023-07-24 12:48:49 -04:00
else if(event.key.keysym.sym == SDLK_F2)
2023-10-25 18:23:56 -04:00
set_debugger(u, !u->dev[0x0e]);
2023-07-24 12:48:49 -04:00
else if(event.key.keysym.sym == SDLK_F3)
capture_screen();
else if(event.key.keysym.sym == SDLK_F4)
2023-08-15 22:19:06 -04:00
emu_restart(u, boot_rom, 0);
2023-08-13 21:48:32 -04:00
else if(event.key.keysym.sym == SDLK_F5)
2023-08-15 22:19:06 -04:00
emu_restart(u, boot_rom, 1);
else if(event.key.keysym.sym == SDLK_F11)
set_fullscreen(!fullscreen, 1);
2023-10-25 17:16:23 -04:00
else if(event.key.keysym.sym == SDLK_F12)
set_borderless(!borderless);
ksym = event.key.keysym.sym;
2023-07-24 12:48:49 -04:00
if(SDL_PeepEvents(&event, 1, SDL_PEEKEVENT, SDL_KEYUP, SDL_KEYUP) == 1 && ksym == event.key.keysym.sym)
return 1;
} else if(event.type == SDL_KEYUP)
2023-01-01 16:34:20 -05:00
controller_up(u, &u->dev[0x80], get_button(&event));
else if(event.type == SDL_JOYAXISMOTION) {
Uint8 vec = get_vector_joystick(&event);
if(!vec)
2023-01-01 16:34:20 -05:00
controller_up(u, &u->dev[0x80], (3 << (!event.jaxis.axis * 2)) << 4);
else
2023-01-01 16:34:20 -05:00
controller_down(u, &u->dev[0x80], (1 << ((vec + !event.jaxis.axis * 2) - 1)) << 4);
} else if(event.type == SDL_JOYBUTTONDOWN)
2023-01-01 16:34:20 -05:00
controller_down(u, &u->dev[0x80], get_button_joystick(&event));
else if(event.type == SDL_JOYBUTTONUP)
2023-01-01 16:34:20 -05:00
controller_up(u, &u->dev[0x80], get_button_joystick(&event));
else if(event.type == SDL_JOYHATMOTION) {
/* NOTE: Assuming there is only one joyhat in the controller */
switch(event.jhat.value) {
case SDL_HAT_UP:
controller_down(u, &u->dev[0x80], 0x10);
break;
case SDL_HAT_DOWN:
controller_down(u, &u->dev[0x80], 0x20);
break;
case SDL_HAT_LEFT:
controller_down(u, &u->dev[0x80], 0x40);
break;
case SDL_HAT_RIGHT:
controller_down(u, &u->dev[0x80], 0x80);
break;
case SDL_HAT_LEFTDOWN:
controller_down(u, &u->dev[0x80], 0x40 | 0x20);
break;
case SDL_HAT_LEFTUP:
controller_down(u, &u->dev[0x80], 0x40 | 0x10);
break;
case SDL_HAT_RIGHTDOWN:
controller_down(u, &u->dev[0x80], 0x80 | 0x20);
break;
case SDL_HAT_RIGHTUP:
controller_down(u, &u->dev[0x80], 0x80 | 0x10);
break;
case SDL_HAT_CENTERED:
/* Set all directions to down */
controller_up(u, &u->dev[0x80], 0x10 | 0x20 | 0x40 | 0x80);
break;
default:
/* Ignore */
break;
}
}
/* Console */
else if(event.type == stdin_event)
console_input(u, event.cbutton.button, CONSOLE_STD);
}
return 1;
}
static int
2023-08-15 21:44:16 -04:00
emu_run(Uxn *u, char *rom)
{
Uint64 next_refresh = 0;
Uint64 frame_interval = SDL_GetPerformanceFrequency() / 60;
Uint8 *vector_addr = &u->dev[0x20];
Uint32 window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
2023-10-25 18:25:32 -04:00
window_created = 1;
2023-10-25 11:24:37 -04:00
if(fullscreen)
window_flags = window_flags | SDL_WINDOW_FULLSCREEN_DESKTOP;
emu_window = SDL_CreateWindow(rom, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, (uxn_screen.width + PAD2) * zoom, (uxn_screen.height + PAD2) * zoom, window_flags);
2023-07-25 17:56:26 -04:00
if(emu_window == NULL)
return system_error("sdl_window", SDL_GetError());
emu_renderer = SDL_CreateRenderer(emu_window, -1, SDL_RENDERER_ACCELERATED);
2023-07-25 17:56:26 -04:00
if(emu_renderer == NULL)
return system_error("sdl_renderer", SDL_GetError());
emu_resize(uxn_screen.width, uxn_screen.height);
/* game loop */
2023-01-28 13:54:54 -05:00
for(;;) {
Uint16 screen_vector;
2023-07-25 17:56:26 -04:00
Uint64 now = SDL_GetPerformanceCounter();
2023-01-28 13:54:54 -05:00
/* .System/halt */
if(u->dev[0x0f])
2023-04-17 14:03:11 -04:00
return system_error("Run", "Ended.");
exec_deadline = now + deadline_interval;
if(!handle_events(u))
return 0;
screen_vector = PEEK2(vector_addr);
2023-07-25 17:56:26 -04:00
if(now >= next_refresh) {
now = SDL_GetPerformanceCounter();
next_refresh = now + frame_interval;
uxn_eval(u, screen_vector);
if(uxn_screen.x2)
2023-08-08 13:27:41 -04:00
emu_redraw(u);
}
2023-07-25 17:56:26 -04:00
if(screen_vector || uxn_screen.x2) {
Uint64 delay_ms = (next_refresh - now) / ms_interval;
if(delay_ms > 0) SDL_Delay(delay_ms);
2023-06-07 11:03:28 -04:00
} else
SDL_WaitEvent(NULL);
2021-02-08 18:46:52 -05:00
}
}
static int
2023-08-15 21:44:16 -04:00
emu_end(Uxn *u)
{
2023-08-25 12:38:03 -04:00
free(u->ram);
2023-08-15 21:44:16 -04:00
#ifdef _WIN32
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
TerminateThread((HANDLE)SDL_GetThreadID(stdin_thread), 0);
#elif !defined(__APPLE__)
close(0); /* make stdin thread exit */
#endif
SDL_Quit();
return u->dev[0x0f] & 0x7f;
}
2021-02-08 18:46:52 -05:00
int
main(int argc, char **argv)
{
Uxn u = {0};
2023-04-17 12:48:27 -04:00
int i = 1;
2023-08-08 18:56:40 -04:00
if(i == argc)
return system_error("usage", "uxnemu [-v] | uxnemu [-f | -2x | -3x | --] file.rom [args...]");
2023-08-08 18:56:40 -04:00
/* Connect Varvara */
2023-08-25 12:38:03 -04:00
system_connect(0x0, SYSTEM_VERSION, SYSTEM_DEIMASK, SYSTEM_DEOMASK);
system_connect(0x1, CONSOLE_VERSION, CONSOLE_DEIMASK, CONSOLE_DEOMASK);
system_connect(0x2, SCREEN_VERSION, SCREEN_DEIMASK, SCREEN_DEOMASK);
system_connect(0x3, AUDIO_VERSION, AUDIO_DEIMASK, AUDIO_DEOMASK);
system_connect(0x4, AUDIO_VERSION, AUDIO_DEIMASK, AUDIO_DEOMASK);
system_connect(0x5, AUDIO_VERSION, AUDIO_DEIMASK, AUDIO_DEOMASK);
system_connect(0x6, AUDIO_VERSION, AUDIO_DEIMASK, AUDIO_DEOMASK);
system_connect(0x8, CONTROL_VERSION, CONTROL_DEIMASK, CONTROL_DEOMASK);
system_connect(0x9, MOUSE_VERSION, MOUSE_DEIMASK, MOUSE_DEOMASK);
system_connect(0xa, FILE_VERSION, FILE_DEIMASK, FILE_DEOMASK);
system_connect(0xb, FILE_VERSION, FILE_DEIMASK, FILE_DEOMASK);
system_connect(0xc, DATETIME_VERSION, DATETIME_DEIMASK, DATETIME_DEOMASK);
/* Read flag. Right now, there can be only one. */
if(argv[i][0] == '-') {
if(argv[i][1] == 'v')
return system_version("Uxnemu - Graphical Varvara Emulator", "30 Oct 2023");
if(argv[i][1] == '-')
i++;
if(strcmp(argv[i], "-2x") == 0 || strcmp(argv[i], "-3x") == 0)
set_zoom(argv[i++][1] - '0', 0);
if(strcmp(argv[i], "-f") == 0) {
i++;
set_fullscreen(1, 0);
}
}
/* Start system. */
if(!emu_init())
2023-08-15 19:06:29 -04:00
return system_error("Init", "Failed to initialize varvara.");
2023-08-15 21:53:51 -04:00
if(!system_init(&u, (Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)), argv[i++]))
2023-08-15 19:06:29 -04:00
return system_error("Init", "Failed to initialize uxn.");
2023-08-15 21:44:16 -04:00
/* Game Loop */
2023-08-15 19:11:21 -04:00
u.dev[0x17] = argc - i;
2023-08-15 21:44:16 -04:00
if(uxn_eval(&u, PAGE_PROGRAM)) {
console_listen(&u, i, argc, argv);
2023-08-15 21:53:51 -04:00
emu_run(&u, boot_rom);
2023-08-15 21:44:16 -04:00
}
return emu_end(&u);
2021-02-08 17:18:01 -05:00
}