Mirror of CollapseOS
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

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