-
-
Save iamgreaser/0887892e6e8290c7c0c92c470362b447 to your computer and use it in GitHub Desktop.
Xlib Wrapper for drludos' LD56 entry "Mini Running Ant"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Pokemon Mini to Xlib shim by GreaseMonkey, 2024, CC0 Public Domain | |
#include <stdint.h> | |
#define _interrupt(x) | |
#define _at(x) | |
#define _rom | |
#define _exit _pm_exit | |
struct OAMTYPE { | |
uint8_t x, y; | |
uint8_t tile; | |
uint8_t ctrl; | |
#define OAM_ENABLE 0x01 | |
}; | |
extern struct OAMTYPE OAM[24]; | |
extern uint8_t PRC_MODE; | |
#define COPY_ENABLE 0x01 | |
#define SPRITE_ENABLE 0x02 | |
#define MAP_ENABLE 0x04 | |
#define MAP_12X16 0x08 | |
extern uint8_t PRC_RATE; | |
#define RATE_36FPS 0x01 | |
extern uint8_t PRC_SCROLL_X; | |
extern uint8_t PRC_SCROLL_Y; | |
extern const uint8_t *PRC_MAP; | |
extern const uint8_t *PRC_SPR; | |
extern uint8_t TILEMAP[192]; | |
extern uint8_t TMR1_OSC; | |
extern uint8_t TMR1_SCALE; | |
extern uint8_t TMR1_CTRL; | |
extern uint8_t TMR1_CNT_L; | |
extern uint8_t KEY_PAD; | |
#define KEY_LEFT 0x01 | |
#define KEY_RIGHT 0x02 | |
#define KEY_A 0x10 | |
#define KEY_B 0x20 | |
#define KEY_C 0x40 | |
#define KEY_POWER 0x80 | |
extern uint8_t IRQ_ACT3; | |
#define IRQ3_KEYPOWER 0x01 | |
void wait_vsync(void); | |
void _int(uint8_t code); | |
void _slp(void); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Pokemon Mini to Xlib shim by GreaseMonkey, 2024, CC0 Public Domain | |
#include <assert.h> | |
#include <stdbool.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <X11/keysym.h> | |
#include <X11/Xlib.h> | |
#include <X11/XKBlib.h> | |
#include "pm.h" | |
#define PSCALE 8 | |
struct OAMTYPE OAM[24]; | |
uint8_t PRC_MODE; | |
uint8_t PRC_RATE; | |
uint8_t PRC_SCROLL_X; | |
uint8_t PRC_SCROLL_Y; | |
const uint8_t *PRC_MAP = NULL; | |
const uint8_t *PRC_SPR = NULL; | |
uint8_t TILEMAP[192]; | |
uint8_t TMR1_OSC; | |
uint8_t TMR1_SCALE; | |
uint8_t TMR1_CTRL; | |
uint8_t TMR1_CNT_L; | |
uint8_t KEY_PAD = 0xFF; | |
uint8_t IRQ_ACT3; | |
void _int(uint8_t code) { | |
switch (code) { | |
default: { | |
fprintf(stderr, "FATAL: unhandled interrupt code %02X, exiting!\n", code); | |
exit(1); | |
} break; | |
} | |
} | |
void _slp(void) {} | |
static bool did_init = false; | |
Display *xdisp = NULL; | |
Window xwindow; | |
GC xgc_fg, xgc_bg; | |
static void pump_x_events(void) { | |
XEvent ev; | |
while (XPending(xdisp) >= 1) { | |
XNextEvent(xdisp, &ev); | |
//printf("ev %d\n", ev.type); | |
switch (ev.type) { | |
case KeyPress: | |
case KeyRelease: { | |
uint8_t mask = 0; | |
KeySym sym = XkbKeycodeToKeysym(xdisp, ev.xkey.keycode, 0, 0); | |
switch (sym) { | |
case XK_Left: mask = KEY_LEFT; break; | |
case XK_Right: mask = KEY_RIGHT; break; | |
case XK_Z: case XK_z: mask = KEY_A; break; | |
case XK_X: case XK_x: mask = KEY_B; break; | |
case XK_C: case XK_c: mask = KEY_C; break; | |
} | |
if (mask != 0) { | |
if (ev.type == KeyPress) { | |
KEY_PAD &= ~mask; | |
} else { | |
KEY_PAD |= mask; | |
} | |
} | |
} break; | |
case Expose: { | |
if (ev.xexpose.count == 0) { | |
int32_t i = 0; | |
for (int32_t y = 0; y < 64/8; y++) { | |
for (int32_t x = 0; x < 96/8; x++) { | |
size_t tidx = TILEMAP[i++]; | |
for (size_t sx = 0; sx < 8; sx++) { | |
// Background | |
uint8_t span = PRC_MAP[(tidx<<3)|sx]; | |
// Actually draw a column | |
for (int32_t sy = 0; sy < 8; sy++) { | |
XFillRectangle(xdisp, xwindow, | |
((span&(1<<sy)) != 0) ? xgc_fg : xgc_bg, | |
((((x*8)+sx) - PRC_SCROLL_X + 96*6) % 96)*PSCALE, | |
((((y*8)+sy) - PRC_SCROLL_Y + 0x100) % 64)*PSCALE, | |
PSCALE, PSCALE); | |
} | |
} | |
} | |
} | |
// Now blit sprites | |
for (int32_t oi = 0; oi < 24; oi++) { | |
const struct OAMTYPE *O = &OAM[24-1-oi]; | |
if ((O->ctrl & OAM_ENABLE) == 0) { | |
continue; | |
} | |
const uint8_t *spr = &PRC_SPR[O->tile<<6]; | |
for (size_t sx = 0; sx < 16; sx++) { | |
// Background | |
uint32_t tidx = O->tile; | |
uint32_t pspan0 = PRC_SPR[(tidx<<6)^(sx&0x7)^((sx&0x8)<<2)^0x10]; | |
uint32_t pspan1 = PRC_SPR[(tidx<<6)^(sx&0x7)^((sx&0x8)<<2)^0x18]; | |
uint32_t pspan = pspan0 | (pspan1<<8); | |
uint32_t mspan0 = PRC_SPR[(tidx<<6)^(sx&0x7)^((sx&0x8)<<2)^0x00]; | |
uint32_t mspan1 = PRC_SPR[(tidx<<6)^(sx&0x7)^((sx&0x8)<<2)^0x08]; | |
uint32_t mspan = mspan0 | (mspan1<<8); | |
// Actually draw a column | |
for (int32_t sy = 0; sy < 16; sy++) { | |
if ((mspan&(1<<sy)) == 0) { | |
XFillRectangle(xdisp, xwindow, | |
((pspan&(1<<sy)) != 0) ? xgc_fg : xgc_bg, | |
((O->x+sx-16)&0xFF)*PSCALE, | |
((O->y+sy-16)&0xFF)*PSCALE, | |
PSCALE, PSCALE); | |
} | |
} | |
} | |
} | |
//printf("blit me!\n"); | |
} | |
} break; | |
} | |
} | |
} | |
void wait_vsync(void) { | |
XEvent ev; | |
if (!did_init) { | |
assert(xdisp == NULL); | |
did_init = true; | |
// Connect to X server | |
xdisp = XOpenDisplay(NULL); | |
assert(xdisp != NULL); | |
// Create a window | |
XSetWindowAttributes winattrs; | |
winattrs.event_mask = (0 | |
| KeyPressMask | |
| KeyReleaseMask | |
| ExposureMask | |
); | |
xwindow = XCreateWindow( | |
xdisp, RootWindow(xdisp, DefaultScreen(xdisp)), | |
0, 0, | |
96*PSCALE, 64*PSCALE, 0, | |
DefaultDepth(xdisp, DefaultScreen(xdisp)), | |
InputOutput, | |
DefaultVisual(xdisp, DefaultScreen(xdisp)), | |
CWEventMask, | |
&winattrs); | |
assert(xwindow != 0); | |
// Create graphics contexts | |
XGCValues vals; | |
vals.foreground = BlackPixel(xdisp, DefaultScreen(xdisp)); | |
xgc_fg = XCreateGC(xdisp, xwindow, GCForeground, &vals); | |
assert(xgc_fg != NULL); | |
vals.foreground = WhitePixel(xdisp, DefaultScreen(xdisp)); | |
xgc_bg = XCreateGC(xdisp, xwindow, GCForeground, &vals); | |
assert(xgc_bg != NULL); | |
// Map the window | |
XMapWindow(xdisp, xwindow); | |
} | |
// Blit to screen | |
XClearArea(xdisp, xwindow, 0, 0, 96*PSCALE, 64*PSCALE, True); | |
// Pump event loop | |
pump_x_events(); | |
// Sleep | |
// TODO: Time this properly! --GM | |
usleep(1000000/36); | |
// Pump event loop one more time | |
pump_x_events(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment