diff --git a/build.sh b/build.sh index 00ef6a7..258424d 100755 --- a/build.sh +++ b/build.sh @@ -9,10 +9,10 @@ echo "Building.." mkdir -p bin # Build(debug) -gcc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/devices/system.c src/devices/screen.c src/uxn11.c -o bin/uxn11 -lX11 +gcc -std=c89 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined src/uxn.c src/devices/system.c src/devices/screen.c src/devices/controller.c src/uxn11.c -o bin/uxn11 -lX11 # Build(release) -# gcc src/uxn.c src/devices/system.c src/devices/screen.c src/uxn11.c -o bin/uxn11 -lX11 +# gcc src/uxn.c src/devices/system.c src/devices/screen.c src/devices/controller.c src/uxn11.c -o bin/uxn11 -lX11 echo "Running.." -bin/uxn11 etc/screen.rom +bin/uxn11 etc/controller.rom diff --git a/etc/controller.rom b/etc/controller.rom new file mode 100644 index 0000000..be2836e Binary files /dev/null and b/etc/controller.rom differ diff --git a/src/devices/controller.c b/src/devices/controller.c new file mode 100644 index 0000000..16c5072 --- /dev/null +++ b/src/devices/controller.c @@ -0,0 +1,52 @@ +#include "../uxn.h" +#include "controller.h" + +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +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. +*/ + +void +controller_down(Device *d, Uint8 mask) +{ + if(mask) { + d->dat[2] |= mask; + uxn_eval(d->u, GETVECTOR(d)); + } +} + +void +controller_up(Device *d, Uint8 mask) +{ + if(mask) { + d->dat[2] &= (~mask); + uxn_eval(d->u, GETVECTOR(d)); + } +} + +void +controller_key(Device *d, Uint8 key) +{ + if(key) { + d->dat[3] = key; + uxn_eval(d->u, GETVECTOR(d)); + d->dat[3] = 0x00; + } +} + +void +controller_special(Device *d, Uint8 key) +{ + if(key) { + d->dat[4] = key; + uxn_eval(d->u, GETVECTOR(d)); + d->dat[4] = 0x00; + } +} diff --git a/src/devices/controller.h b/src/devices/controller.h new file mode 100644 index 0000000..ed7f831 --- /dev/null +++ b/src/devices/controller.h @@ -0,0 +1,16 @@ +/* +Copyright (c) 2021 Devine Lu Linvega +Copyright (c) 2021 Andrew Alderwick + +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. +*/ + +void controller_down(Device *d, Uint8 mask); +void controller_up(Device *d, Uint8 mask); +void controller_key(Device *d, Uint8 key); +void controller_special(Device *d, Uint8 key); diff --git a/src/devices/ppu.c b/src/devices/ppu.c deleted file mode 100644 index 1697c8f..0000000 --- a/src/devices/ppu.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "ppu.h" - -/* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick - -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. -*/ - -static Uint8 blending[5][16] = { - {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, - {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, - {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, - {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}, - {1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0}}; - -static Uint8 font[][8] = { - {0x00, 0x7c, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7c}, - {0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}, - {0x00, 0x7c, 0x82, 0x02, 0x7c, 0x80, 0x80, 0xfe}, - {0x00, 0x7c, 0x82, 0x02, 0x1c, 0x02, 0x82, 0x7c}, - {0x00, 0x0c, 0x14, 0x24, 0x44, 0x84, 0xfe, 0x04}, - {0x00, 0xfe, 0x80, 0x80, 0x7c, 0x02, 0x82, 0x7c}, - {0x00, 0x7c, 0x82, 0x80, 0xfc, 0x82, 0x82, 0x7c}, - {0x00, 0x7c, 0x82, 0x02, 0x1e, 0x02, 0x02, 0x02}, - {0x00, 0x7c, 0x82, 0x82, 0x7c, 0x82, 0x82, 0x7c}, - {0x00, 0x7c, 0x82, 0x82, 0x7e, 0x02, 0x82, 0x7c}, - {0x00, 0x7c, 0x82, 0x02, 0x7e, 0x82, 0x82, 0x7e}, - {0x00, 0xfc, 0x82, 0x82, 0xfc, 0x82, 0x82, 0xfc}, - {0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0x82, 0x7c}, - {0x00, 0xfc, 0x82, 0x82, 0x82, 0x82, 0x82, 0xfc}, - {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x82, 0x7c}, - {0x00, 0x7c, 0x82, 0x80, 0xf0, 0x80, 0x80, 0x80}}; - -void -ppu_palette(Ppu *p, Uint8 *addr) -{ - int i, shift; - for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) { - Uint8 - r = (addr[0 + i / 2] >> shift) & 0x0f, - g = (addr[2 + i / 2] >> shift) & 0x0f, - b = (addr[4 + i / 2] >> shift) & 0x0f; - p->palette[i] = 0x0f000000 | r << 16 | g << 8 | b; - p->palette[i] |= p->palette[i] << 4; - } - p->fg.changed = p->bg.changed = 1; -} - -void -ppu_resize(Ppu *p, Uint16 width, Uint16 height) -{ - Uint8 - *bg = realloc(p->bg.pixels, width * height), - *fg = realloc(p->fg.pixels, width * height); - if(bg) p->bg.pixels = bg; - if(fg) p->fg.pixels = fg; - if(bg && fg) { - p->width = width; - p->height = height; - ppu_clear(p, &p->bg); - ppu_clear(p, &p->fg); - } -} - -void -ppu_clear(Ppu *p, Layer *layer) -{ - Uint32 i, size = p->width * p->height; - for(i = 0; i < size; ++i) - layer->pixels[i] = 0x00; - layer->changed = 1; -} - -#pragma weak ppu_redraw -void -ppu_redraw(Ppu *p, Uint32 *screen) -{ - Uint32 i, size = p->width * p->height, palette[16]; - for(i = 0; i < 16; ++i) - palette[i] = p->palette[(i >> 2) ? (i >> 2) : (i & 3)]; - for(i = 0; i < size; ++i) - screen[i] = palette[p->fg.pixels[i] << 2 | p->bg.pixels[i]]; - p->fg.changed = p->bg.changed = 0; -} - -void -ppu_write(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 color) -{ - if(x < p->width && y < p->height) { - Uint32 i = x + y * p->width; - Uint8 prev = layer->pixels[i]; - if(color != prev) { - layer->pixels[i] = color; - layer->changed = 1; - } - } -} - -void -ppu_blit(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp) -{ - int v, h, opaque = blending[4][color]; - for(v = 0; v < 8; ++v) { - Uint16 c = sprite[v] | (twobpp ? sprite[v + 8] : 0) << 8; - for(h = 7; h >= 0; --h, c >>= 1) { - Uint8 ch = (c & 1) | ((c >> 7) & 2); - if(opaque || ch) - ppu_write(p, - layer, - x + (flipx ? 7 - h : h), - y + (flipy ? 7 - v : v), - blending[ch][color]); - } - } -} - -void -ppu_debug(Ppu *p, Uint8 *stack, Uint8 wptr, Uint8 rptr, Uint8 *memory) -{ - Uint8 i, x, y, b; - for(i = 0; i < 0x20; ++i) { - x = ((i % 8) * 3 + 1) * 8, y = (i / 8 + 1) * 8, b = stack[i]; - /* working stack */ - ppu_blit(p, &p->fg, x, y, font[(b >> 4) & 0xf], 1 + (wptr == i) * 0x7, 0, 0, 0); - ppu_blit(p, &p->fg, x + 8, y, font[b & 0xf], 1 + (wptr == i) * 0x7, 0, 0, 0); - y = 0x28 + (i / 8 + 1) * 8; - b = memory[i]; - /* return stack */ - ppu_blit(p, &p->fg, x, y, font[(b >> 4) & 0xf], 3, 0, 0, 0); - ppu_blit(p, &p->fg, x + 8, y, font[b & 0xf], 3, 0, 0, 0); - } - /* return pointer */ - ppu_blit(p, &p->fg, 0x8, y + 0x10, font[(rptr >> 4) & 0xf], 0x2, 0, 0, 0); - ppu_blit(p, &p->fg, 0x10, y + 0x10, font[rptr & 0xf], 0x2, 0, 0, 0); - /* guides */ - for(x = 0; x < 0x10; ++x) { - ppu_write(p, &p->fg, x, p->height / 2, 2); - ppu_write(p, &p->fg, p->width - x, p->height / 2, 2); - ppu_write(p, &p->fg, p->width / 2, p->height - x, 2); - ppu_write(p, &p->fg, p->width / 2, x, 2); - ppu_write(p, &p->fg, p->width / 2 - 0x10 / 2 + x, p->height / 2, 2); - ppu_write(p, &p->fg, p->width / 2, p->height / 2 - 0x10 / 2 + x, 2); - } -} diff --git a/src/devices/ppu.h b/src/devices/ppu.h deleted file mode 100644 index 9e5f217..0000000 --- a/src/devices/ppu.h +++ /dev/null @@ -1,37 +0,0 @@ -#include - -/* -Copyright (c) 2021 Devine Lu Linvega -Copyright (c) 2021 Andrew Alderwick - -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. -*/ - -typedef unsigned char Uint8; -typedef unsigned short Uint16; -typedef unsigned int Uint32; - -typedef struct Layer { - Uint8 *pixels; - Uint8 changed; -} Layer; - -typedef struct Ppu { - Uint32 palette[4]; - Uint16 width, height; - Layer fg, bg; -} Ppu; - -void ppu_palette(Ppu *p, Uint8 *addr); -void ppu_resize(Ppu *p, Uint16 width, Uint16 height); -void ppu_clear(Ppu *p, Layer *layer); -void ppu_redraw(Ppu *p, Uint32 *screen); - -void ppu_write(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 color); -void ppu_blit(Ppu *p, Layer *layer, Uint16 x, Uint16 y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp); -void ppu_debug(Ppu *p, Uint8 *stack, Uint8 wptr, Uint8 rptr, Uint8 *memory); diff --git a/src/uxn11.c b/src/uxn11.c index 346f523..5e8d1f1 100644 --- a/src/uxn11.c +++ b/src/uxn11.c @@ -12,6 +12,9 @@ #include "uxn.h" #include "devices/system.h" #include "devices/screen.h" +#include "devices/controller.h" + +static Device *devctrl; static int error(char *msg, const char *err) @@ -64,6 +67,18 @@ load(Uxn *u, char *filepath) return 1; } +/* /usr/include/X11/keysymdef.h */ + +#define XK_Left 0xff51 +#define XK_Up 0xff52 +#define XK_Right 0xff53 +#define XK_Down 0xff54 + +#define XK_Home 0xff50 +#define XK_Shift 0xffe1 +#define XK_Control 0xffe3 +#define XK_Alt 0xffe9 + void processEvent(Display *display, Window window, XImage *ximage, int width, int height) { @@ -73,6 +88,28 @@ processEvent(Display *display, Window window, XImage *ximage, int width, int hei case Expose: XPutImage(display, window, DefaultGC(display, 0), ximage, 0, 0, 0, 0, width, height); break; + case KeyPress: { + XKeyPressedEvent *e = (XKeyPressedEvent *)&ev; + if(e->keycode == XKeysymToKeycode(display, XK_Up)) controller_down(devctrl, 0x10); + if(e->keycode == XKeysymToKeycode(display, XK_Down)) controller_down(devctrl, 0x20); + if(e->keycode == XKeysymToKeycode(display, XK_Left)) controller_down(devctrl, 0x40); + if(e->keycode == XKeysymToKeycode(display, XK_Right)) controller_down(devctrl, 0x80); + if(e->keycode == XKeysymToKeycode(display, XK_Control)) controller_down(devctrl, 0x01); + if(e->keycode == XKeysymToKeycode(display, XK_Alt)) controller_down(devctrl, 0x02); + if(e->keycode == XKeysymToKeycode(display, XK_Shift)) controller_down(devctrl, 0x04); + if(e->keycode == XKeysymToKeycode(display, XK_Home)) controller_down(devctrl, 0x08); + } break; + case KeyRelease: { + XKeyPressedEvent *e = (XKeyPressedEvent *)&ev; + if(e->keycode == XKeysymToKeycode(display, XK_Up)) controller_up(devctrl, 0x10); + if(e->keycode == XKeysymToKeycode(display, XK_Down)) controller_up(devctrl, 0x20); + if(e->keycode == XKeysymToKeycode(display, XK_Left)) controller_up(devctrl, 0x40); + if(e->keycode == XKeysymToKeycode(display, XK_Right)) controller_up(devctrl, 0x80); + if(e->keycode == XKeysymToKeycode(display, XK_Control)) controller_up(devctrl, 0x01); + if(e->keycode == XKeysymToKeycode(display, XK_Alt)) controller_up(devctrl, 0x02); + if(e->keycode == XKeysymToKeycode(display, XK_Shift)) controller_up(devctrl, 0x04); + if(e->keycode == XKeysymToKeycode(display, XK_Home)) controller_up(devctrl, 0x08); + } break; case ButtonPress: exit(0); } @@ -91,7 +128,7 @@ start(Uxn *u) /* empty */ uxn_port(u, 0x5, nil_dei, nil_deo); /* empty */ uxn_port(u, 0x6, nil_dei, nil_deo); /* empty */ uxn_port(u, 0x7, nil_dei, nil_deo); - /* empty */ uxn_port(u, 0x8, nil_dei, nil_deo); + /* control */ devctrl = uxn_port(u, 0x8, nil_dei, nil_deo); /* empty */ uxn_port(u, 0x9, nil_dei, nil_deo); /* file */ uxn_port(u, 0xa, nil_dei, nil_deo); /* datetime */ uxn_port(u, 0xb, nil_dei, nil_deo); @@ -102,6 +139,11 @@ start(Uxn *u) return 1; } +void +redraw(void) +{ +} + int main(int argc, char **argv) { @@ -132,10 +174,13 @@ main(int argc, char **argv) ximage = XCreateImage(display, visual, DefaultDepth(display, DefaultScreen(display)), ZPixmap, 0, (char *)uxn_screen.pixels, uxn_screen.width, uxn_screen.height, 32, 0); - XSelectInput(display, window, ButtonPressMask | ExposureMask); + XSelectInput(display, window, ButtonPressMask | ExposureMask | KeyPressMask | KeyReleaseMask); XMapWindow(display, window); while(1) { processEvent(display, window, ximage, uxn_screen.width, uxn_screen.height); + + if(uxn_screen.fg.changed || uxn_screen.bg.changed) + redraw(); } return 0; } \ No newline at end of file diff --git a/src/uxncli.c b/src/uxncli.c new file mode 100644 index 0000000..c6da9b2 --- /dev/null +++ b/src/uxncli.c @@ -0,0 +1,148 @@ +#include +#include + +#include "uxn.h" +#include "devices/system.h" +#include "devices/file.h" +#include "devices/datetime.h" + +/* +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. +*/ + +static Device *devfile0; + +static int +error(char *msg, const char *err) +{ + fprintf(stderr, "Error %s: %s\n", msg, err); + return 0; +} + +void +system_deo_special(Device *d, Uint8 port) +{ + (void)d; + (void)port; +} + +static void +console_deo(Device *d, Uint8 port) +{ + FILE *fd = port == 0x8 ? stdout : port == 0x9 ? stderr + : 0; + if(fd) { + fputc(d->dat[port], fd); + fflush(fd); + } +} + +static void +file_deo(Device *d, Uint8 port) +{ + file_i_deo(d - devfile0, d, port); +} + +static Uint8 +file_dei(Device *d, Uint8 port) +{ + return file_i_dei(d - devfile0, d, port); +} + +static Uint8 +nil_dei(Device *d, Uint8 port) +{ + return d->dat[port]; +} + +static void +nil_deo(Device *d, Uint8 port) +{ + (void)d; + (void)port; +} + +static int +console_input(Uxn *u, char c) +{ + Device *d = &u->dev[1]; + d->dat[0x2] = c; + return uxn_eval(u, GETVECTOR(d)); +} + +static void +run(Uxn *u) +{ + Device *d = &u->dev[0]; + while(!d->dat[0xf]) { + int c = fgetc(stdin); + if(c != EOF) + console_input(u, (Uint8)c); + } +} + +static int +load(Uxn *u, char *filepath) +{ + FILE *f; + int r; + if(!(f = fopen(filepath, "rb"))) return 0; + r = fread(u->ram + PAGE_PROGRAM, 1, 0x10000 - PAGE_PROGRAM, f); + fclose(f); + if(r < 1) return 0; + fprintf(stderr, "Loaded %s\n", filepath); + return 1; +} + +static int +start(Uxn *u) +{ + if(!uxn_boot(u, (Uint8 *)calloc(0x10000, sizeof(Uint8)))) + return error("Boot", "Failed"); + /* system */ uxn_port(u, 0x0, system_dei, system_deo); + /* console */ uxn_port(u, 0x1, nil_dei, console_deo); + /* empty */ uxn_port(u, 0x2, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x3, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x4, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x5, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x6, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x7, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x8, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0x9, nil_dei, nil_deo); + /* file0 */ devfile0 = uxn_port(u, 0xa, file_dei, file_deo); + /* file1 */ uxn_port(u, 0xb, file_dei, file_deo); + /* datetime */ uxn_port(u, 0xc, datetime_dei, nil_deo); + /* empty */ uxn_port(u, 0xd, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0xe, nil_dei, nil_deo); + /* empty */ uxn_port(u, 0xf, nil_dei, nil_deo); + return 1; +} + +int +main(int argc, char **argv) +{ + Uxn u; + int i; + if(argc < 2) + return error("Usage", "uxncli game.rom args"); + if(!start(&u)) + return error("Start", "Failed"); + if(!load(&u, argv[1])) + return error("Load", "Failed"); + if(!uxn_eval(&u, PAGE_PROGRAM)) + return error("Init", "Failed"); + for(i = 2; i < argc; i++) { + char *p = argv[i]; + while(*p) console_input(&u, *p++); + console_input(&u, '\n'); + } + run(&u); + return 0; +}