uxn/src/uxnemu.c

603 lines
16 KiB
C
Raw Normal View History

2021-02-08 17:18:01 -05:00
#include <stdio.h>
2021-06-26 18:52:44 -04:00
#include <unistd.h>
2021-03-26 17:01:51 -04:00
#include <time.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 GCC diagnostic ignored "-Wpedantic"
#include <SDL.h>
2021-05-12 21:28:45 -04:00
#include "devices/ppu.h"
#include "devices/apu.h"
#include "devices/file.h"
2021-06-27 13:53:54 -04:00
#pragma GCC diagnostic pop
2021-02-08 17:18:01 -05:00
/*
Copyright (c) 2021 Devine Lu Linvega
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.
*/
2021-09-21 18:41:59 -04:00
#define WIDTH 64 * 8
#define HEIGHT 40 * 8
2021-08-04 23:30:57 -04:00
#define PAD 4
#define FIXED_SIZE 0
2021-08-04 23:30:57 -04:00
#define POLYPHONY 4
#define BENCH 0
2021-03-16 12:01:47 -04:00
static SDL_Window *gWindow;
static SDL_Texture *gTexture;
static SDL_Renderer *gRenderer;
static SDL_AudioDeviceID audio_id;
static SDL_Rect gRect;
/* devices */
2021-04-07 23:30:10 -04:00
static Ppu ppu;
2021-04-25 10:12:45 -04:00
static Apu apu[POLYPHONY];
2021-07-28 23:20:57 -04:00
static Device *devsystem, *devscreen, *devmouse, *devctrl, *devaudio0, *devconsole;
2021-09-29 20:58:58 -04:00
static Uint8 zoom = 1;
2021-09-20 18:02:42 -04:00
static Uint32 *ppu_screen, stdin_event, audio0_event, palette[16];
2021-02-18 21:16:39 -05:00
2021-06-28 17:42:36 -04:00
static int
2021-04-08 19:47:38 -04:00
clamp(int val, int min, int max)
{
return (val >= min) ? (val <= max) ? val : max : min;
}
2021-02-14 13:22:42 -05:00
2021-06-28 17:42:36 -04:00
static int
2021-02-14 13:22:42 -05:00
error(char *msg, const char *err)
{
2021-07-24 20:09:46 -04:00
fprintf(stderr, "%s: %s\n", msg, err);
2021-02-14 13:22:42 -05:00
return 0;
2021-02-09 13:58:06 -05:00
}
2021-09-21 18:41:59 -04:00
#pragma mark - Generics
static void
audio_callback(void *u, Uint8 *stream, int len)
{
int i, running = 0;
2021-04-25 10:12:45 -04:00
Sint16 *samples = (Sint16 *)stream;
SDL_memset(stream, 0, len);
for(i = 0; i < POLYPHONY; ++i)
running += apu_render(&apu[i], samples, samples + len / 2);
if(!running)
SDL_PauseAudioDevice(audio_id, 1);
2021-04-25 10:12:45 -04:00
(void)u;
}
2021-09-21 18:41:59 -04:00
void
apu_finished_handler(Apu *c)
{
SDL_Event event;
event.type = audio0_event + (c - apu);
SDL_PushEvent(&event);
}
static int
stdin_handler(void *p)
{
SDL_Event event;
event.type = stdin_event;
while(read(0, &event.cbutton.button, 1) > 0)
SDL_PushEvent(&event);
return 0;
(void)p;
}
2021-09-22 14:54:25 -04:00
void
set_palette(Uint8 *addr)
{
int i;
for(i = 0; i < 4; ++i) {
Uint8
r = (*(addr + i / 2) >> (!(i % 2) << 2)) & 0x0f,
g = (*(addr + 2 + i / 2) >> (!(i % 2) << 2)) & 0x0f,
b = (*(addr + 4 + i / 2) >> (!(i % 2) << 2)) & 0x0f;
palette[i] = 0xff000000 | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b;
}
for(i = 4; i < 16; ++i)
palette[i] = palette[i / 4];
2021-09-29 20:58:58 -04:00
ppu.reqdraw = 1;
2021-09-22 14:54:25 -04:00
}
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);
2021-09-22 13:57:43 -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);
}
2021-07-30 17:38:08 -04:00
static void
2021-09-22 13:57:43 -04:00
set_zoom(Uint8 scale)
{
2021-09-29 21:03:56 -04:00
zoom = clamp(scale, 1, 3);
if(!gWindow)
2021-09-22 13:57:43 -04:00
return;
2021-09-29 19:08:36 -04:00
set_window_size(gWindow, (ppu.width + PAD * 2) * zoom, (ppu.height + PAD * 2) * zoom);
2021-09-29 20:58:58 -04:00
ppu.reqdraw = 1;
2021-09-22 13:57:43 -04:00
}
static int
set_size(Uint16 width, Uint16 height, int is_resize)
{
ppu_set_size(&ppu, width, height);
gRect.x = PAD;
gRect.y = PAD;
gRect.w = ppu.width;
gRect.h = ppu.height;
if(!(ppu_screen = realloc(ppu_screen, ppu.width * ppu.height * sizeof(Uint32))))
return error("ppu_screen", "Memory failure");
memset(ppu_screen, 0, ppu.width * ppu.height * sizeof(Uint32));
if(gTexture != NULL) SDL_DestroyTexture(gTexture);
SDL_RenderSetLogicalSize(gRenderer, ppu.width + PAD * 2, ppu.height + PAD * 2);
gTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, ppu.width + PAD * 2, ppu.height + PAD * 2);
if(gTexture == NULL || SDL_SetTextureBlendMode(gTexture, SDL_BLENDMODE_NONE))
return error("gTexture", SDL_GetError());
if(SDL_UpdateTexture(gTexture, NULL, ppu_screen, sizeof(Uint32)) != 0)
return error("SDL_UpdateTexture", SDL_GetError());
2021-09-22 13:57:43 -04:00
if(is_resize)
set_window_size(gWindow, (ppu.width + PAD * 2) * zoom, (ppu.height + PAD * 2) * zoom);
2021-09-29 20:58:58 -04:00
ppu.reqdraw = 1;
2021-09-22 13:57:43 -04:00
return 1;
}
static void
capture_screen(void)
{
const Uint32 format = SDL_PIXELFORMAT_RGB24;
time_t t = time(NULL);
char fname[64];
int w, h;
SDL_Surface *surface;
SDL_GetRendererOutputSize(gRenderer, &w, &h);
surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 24, format);
SDL_RenderReadPixels(gRenderer, NULL, format, surface->pixels, surface->pitch);
strftime(fname, sizeof(fname), "screenshot-%Y%m%d-%H%M%S.bmp", localtime(&t));
SDL_SaveBMP(surface, fname);
SDL_FreeSurface(surface);
fprintf(stderr, "Saved %s\n", fname);
}
2021-06-28 17:42:36 -04:00
static void
redraw(Uxn *u)
2021-02-08 18:46:52 -05:00
{
2021-09-29 20:58:58 -04:00
Uint16 x, y;
2021-07-28 23:20:57 -04:00
if(devsystem->dat[0xe])
2021-11-04 11:42:15 -04:00
ppu_debug(&ppu, u->wst.dat, u->wst.ptr, u->rst.ptr, u->ram.dat);
2021-09-29 20:58:58 -04:00
for(y = 0; y < ppu.height; ++y)
for(x = 0; x < ppu.width; ++x)
2021-09-29 19:01:54 -04:00
ppu_screen[x + y * ppu.width] = palette[ppu_read(&ppu, x, y)];
if(SDL_UpdateTexture(gTexture, &gRect, ppu_screen, ppu.width * sizeof(Uint32)) != 0)
error("SDL_UpdateTexture", SDL_GetError());
SDL_RenderClear(gRenderer);
SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
SDL_RenderPresent(gRenderer);
2021-09-29 20:58:58 -04:00
ppu.reqdraw = 0;
2021-02-08 18:46:52 -05:00
}
2021-09-21 18:41:59 -04:00
static void
quit(void)
{
2021-10-24 16:14:27 -04:00
if(audio_id)
SDL_CloseAudioDevice(audio_id);
2021-09-21 18:41:59 -04:00
SDL_DestroyTexture(gTexture);
gTexture = NULL;
SDL_DestroyRenderer(gRenderer);
gRenderer = NULL;
SDL_DestroyWindow(gWindow);
SDL_Quit();
exit(0);
}
2021-06-28 17:42:36 -04:00
static int
2021-11-08 10:51:09 -05:00
init(void)
2021-02-08 18:46:52 -05:00
{
SDL_AudioSpec as;
SDL_zero(as);
as.freq = SAMPLE_FREQUENCY;
as.format = AUDIO_S16;
as.channels = 2;
as.callback = audio_callback;
as.samples = 512;
as.userdata = NULL;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
error("sdl", SDL_GetError());
if(SDL_Init(SDL_INIT_VIDEO) < 0)
return error("sdl", SDL_GetError());
} else {
audio_id = SDL_OpenAudioDevice(NULL, 0, &as, NULL, 0);
if(!audio_id)
error("sdl_audio", SDL_GetError());
}
2021-11-08 10:51:09 -05:00
gWindow = SDL_CreateWindow("Uxn", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, (WIDTH + PAD * 2) * zoom, (HEIGHT + PAD * 2) * zoom, SDL_WINDOW_SHOWN);
2021-02-08 18:46:52 -05:00
if(gWindow == NULL)
2021-07-24 20:09:46 -04:00
return error("sdl_window", SDL_GetError());
gRenderer = SDL_CreateRenderer(gWindow, -1, 0);
if(gRenderer == NULL)
return error("sdl_renderer", SDL_GetError());
2021-09-21 18:41:59 -04:00
stdin_event = SDL_RegisterEvents(1);
audio0_event = SDL_RegisterEvents(POLYPHONY);
SDL_CreateThread(stdin_handler, "stdin", NULL);
SDL_StartTextInput();
2021-02-18 18:11:02 -05:00
SDL_ShowCursor(SDL_DISABLE);
2021-02-08 18:46:52 -05:00
return 1;
}
2021-06-28 17:42:36 -04:00
static void
2021-04-20 17:30:26 -04:00
domouse(SDL_Event *event)
2021-02-08 18:46:52 -05:00
{
2021-02-24 14:11:19 -05:00
Uint8 flag = 0x00;
Uint16 x = clamp(event->motion.x - PAD, 0, ppu.width - 1);
Uint16 y = clamp(event->motion.y - PAD, 0, ppu.height - 1);
2021-09-22 14:53:13 -04:00
if(event->type == SDL_MOUSEWHEEL) {
devmouse->dat[7] = event->wheel.y;
return;
}
2021-08-29 17:41:05 -04:00
poke16(devmouse->dat, 0x2, x);
poke16(devmouse->dat, 0x4, y);
2021-09-22 14:53:13 -04:00
devmouse->dat[7] = 0x00;
switch(event->button.button) {
case SDL_BUTTON_LEFT: flag = 0x01; break;
case SDL_BUTTON_RIGHT: flag = 0x10; break;
}
2021-02-09 20:22:52 -05:00
switch(event->type) {
2021-02-20 17:07:20 -05:00
case SDL_MOUSEBUTTONDOWN:
2021-04-20 17:30:26 -04:00
devmouse->dat[6] |= flag;
2021-05-29 18:05:43 -04:00
break;
2021-04-08 19:30:44 -04:00
case SDL_MOUSEBUTTONUP:
2021-04-20 17:30:26 -04:00
devmouse->dat[6] &= (~flag);
2021-04-08 19:30:44 -04:00
break;
2021-02-09 20:22:52 -05:00
}
2021-02-08 18:46:52 -05:00
}
2021-02-09 00:59:46 -05:00
#pragma mark - Devices
static Uint8
2021-11-04 13:13:01 -04:00
system_dei(Device *d, Uint8 port)
2021-04-20 13:31:50 -04:00
{
2021-11-04 13:13:01 -04:00
switch(port) {
case 0x2: return d->u->wst.ptr;
case 0x3: return d->u->rst.ptr;
2021-11-04 13:13:01 -04:00
default: return d->dat[port];
2021-08-15 20:48:15 -04:00
}
2021-04-20 13:31:50 -04:00
}
static void
2021-11-04 13:13:01 -04:00
system_deo(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
switch(port) {
case 0x2: d->u->wst.ptr = d->dat[port]; break;
case 0x3: d->u->rst.ptr = d->dat[port]; break;
}
2021-11-04 13:13:01 -04:00
if(port > 0x7 && port < 0xe)
set_palette(&d->dat[0x8]);
}
static void
2021-11-04 13:13:01 -04:00
console_deo(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
if(port == 0x1)
d->vector = peek16(d->dat, 0x0);
2021-11-04 13:13:01 -04:00
if(port > 0x7)
write(port - 0x7, (char *)&d->dat[port], 1);
}
static Uint8
2021-11-04 13:13:01 -04:00
screen_dei(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
switch(port) {
case 0x2: return ppu.width >> 8;
case 0x3: return ppu.width;
case 0x4: return ppu.height >> 8;
case 0x5: return ppu.height;
2021-11-04 13:13:01 -04:00
default: return d->dat[port];
}
}
static void
2021-11-04 13:13:01 -04:00
screen_deo(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
switch(port) {
case 0x1: d->vector = peek16(d->dat, 0x0); break;
case 0x5:
if(!FIXED_SIZE) set_size(peek16(d->dat, 0x2), peek16(d->dat, 0x4), 1);
break;
case 0xe: {
Uint16 x = peek16(d->dat, 0x8);
Uint16 y = peek16(d->dat, 0xa);
Uint8 layer = d->dat[0xe] & 0x40;
ppu_write(&ppu, !!layer, x, y, d->dat[0xe] & 0x3);
if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 1); /* auto x+1 */
if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 1); /* auto y+1 */
break;
}
case 0xf: {
Uint16 x = peek16(d->dat, 0x8);
Uint16 y = peek16(d->dat, 0xa);
Uint8 layer = d->dat[0xf] & 0x40;
Uint8 *addr = &d->mem[peek16(d->dat, 0xc)];
if(d->dat[0xf] & 0x80) {
ppu_2bpp(&ppu, !!layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20);
if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 16); /* auto addr+16 */
} else {
ppu_1bpp(&ppu, !!layer, x, y, addr, d->dat[0xf] & 0xf, d->dat[0xf] & 0x10, d->dat[0xf] & 0x20);
if(d->dat[0x6] & 0x04) poke16(d->dat, 0xc, peek16(d->dat, 0xc) + 8); /* auto addr+8 */
2021-09-10 11:52:07 -04:00
}
if(d->dat[0x6] & 0x01) poke16(d->dat, 0x8, x + 8); /* auto x+8 */
if(d->dat[0x6] & 0x02) poke16(d->dat, 0xa, y + 8); /* auto y+8 */
break;
}
}
}
static void
2021-11-04 13:13:01 -04:00
file_deo(Device *d, Uint8 port)
2021-03-04 23:15:01 -05:00
{
switch(port) {
2021-11-07 10:30:56 -05:00
case 0x1: d->vector = peek16(d->dat, 0x0); break;
case 0x9: poke16(d->dat, 0x2, file_init(&d->mem[peek16(d->dat, 0x8)])); break;
case 0xd: poke16(d->dat, 0x2, file_read(&d->mem[peek16(d->dat, 0xc)], peek16(d->dat, 0xa))); break;
case 0xf: poke16(d->dat, 0x2, file_write(&d->mem[peek16(d->dat, 0xe)], peek16(d->dat, 0xa), d->dat[0x7])); break;
case 0x5: poke16(d->dat, 0x2, file_stat(&d->mem[peek16(d->dat, 0x4)], peek16(d->dat, 0xa))); break;
case 0x6: poke16(d->dat, 0x2, file_delete()); break;
2021-03-04 23:15:01 -05:00
}
}
static Uint8
2021-11-04 13:13:01 -04:00
audio_dei(Device *d, Uint8 port)
{
2021-04-26 14:49:34 -04:00
Apu *c = &apu[d - devaudio0];
2021-11-04 13:13:01 -04:00
if(!audio_id) return d->dat[port];
switch(port) {
case 0x4: return apu_get_vu(c);
case 0x2: poke16(d->dat, 0x2, c->i); /* fall through */
2021-11-04 13:13:01 -04:00
default: return d->dat[port];
}
}
static void
2021-11-04 13:13:01 -04:00
audio_deo(Device *d, Uint8 port)
{
Apu *c = &apu[d - devaudio0];
if(!audio_id) return;
2021-11-04 13:13:01 -04:00
if(port == 0xf) {
2021-04-26 14:49:34 -04:00
SDL_LockAudioDevice(audio_id);
2021-08-29 17:41:05 -04:00
c->len = peek16(d->dat, 0xa);
c->addr = &d->mem[peek16(d->dat, 0xc)];
2021-04-26 14:49:34 -04:00
c->volume[0] = d->dat[0xe] >> 4;
c->volume[1] = d->dat[0xe] & 0xf;
2021-04-25 10:12:45 -04:00
c->repeat = !(d->dat[0xf] & 0x80);
2021-08-29 17:41:05 -04:00
apu_start(c, peek16(d->dat, 0x8), d->dat[0xf] & 0x7f);
2021-04-26 14:49:34 -04:00
SDL_UnlockAudioDevice(audio_id);
SDL_PauseAudioDevice(audio_id, 0);
2021-04-25 10:12:45 -04:00
}
2021-03-24 19:30:52 -04:00
}
static Uint8
2021-11-04 13:13:01 -04:00
datetime_dei(Device *d, Uint8 port)
2021-03-26 17:01:51 -04:00
{
time_t seconds = time(NULL);
struct tm *t = localtime(&seconds);
2021-11-04 13:13:01 -04:00
switch(port) {
case 0x0: return (t->tm_year + 1900) >> 8;
case 0x1: return (t->tm_year + 1900);
case 0x2: return t->tm_mon;
case 0x3: return t->tm_mday;
case 0x4: return t->tm_hour;
case 0x5: return t->tm_min;
case 0x6: return t->tm_sec;
case 0x7: return t->tm_wday;
case 0x8: return t->tm_yday >> 8;
case 0x9: return t->tm_yday;
case 0xa: return t->tm_isdst;
2021-11-04 13:13:01 -04:00
default: return d->dat[port];
}
2021-03-26 17:01:51 -04:00
}
static Uint8
2021-11-04 13:13:01 -04:00
nil_dei(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
return d->dat[port];
}
static void
2021-11-04 13:13:01 -04:00
nil_deo(Device *d, Uint8 port)
{
2021-11-04 13:13:01 -04:00
if(port == 0x1) d->vector = peek16(d->dat, 0x0);
}
2021-11-08 10:51:09 -05:00
/* Boot */
static int
load(Uxn *u, char *rom)
{
FILE *f;
if(!(f = fopen(rom, "rb"))) return 0;
fread(u->ram.dat + PAGE_PROGRAM, sizeof(u->ram.dat) - PAGE_PROGRAM, 1, f);
fprintf(stderr, "Loaded %s\n", rom);
SDL_SetWindowTitle(gWindow, rom);
return 1;
}
static int
2021-11-08 12:12:17 -05:00
start(Uxn *u, char *rom)
2021-11-08 10:51:09 -05:00
{
if(!uxn_boot(u))
return error("Boot", "Failed to start uxn.");
if(!load(u, rom))
return error("Boot", "Failed to load rom.");
/* system */ devsystem = uxn_port(u, 0x0, system_dei, system_deo);
/* console */ devconsole = uxn_port(u, 0x1, nil_dei, console_deo);
/* screen */ devscreen = uxn_port(u, 0x2, screen_dei, screen_deo);
/* audio0 */ devaudio0 = uxn_port(u, 0x3, audio_dei, audio_deo);
/* audio1 */ uxn_port(u, 0x4, audio_dei, audio_deo);
/* audio2 */ uxn_port(u, 0x5, audio_dei, audio_deo);
/* audio3 */ uxn_port(u, 0x6, audio_dei, audio_deo);
/* unused */ uxn_port(u, 0x7, nil_dei, nil_deo);
/* control */ devctrl = uxn_port(u, 0x8, nil_dei, nil_deo);
/* mouse */ devmouse = uxn_port(u, 0x9, nil_dei, nil_deo);
/* file */ uxn_port(u, 0xa, nil_dei, file_deo);
/* datetime */ uxn_port(u, 0xb, datetime_dei, nil_deo);
/* unused */ uxn_port(u, 0xc, nil_dei, nil_deo);
/* unused */ uxn_port(u, 0xd, nil_dei, nil_deo);
/* unused */ uxn_port(u, 0xe, nil_dei, nil_deo);
/* unused */ uxn_port(u, 0xf, nil_dei, nil_deo);
if(!uxn_eval(u, PAGE_PROGRAM))
return error("Boot", "Failed to start rom.");
return 1;
}
void
2021-11-08 12:12:17 -05:00
restart(Uxn *u)
2021-11-08 10:51:09 -05:00
{
set_size(WIDTH, HEIGHT, 1);
2021-11-08 12:12:17 -05:00
start(u, "boot.rom");
2021-11-08 10:51:09 -05:00
}
static void
doctrl(Uxn *u, SDL_Event *event, int z)
{
Uint8 flag = 0x00;
SDL_Keymod mods = SDL_GetModState();
devctrl->dat[2] &= 0xf8;
if(mods & KMOD_CTRL) devctrl->dat[2] |= 0x01;
if(mods & KMOD_ALT) devctrl->dat[2] |= 0x02;
if(mods & KMOD_SHIFT) devctrl->dat[2] |= 0x04;
/* clang-format off */
switch(event->key.keysym.sym) {
case SDLK_ESCAPE: flag = 0x08; break;
case SDLK_UP: flag = 0x10; break;
case SDLK_DOWN: flag = 0x20; break;
case SDLK_LEFT: flag = 0x40; break;
case SDLK_RIGHT: flag = 0x80; break;
case SDLK_F1: if(z) set_zoom(zoom > 2 ? 1 : zoom + 1); break;
case SDLK_F2: if(z) devsystem->dat[0xe] = !devsystem->dat[0xe]; break;
case SDLK_F3: if(z) capture_screen(); break;
2021-11-08 12:12:17 -05:00
case SDLK_F4: if(z) restart(u); break;
2021-11-08 10:51:09 -05:00
}
/* clang-format on */
if(z) {
devctrl->dat[2] |= flag;
if(event->key.keysym.sym < 0x20 || event->key.keysym.sym == SDLK_DELETE)
devctrl->dat[3] = event->key.keysym.sym;
else if((mods & KMOD_CTRL) && event->key.keysym.sym >= SDLK_a && event->key.keysym.sym <= SDLK_z)
devctrl->dat[3] = event->key.keysym.sym - (mods & KMOD_SHIFT) * 0x20;
} else
devctrl->dat[2] &= ~flag;
}
static const char *errors[] = {"underflow", "overflow", "division by zero"};
int
2021-08-01 17:46:43 -04:00
uxn_halt(Uxn *u, Uint8 error, char *name, int id)
{
fprintf(stderr, "Halted: %s %s#%04x, at 0x%04x\n", name, errors[error - 1], id, u->ram.ptr);
return 0;
}
static int
console_input(Uxn *u, char c)
{
devconsole->dat[0x2] = c;
return uxn_eval(u, devconsole->vector);
}
2021-09-21 18:41:59 -04:00
static int
2021-06-29 09:43:28 -04:00
run(Uxn *u)
2021-02-08 17:18:01 -05:00
{
redraw(u);
2021-10-02 15:06:50 -04:00
while(!devsystem->dat[0xf]) {
2021-04-04 23:58:47 -04:00
SDL_Event event;
double elapsed, start = 0;
2021-08-04 23:30:57 -04:00
if(!BENCH)
2021-05-19 03:29:00 -04:00
start = SDL_GetPerformanceCounter();
2021-04-04 23:58:47 -04:00
while(SDL_PollEvent(&event) != 0) {
switch(event.type) {
2021-04-07 16:50:35 -04:00
case SDL_QUIT:
2021-09-21 18:41:59 -04:00
return error("Run", "Quit.");
case SDL_TEXTINPUT:
2021-06-20 17:38:45 -04:00
devctrl->dat[3] = event.text.text[0]; /* fall-thru */
2021-04-08 19:30:44 -04:00
case SDL_KEYDOWN:
case SDL_KEYUP:
2021-11-08 10:51:09 -05:00
doctrl(u, &event, event.type == SDL_KEYDOWN);
uxn_eval(u, devctrl->vector);
devctrl->dat[3] = 0;
2021-04-10 23:55:31 -04:00
break;
case SDL_MOUSEWHEEL:
2021-04-04 23:58:47 -04:00
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
2021-04-05 16:00:55 -04:00
case SDL_MOUSEMOTION:
2021-04-20 17:30:26 -04:00
domouse(&event);
uxn_eval(u, devmouse->vector);
2021-04-05 16:00:55 -04:00
break;
2021-04-04 23:58:47 -04:00
case SDL_WINDOWEVENT:
if(event.window.event == SDL_WINDOWEVENT_EXPOSED)
redraw(u);
2021-04-04 23:58:47 -04:00
break;
default:
if(event.type == stdin_event) {
console_input(u, event.cbutton.button);
2021-08-29 13:36:23 -04:00
} else if(event.type >= audio0_event && event.type < audio0_event + POLYPHONY)
2021-08-29 17:41:05 -04:00
uxn_eval(u, peek16((devaudio0 + (event.type - audio0_event))->dat, 0));
2021-04-04 23:58:47 -04:00
}
}
uxn_eval(u, devscreen->vector);
2021-09-29 20:58:58 -04:00
if(ppu.reqdraw || devsystem->dat[0xe])
redraw(u);
2021-08-04 23:30:57 -04:00
if(!BENCH) {
2021-05-19 03:29:00 -04:00
elapsed = (SDL_GetPerformanceCounter() - start) / (double)SDL_GetPerformanceFrequency() * 1000.0f;
SDL_Delay(clamp(16.666f - elapsed, 0, 1000));
}
2021-02-08 18:46:52 -05:00
}
2021-09-21 18:41:59 -04:00
return error("Run", "Ended.");
2021-02-08 18:46:52 -05:00
}
int
main(int argc, char **argv)
{
SDL_DisplayMode DM;
2021-02-09 13:58:06 -05:00
Uxn u;
int i, loaded = 0;
2021-11-08 10:51:09 -05:00
if(!init())
return error("Init", "Failed to initialize emulator.");
if(!set_size(WIDTH, HEIGHT, 0))
return error("Window", "Failed to set window size.");
2021-08-29 22:52:12 -04:00
2021-09-22 14:21:57 -04:00
/* set default zoom */
if(SDL_GetCurrentDisplayMode(0, &DM) == 0)
set_zoom(DM.w / 1280);
for(i = 1; i < argc; ++i) {
/* get default zoom from flags */
2021-09-21 18:56:42 -04:00
if(strcmp(argv[i], "-s") == 0) {
if(i < argc - 1)
2021-09-29 21:03:56 -04:00
set_zoom(atoi(argv[++i]));
2021-09-21 18:56:42 -04:00
else
return error("Opt", "-s No scale provided.");
} else if(!loaded++) {
2021-11-08 12:12:17 -05:00
if(!start(&u, argv[i]))
2021-11-08 10:51:09 -05:00
return error("Boot", "Failed to boot.");
} else {
char *p = argv[i];
while(*p) console_input(&u, *p++);
console_input(&u, '\n');
2021-09-21 18:56:42 -04:00
}
}
if(!loaded)
2021-11-02 23:15:25 -04:00
return error("usage", "uxnemu [-s scale] file.rom");
2021-09-22 14:21:57 -04:00
2021-06-29 09:43:28 -04:00
run(&u);
2021-02-08 18:46:52 -05:00
quit();
2021-02-08 17:18:01 -05:00
return 0;
}