Skip to content

Instantly share code, notes, and snippets.

@iamgreaser
Created October 9, 2024 07:08
Show Gist options
  • Save iamgreaser/0887892e6e8290c7c0c92c470362b447 to your computer and use it in GitHub Desktop.
Save iamgreaser/0887892e6e8290c7c0c92c470362b447 to your computer and use it in GitHub Desktop.
Xlib Wrapper for drludos' LD56 entry "Mini Running Ant"
// 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);
// 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