Mirror of CollapseOS
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

314 Zeilen
8.1KB

  1. /* TI-84+
  2. *
  3. * A plain TI-84 with its built-in keyboard as an input and its LCD screen
  4. * as an output.
  5. *
  6. * Uses XCB to render the screen and record keystrokes.
  7. */
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <stdbool.h>
  11. #include <xcb/xcb.h>
  12. #define XK_MISCELLANY
  13. #include <X11/keysymdef.h>
  14. #define MAX_ROMSIZE 0x4000
  15. #include "emul.h"
  16. #include "t6a04.h"
  17. #include "ti84_kbd.h"
  18. #define RAMSTART 0x8000
  19. #define KBD_PORT 0x01
  20. #define INTERRUPT_PORT 0x03
  21. #define LCD_CMD_PORT 0x10
  22. #define LCD_DATA_PORT 0x11
  23. static xcb_connection_t *conn;
  24. static xcb_screen_t *screen;
  25. /* graphics contexts */
  26. static xcb_gcontext_t fg;
  27. /* win */
  28. static xcb_drawable_t win;
  29. // pixels to draw. We draw them in one shot.
  30. static xcb_rectangle_t rectangles[96*64];
  31. static Machine *m;
  32. static T6A04 lcd;
  33. static bool lcd_changed;
  34. static KBD kbd;
  35. static bool on_was_pressed;
  36. static uint8_t iord_lcd_cmd()
  37. {
  38. return t6a04_cmd_rd(&lcd);
  39. }
  40. static uint8_t iord_lcd_data()
  41. {
  42. return t6a04_data_rd(&lcd);
  43. }
  44. static uint8_t iord_kbd()
  45. {
  46. return kbd_rd(&kbd);
  47. }
  48. static uint8_t iord_interrupt()
  49. {
  50. return on_was_pressed ? 1 : 0;
  51. }
  52. static void iowr_lcd_cmd(uint8_t val)
  53. {
  54. t6a04_cmd_wr(&lcd, val);
  55. }
  56. static void iowr_lcd_data(uint8_t val)
  57. {
  58. lcd_changed = true;
  59. t6a04_data_wr(&lcd, val);
  60. }
  61. static void iowr_kbd(uint8_t val)
  62. {
  63. kbd_wr(&kbd, val);
  64. }
  65. static void iowr_interrupt(uint8_t val)
  66. {
  67. if ((val & 1) == 0) {
  68. on_was_pressed = false;
  69. }
  70. }
  71. static uint8_t keycode_to_tikbd(xcb_keycode_t kc)
  72. {
  73. // First, change keycode into symbol
  74. xcb_get_keyboard_mapping_reply_t* km = xcb_get_keyboard_mapping_reply(
  75. conn, xcb_get_keyboard_mapping(conn, kc, 1), NULL);
  76. xcb_keysym_t* keysyms = (xcb_keysym_t*)(km + 1);
  77. uint8_t res = 0;
  78. for (int i=0; i<km->length; i++) {
  79. switch (keysyms[0]) {
  80. case XK_Shift_L: res = KBD_2ND; break;
  81. case XK_Control_L: res = KBD_ALPHA; break;
  82. case XK_Return: res = 0x10; break;
  83. case XK_Delete: res = 0x67; break;
  84. case ' ': res = 0x40; break;
  85. case '1': res = 0x41; break;
  86. case '2': res = 0x31; break;
  87. case '3': res = 0x21; break;
  88. case '4': res = 0x42; break;
  89. case '5': res = 0x32; break;
  90. case '6': res = 0x22; break;
  91. case '7': res = 0x43; break;
  92. case '8': res = 0x33; break;
  93. case '9': res = 0x23; break;
  94. case '0': res = 0x40; break;
  95. case '-': res = 0x12; break;
  96. case '+': res = 0x11; break;
  97. case 'q': res = 0x23; break;
  98. case 'w': res = 0x12; break;
  99. case 'e': res = 0x45; break;
  100. case 'r': res = 0x13; break;
  101. case 't': res = 0x42; break;
  102. case 'y': res = 0x41; break;
  103. case 'u': res = 0x32; break;
  104. case 'i': res = 0x54; break;
  105. case 'o': res = 0x43; break;
  106. case 'p': res = 0x33; break;
  107. case '(': res = 0x34; break;
  108. case ')': res = 0x24; break;
  109. case 'a': res = 0x56; break;
  110. case 's': res = 0x52; break;
  111. case 'd': res = 0x55; break;
  112. case 'f': res = 0x35; break;
  113. case 'g': res = 0x25; break;
  114. case 'h': res = 0x15; break;
  115. case 'j': res = 0x44; break;
  116. case 'k': res = 0x34; break;
  117. case 'l': res = 0x24; break;
  118. case ':': res = 0x30; break;
  119. case '"': res = 0x11; break;
  120. case 'z': res = 0x31; break;
  121. case 'x': res = 0x51; break;
  122. case 'c': res = 0x36; break;
  123. case 'v': res = 0x22; break;
  124. case 'b': res = 0x46; break;
  125. case 'n': res = 0x53; break;
  126. case 'm': res = 0x14; break;
  127. case ',': res = 0x44; break;
  128. case '.': res = 0x30; break;
  129. case '?': res = 0x20; break;
  130. }
  131. if (res) {
  132. break;
  133. }
  134. }
  135. free(km);
  136. return res;
  137. }
  138. void create_window()
  139. {
  140. uint32_t mask;
  141. uint32_t values[2];
  142. /* Create the window */
  143. win = xcb_generate_id(conn);
  144. mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
  145. values[0] = screen->white_pixel;
  146. values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS |
  147. XCB_EVENT_MASK_KEY_RELEASE;
  148. xcb_create_window(
  149. conn,
  150. screen->root_depth,
  151. win,
  152. screen->root,
  153. 0, 0,
  154. 500, 500,
  155. 10,
  156. XCB_WINDOW_CLASS_INPUT_OUTPUT,
  157. screen->root_visual,
  158. mask, values);
  159. fg = xcb_generate_id(conn);
  160. mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
  161. values[0] = screen->black_pixel;
  162. values[1] = 0;
  163. xcb_create_gc(conn, fg, screen->root, mask, values);
  164. /* Map the window on the screen */
  165. xcb_map_window(conn, win);
  166. }
  167. bool get_pixel(int x, int y)
  168. {
  169. return t6a04_pixel(&lcd, x, y);
  170. }
  171. void draw_pixels()
  172. {
  173. xcb_get_geometry_reply_t *geom;
  174. geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL);
  175. xcb_clear_area(
  176. conn, 0, win, 0, 0, geom->width, geom->height);
  177. // Figure out inner size to maximize a 96x64 screen (1.5 aspect ratio)
  178. int psize = geom->height / 64;
  179. if (geom->width / 96 < psize) {
  180. // width is the constraint
  181. psize = geom->width / 96;
  182. }
  183. int innerw = psize * 96;
  184. int innerh = psize * 64;
  185. int innerx = (geom->width - innerw) / 2;
  186. int innery = (geom->height - innerh) / 2;
  187. free(geom);
  188. int drawcnt = 0;
  189. for (int i=0; i<96; i++) {
  190. for (int j=0; j<64; j++) {
  191. if (get_pixel(i, j)) {
  192. int x = innerx + (i*psize);
  193. int y = innery + (j*psize);
  194. rectangles[drawcnt].x = x;
  195. rectangles[drawcnt].y = y;
  196. rectangles[drawcnt].height = psize;
  197. rectangles[drawcnt].width = psize;
  198. drawcnt++;
  199. }
  200. }
  201. }
  202. if (drawcnt) {
  203. xcb_poly_fill_rectangle(
  204. conn, win, fg, drawcnt, rectangles);
  205. }
  206. lcd_changed = false;
  207. xcb_flush(conn);
  208. }
  209. void event_loop()
  210. {
  211. while (1) {
  212. emul_steps(100);
  213. if (lcd_changed) {
  214. draw_pixels();
  215. }
  216. // A low tech way of checking when the window was closed. The proper way
  217. // involving WM_DELETE is too complicated.
  218. xcb_get_geometry_reply_t *geom;
  219. geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL);
  220. if (geom == NULL) {
  221. return; // window has been closed.
  222. } else {
  223. free(geom);
  224. }
  225. xcb_generic_event_t *e = xcb_poll_for_event(conn);
  226. if (!e) {
  227. continue;
  228. }
  229. switch (e->response_type & ~0x80) {
  230. /* ESC to exit */
  231. case XCB_KEY_RELEASE:
  232. case XCB_KEY_PRESS: {
  233. xcb_key_press_event_t *ev = (xcb_key_press_event_t *)e;
  234. if (ev->detail == 0x09) return;
  235. if (ev->detail == 0x31 && e->response_type == XCB_KEY_PRESS) {
  236. // tilde, mapped to ON
  237. on_was_pressed = true;
  238. Z80INT(&m->cpu, 0);
  239. Z80Execute(&m->cpu); // unhalts the CPU
  240. }
  241. uint8_t key = keycode_to_tikbd(ev->detail);
  242. if (key) {
  243. kbd_setkey(&kbd, key, e->response_type == XCB_KEY_PRESS);
  244. }
  245. break;
  246. }
  247. case XCB_EXPOSE: {
  248. draw_pixels();
  249. break;
  250. }
  251. default: {
  252. break;
  253. }
  254. }
  255. free(e);
  256. }
  257. }
  258. int main(int argc, char *argv[])
  259. {
  260. if (argc != 2) {
  261. fprintf(stderr, "Usage: ./ti84 /path/to/rom\n");
  262. return 1;
  263. }
  264. m = emul_init(argv[1], 0);
  265. if (m == NULL) return 1;
  266. m->ramstart = RAMSTART;
  267. t6a04_init(&lcd);
  268. kbd_init(&kbd);
  269. lcd_changed = false;
  270. on_was_pressed = false;
  271. m->iord[KBD_PORT] = iord_kbd;
  272. m->iord[INTERRUPT_PORT] = iord_interrupt;
  273. m->iord[LCD_CMD_PORT] = iord_lcd_cmd;
  274. m->iord[LCD_DATA_PORT] = iord_lcd_data;
  275. m->iowr[KBD_PORT] = iowr_kbd;
  276. m->iowr[INTERRUPT_PORT] = iowr_interrupt;
  277. m->iowr[LCD_CMD_PORT] = iowr_lcd_cmd;
  278. m->iowr[LCD_DATA_PORT] = iowr_lcd_data;
  279. conn = xcb_connect(NULL, NULL);
  280. screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
  281. create_window();
  282. draw_pixels();
  283. event_loop();
  284. emul_printdebug();
  285. return 0;
  286. }