Mirror of CollapseOS
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

367 lines
9.3KB

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <stdbool.h>
  4. #include <unistd.h>
  5. #include <xcb/xcb.h>
  6. #define XK_MISCELLANY
  7. #include <X11/keysymdef.h>
  8. #define MAX_ROMSIZE 0x8000
  9. #include "emul.h"
  10. #include "sms_vdp.h"
  11. #include "sms_ports.h"
  12. #include "sms_pad.h"
  13. #include "sms_spi.h"
  14. #include "ps2_kbd.h"
  15. #include "sdc.h"
  16. #define RAMSTART 0xc000
  17. #define VDP_CMD_PORT 0xbf
  18. #define VDP_DATA_PORT 0xbe
  19. #define PORTS_CTL_PORT 0x3f
  20. #define PORTS_IO1_PORT 0xdc
  21. #define PORTS_IO2_PORT 0xdd
  22. #define SDC_CTL 0x05
  23. #define SDC_SPI 0x04
  24. static xcb_connection_t *conn;
  25. static xcb_screen_t *screen;
  26. /* graphics contexts */
  27. static xcb_gcontext_t fg;
  28. /* win */
  29. static xcb_drawable_t win;
  30. // pixels to draw. We draw them in one shot.
  31. static xcb_rectangle_t rectangles[(32*8)*(24*8)];
  32. static Machine *m;
  33. static VDP vdp;
  34. static bool vdp_changed;
  35. static Ports ports;
  36. static Pad pad;
  37. static Kbd kbd;
  38. static bool use_kbd = false;
  39. static SDC sdc;
  40. static SPI spi;
  41. static uint8_t iord_vdp_cmd()
  42. {
  43. return tms_cmd_rd(&vdp.tms);
  44. }
  45. static uint8_t iord_vdp_data()
  46. {
  47. return vdp_data_rd(&vdp);
  48. }
  49. static uint8_t iord_ports_io1()
  50. {
  51. return ports_A_rd(&ports);
  52. }
  53. static uint8_t iord_ports_io2()
  54. {
  55. return ports_B_rd(&ports);
  56. }
  57. static uint8_t iord_pad()
  58. {
  59. return pad_rd(&pad);
  60. }
  61. static uint8_t iord_kbd()
  62. {
  63. return kbd_rd(&kbd);
  64. }
  65. static void iowr_vdp_cmd(uint8_t val)
  66. {
  67. vdp_cmd_wr(&vdp, val);
  68. }
  69. static void iowr_vdp_data(uint8_t val)
  70. {
  71. vdp_changed = true;
  72. vdp_data_wr(&vdp, val);
  73. }
  74. static void iowr_ports_ctl(uint8_t val)
  75. {
  76. ports_ctl_wr(&ports, val);
  77. }
  78. static byte iord_spi()
  79. {
  80. return spi_rd(&spi);
  81. }
  82. static byte spix_sdc(byte val) { return sdc_spix(&sdc, val); }
  83. void create_window()
  84. {
  85. uint32_t mask;
  86. uint32_t values[2];
  87. /* Create the window */
  88. win = xcb_generate_id(conn);
  89. mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
  90. values[0] = screen->white_pixel;
  91. values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS |
  92. XCB_EVENT_MASK_KEY_RELEASE;
  93. xcb_create_window(
  94. conn,
  95. screen->root_depth,
  96. win,
  97. screen->root,
  98. 0, 0,
  99. 500, 500,
  100. 10,
  101. XCB_WINDOW_CLASS_INPUT_OUTPUT,
  102. screen->root_visual,
  103. mask, values);
  104. fg = xcb_generate_id(conn);
  105. mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
  106. values[0] = screen->black_pixel;
  107. values[1] = 0;
  108. xcb_create_gc(conn, fg, screen->root, mask, values);
  109. /* Map the window on the screen */
  110. xcb_map_window(conn, win);
  111. }
  112. // To make things simple with X11, we only support monochrome display, which is
  113. // inverted: As soon as the color of the pixel is non-black, we show a black
  114. // pixel. If the pixel is white, we show black.
  115. void draw_pixels()
  116. {
  117. xcb_get_geometry_reply_t *geom;
  118. geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL);
  119. xcb_clear_area(
  120. conn, 0, win, 0, 0, geom->width, geom->height);
  121. // Figure out inner size to maximize our screen's aspect ratio
  122. int psize = geom->height / vdp.tms.height;
  123. if (geom->width / vdp.tms.width < psize) {
  124. // width is the constraint
  125. psize = geom->width / vdp.tms.width;
  126. }
  127. int innerw = psize * vdp.tms.width;
  128. int innerh = psize * vdp.tms.height;
  129. int innerx = (geom->width - innerw) / 2;
  130. int innery = (geom->height - innerh) / 2;
  131. free(geom);
  132. int drawcnt = 0;
  133. for (int i=0; i<vdp.tms.width; i++) {
  134. for (int j=0; j<vdp.tms.height; j++) {
  135. if (vdp_pixel(&vdp, i, j)) {
  136. int x = innerx + (i*psize);
  137. int y = innery + (j*psize);
  138. rectangles[drawcnt].x = x;
  139. rectangles[drawcnt].y = y;
  140. rectangles[drawcnt].height = psize;
  141. rectangles[drawcnt].width = psize;
  142. drawcnt++;
  143. }
  144. }
  145. }
  146. if (drawcnt) {
  147. xcb_poly_fill_rectangle(
  148. conn, win, fg, drawcnt, rectangles);
  149. }
  150. vdp_changed = false;
  151. xcb_flush(conn);
  152. }
  153. // Returns true to exist event loop
  154. static bool _handle_keypress(xcb_generic_event_t *e)
  155. {
  156. xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
  157. if (ev->detail == 0x09) { // ESC
  158. return true;
  159. }
  160. bool ispressed = e->response_type == XCB_KEY_PRESS;
  161. // change keycode into symbol
  162. xcb_get_keyboard_mapping_reply_t* km = xcb_get_keyboard_mapping_reply(
  163. conn, xcb_get_keyboard_mapping(conn, ev->detail, 1), NULL);
  164. if (km->length) {
  165. xcb_keysym_t* keysyms = (xcb_keysym_t*)(km + 1);
  166. if (use_kbd) {
  167. if ((keysyms[0] == XK_Shift_L) || (keysyms[0] == XK_Shift_R)) {
  168. kbd_pressshift(&kbd, ispressed);
  169. } else if (ispressed) {
  170. fprintf(stderr, "pressing %x\n", keysyms[0]);
  171. kbd_presskey(&kbd, keysyms[0]);
  172. }
  173. } else { // pad
  174. switch (keysyms[0]) {
  175. case 'w':
  176. pad_setbtn(&pad, PAD_BTN_UP, ispressed);
  177. break;
  178. case 'a':
  179. pad_setbtn(&pad, PAD_BTN_LEFT, ispressed);
  180. break;
  181. case 's':
  182. pad_setbtn(&pad, PAD_BTN_DOWN, ispressed);
  183. break;
  184. case 'd':
  185. pad_setbtn(&pad, PAD_BTN_RIGHT, ispressed);
  186. break;
  187. case 'h':
  188. pad_setbtn(&pad, PAD_BTN_A, ispressed);
  189. break;
  190. case 'j':
  191. pad_setbtn(&pad, PAD_BTN_B, ispressed);
  192. break;
  193. case 'k':
  194. pad_setbtn(&pad, PAD_BTN_C, ispressed);
  195. break;
  196. case 'l':
  197. pad_setbtn(&pad, PAD_BTN_START, ispressed);
  198. break;
  199. }
  200. }
  201. }
  202. free(km);
  203. return false;
  204. }
  205. void event_loop()
  206. {
  207. while (1) {
  208. for (int i=0; i<100; i++) {
  209. if (!emul_step()) {
  210. fprintf(stderr, "CPU halted, quitting\n");
  211. usleep(1000 * 1000);
  212. break;
  213. }
  214. spi_pulse(&spi);
  215. }
  216. if (vdp_changed) {
  217. // To avoid overdrawing, we'll let the CPU run a bit to finish its
  218. // drawing operation.
  219. for (int i=0; i<10000; i++) {
  220. if (!emul_step()) {
  221. fprintf(stderr, "CPU halted, quitting\n");
  222. usleep(1000 * 1000);
  223. break;
  224. }
  225. spi_pulse(&spi);
  226. }
  227. draw_pixels();
  228. }
  229. // A low tech way of checking when the window was closed. The proper way
  230. // involving WM_DELETE is too complicated.
  231. xcb_get_geometry_reply_t *geom;
  232. geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL);
  233. if (geom == NULL) {
  234. return; // window has been closed.
  235. } else {
  236. free(geom);
  237. }
  238. xcb_generic_event_t *e = xcb_poll_for_event(conn);
  239. if (!e) {
  240. continue;
  241. }
  242. switch (e->response_type & ~0x80) {
  243. /* ESC to exit */
  244. case XCB_KEY_RELEASE:
  245. case XCB_KEY_PRESS:
  246. if (_handle_keypress(e)) return;
  247. break;
  248. case XCB_EXPOSE: {
  249. draw_pixels();
  250. break;
  251. }
  252. default: {
  253. break;
  254. }
  255. }
  256. free(e);
  257. }
  258. }
  259. static void usage()
  260. {
  261. fprintf(stderr, "Usage: ./sms [-k] [-c sdcard.img] /path/to/rom\n");
  262. }
  263. static byte spi_dbg(byte val)
  264. {
  265. fprintf(stderr, "SPI XCH: %x\n", val);
  266. return val+1;
  267. }
  268. int main(int argc, char *argv[])
  269. {
  270. if (argc < 2) {
  271. usage();
  272. return 1;
  273. }
  274. vdp_init(&vdp);
  275. vdp_changed = false;
  276. ports_init(&ports);
  277. pad_init(&pad, &ports.THA);
  278. kbd_init(&kbd, &ports.THA);
  279. sdc_init(&sdc);
  280. spi_init(&spi, &ports.THB, &ports.TRB, spix_sdc);
  281. int ch;
  282. while ((ch = getopt(argc, argv, "kc:")) != -1) {
  283. switch (ch) {
  284. case 'k':
  285. use_kbd = true;
  286. break;
  287. case 'c':
  288. fprintf(stderr, "Setting up SD card image with %s\n", optarg);
  289. sdc.fp = fopen(optarg, "r+");
  290. if (sdc.fp == NULL) {
  291. fprintf(stderr, "Can't open file\n");
  292. return 1;
  293. }
  294. break;
  295. }
  296. }
  297. if (optind != argc-1) {
  298. usage();
  299. return 1;
  300. }
  301. FILE *fp = fopen(argv[optind], "r");
  302. if (fp == NULL) {
  303. fprintf(stderr, "Can't open %s\n", argv[1]);
  304. return 1;
  305. }
  306. m = emul_init(argv[optind], 0);
  307. if (m == NULL) return 1;
  308. m->ramstart = RAMSTART;
  309. if (use_kbd) {
  310. ports.portA_rd = iord_kbd;
  311. } else {
  312. ports.portA_rd = iord_pad;
  313. }
  314. ports.portB_rd = iord_spi;
  315. m->iord[VDP_CMD_PORT] = iord_vdp_cmd;
  316. m->iord[VDP_DATA_PORT] = iord_vdp_data;
  317. m->iord[PORTS_IO1_PORT] = iord_ports_io1;
  318. m->iord[PORTS_IO2_PORT] = iord_ports_io2;
  319. m->iord[PORTS_CTL_PORT] = iord_noop;
  320. m->iowr[VDP_CMD_PORT] = iowr_vdp_cmd;
  321. m->iowr[VDP_DATA_PORT] = iowr_vdp_data;
  322. m->iowr[PORTS_CTL_PORT] = iowr_ports_ctl;
  323. conn = xcb_connect(NULL, NULL);
  324. screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
  325. create_window();
  326. draw_pixels();
  327. event_loop();
  328. emul_printdebug();
  329. if (sdc.fp) {
  330. fclose(sdc.fp);
  331. }
  332. return 0;
  333. }