Mirror of CollapseOS
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

381 行
9.5KB

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