Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
6.9KB

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