#include #include #include #include #include "../../emul.h" #include "vdp.h" #include "port.h" #include "pad.h" #define RAMSTART 0xc000 #define VDP_CMD_PORT 0xbf #define VDP_DATA_PORT 0xbe #define PORTS_CTL_PORT 0x3f #define PORTS_IO1_PORT 0xdc #define PORTS_IO2_PORT 0xdd #define MAX_ROMSIZE 0x8000 static xcb_connection_t *conn; static xcb_screen_t *screen; /* graphics contexts */ static xcb_gcontext_t fg; /* win */ static xcb_drawable_t win; // pixels to draw. We draw them in one shot. static xcb_rectangle_t rectangles[VDP_SCREENW*VDP_SCREENH]; static Machine *m; static VDP vdp; static bool vdp_changed; static Ports ports; static Pad pad; static uint8_t iord_vdp_cmd() { return vdp_cmd_rd(&vdp); } static uint8_t iord_vdp_data() { return vdp_data_rd(&vdp); } static uint8_t iord_ports_io1() { return ports_A_rd(&ports); } static uint8_t iord_ports_io2() { return ports_B_rd(&ports); } static uint8_t iord_pad() { return pad_rd(&pad); } static void iowr_vdp_cmd(uint8_t val) { vdp_cmd_wr(&vdp, val); } static void iowr_vdp_data(uint8_t val) { vdp_changed = true; vdp_data_wr(&vdp, val); } static void iowr_ports_ctl(uint8_t val) { ports_ctl_wr(&ports, val); } void create_window() { uint32_t mask; uint32_t values[2]; /* Create the window */ win = xcb_generate_id(conn); mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; values[0] = screen->white_pixel; values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE; xcb_create_window( conn, screen->root_depth, win, screen->root, 0, 0, 500, 500, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); fg = xcb_generate_id(conn); mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; values[0] = screen->black_pixel; values[1] = 0; xcb_create_gc(conn, fg, screen->root, mask, values); /* Map the window on the screen */ xcb_map_window(conn, win); } // To make things simple with X11, we only support monochrome display, which is // inverted: As soon as the color of the pixel is non-black, we show a black // pixel. If the pixel is white, we show black. void draw_pixels() { xcb_get_geometry_reply_t *geom; geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL); xcb_clear_area( conn, 0, win, 0, 0, geom->width, geom->height); // Figure out inner size to maximize our screen's aspect ratio int psize = geom->height / VDP_SCREENH; if (geom->width / VDP_SCREENW < psize) { // width is the constraint psize = geom->width / VDP_SCREENW; } int innerw = psize * VDP_SCREENW; int innerh = psize * VDP_SCREENH; int innerx = (geom->width - innerw) / 2; int innery = (geom->height - innerh) / 2; free(geom); int drawcnt = 0; for (int i=0; iresponse_type & ~0x80) { /* ESC to exit */ case XCB_KEY_RELEASE: case XCB_KEY_PRESS: { xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e; bool ispressed = e->response_type == XCB_KEY_PRESS; switch (ev->detail) { case 0x09: return; // ESC case 0x19: // W pad_setbtn(&pad, PAD_BTN_UP, ispressed); break; case 0x26: // A pad_setbtn(&pad, PAD_BTN_LEFT, ispressed); break; case 0x27: // S pad_setbtn(&pad, PAD_BTN_DOWN, ispressed); break; case 0x28: // D pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed); break; case 0x2b: // H pad_setbtn(&pad, PAD_BTN_A, ispressed); break; case 0x2c: // J pad_setbtn(&pad, PAD_BTN_B, ispressed); break; case 0x2d: // K pad_setbtn(&pad, PAD_BTN_C, ispressed); break; case 0x2e: // L pad_setbtn(&pad, PAD_BTN_START, ispressed); break; } break; } case XCB_EXPOSE: { draw_pixels(); break; } default: { break; } } free(e); } } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: ./sms /path/to/rom\n"); return 1; } FILE *fp = fopen(argv[1], "r"); if (fp == NULL) { fprintf(stderr, "Can't open %s\n", argv[1]); return 1; } m = emul_init(); m->ramstart = RAMSTART; int i = 0; int c; while ((c = fgetc(fp)) != EOF && i < MAX_ROMSIZE) { m->mem[i++] = c & 0xff; } pclose(fp); if (c != EOF) { fprintf(stderr, "ROM image too large.\n"); return 1; } vdp_init(&vdp); vdp_changed = false; ports_init(&ports); ports.portA_rd = iord_pad; pad_init(&pad, &ports.THA); m->iord[VDP_CMD_PORT] = iord_vdp_cmd; m->iord[VDP_DATA_PORT] = iord_vdp_data; m->iord[PORTS_IO1_PORT] = iord_ports_io1; m->iord[PORTS_IO2_PORT] = iord_ports_io2; m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd; m->iowr[VDP_DATA_PORT] = iowr_vdp_data; m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl; conn = xcb_connect(NULL, NULL); screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; create_window(); draw_pixels(); event_loop(); emul_printdebug(); return 0; }