Revision | 9881e238851f40236430a3d767d6c27519dd2680 (tree) |
---|---|
Time | 2019-05-20 16:15:36 |
Author | inglorion <homemicro@ingl...> |
Commiter | inglorion |
partitioned emulator into hm1000.c, main.c, and xcb.c
@@ -1,5 +1,5 @@ | ||
1 | 1 | TARGETS = Makefile hm1000 |
2 | -OBJECTS = hm1000.o | |
2 | +OBJECTS = main.o xcb.o | |
3 | 3 | |
4 | 4 | CFLAGS = @CFLAGS@ @XCB_CFLAGS@ |
5 | 5 | LIBS = @LIBS@ @XCB_LIBS@ |
@@ -15,7 +15,10 @@ | ||
15 | 15 | hm1000 : $(OBJECTS) |
16 | 16 | $(CC) $(CFLAGS) -o hm1000 $(OBJECTS) $(LIBS) |
17 | 17 | |
18 | -hm1000.o : hm1000.c ops.inc | |
19 | - $(CC) $(CFLAGS) -c hm1000.c | |
18 | +main.o : main.c hm1000.c hm1000.h ops.inc xcb.h | |
19 | + $(CC) $(CFLAGS) -c main.c | |
20 | + | |
21 | +xcb.o : xcb.c xcb.h | |
22 | + $(CC) $(CFLAGS) -c xcb.c | |
20 | 23 | |
21 | 24 | .PHONY : all clean distclean |
@@ -1,10 +1,12 @@ | ||
1 | +#include "hm1000.h" | |
2 | +#include "xcb.h" | |
3 | + | |
1 | 4 | #include <stdbool.h> |
2 | 5 | #include <stdint.h> |
3 | 6 | #include <stdio.h> |
4 | 7 | #include <stdlib.h> |
5 | 8 | #include <string.h> |
6 | 9 | #include <time.h> |
7 | -#include <xcb/xcb.h> | |
8 | 10 | |
9 | 11 | #include <errno.h> |
10 | 12 |
@@ -47,70 +49,6 @@ | ||
47 | 49 | #define FATALF(FMT, ...) { fprintf(stderr, FMT "\n", __VA_ARGS__); exit(1); } |
48 | 50 | #define FATAL(MSG) FATALF("%s", MSG) |
49 | 51 | |
50 | -#define POS(ROW, COL) (ROW | ((COL) << 3)) | |
51 | -#define KEY_BS POS(4, 1) | |
52 | -#define KEY_CTL POS(2, 0) | |
53 | -#define KEY_TAB POS(1, 1) | |
54 | -#define KEY_RET POS(6, 1) | |
55 | -#define KEY_ESC POS(0, 0) | |
56 | -#define KEY_SH POS(2, 7) | |
57 | -#define KEY_SPC POS(3, 7) | |
58 | -#define KEY_LEFT POS(7, 3) | |
59 | -#define KEY_UP POS(7, 4) | |
60 | -#define KEY_DOWN POS(7, 5) | |
61 | -#define KEY_RIGHT POS(7, 6) | |
62 | -#define KEY_0 POS(4, 3) | |
63 | -#define KEY_1 POS(0, 1) | |
64 | -#define KEY_2 POS(0, 2) | |
65 | -#define KEY_3 POS(0, 3) | |
66 | -#define KEY_4 POS(0, 4) | |
67 | -#define KEY_5 POS(0, 5) | |
68 | -#define KEY_6 POS(0, 6) | |
69 | -#define KEY_7 POS(4, 6) | |
70 | -#define KEY_8 POS(4, 5) | |
71 | -#define KEY_9 POS(4, 4) | |
72 | -#define KEY_BACKTICK POS(1, 0) | |
73 | -#define KEY_MINUS POS(4, 2) | |
74 | -#define KEY_EQUALS POS(4, 0) | |
75 | -#define KEY_LBRACKET POS(5, 2) | |
76 | -#define KEY_RBRACKET POS(5, 0) | |
77 | -#define KEY_BACKSLASH POS(5, 1) | |
78 | -#define KEY_SEMICOLON POS(7, 2) | |
79 | -#define KEY_QUOTE POS(6, 0) | |
80 | -#define KEY_COMMA POS(3, 0) | |
81 | -#define KEY_PERIOD POS(7, 0) | |
82 | -#define KEY_SLASH POS(7, 1) | |
83 | -#define KEY_A POS(2, 1) | |
84 | -#define KEY_B POS(3, 5) | |
85 | -#define KEY_C POS(3, 3) | |
86 | -#define KEY_D POS(2, 4) | |
87 | -#define KEY_E POS(1, 3) | |
88 | -#define KEY_F POS(2, 5) | |
89 | -#define KEY_G POS(2, 6) | |
90 | -#define KEY_H POS(5, 6) | |
91 | -#define KEY_I POS(6, 4) | |
92 | -#define KEY_J POS(6, 5) | |
93 | -#define KEY_K POS(6, 3) | |
94 | -#define KEY_L POS(6, 2) | |
95 | -#define KEY_M POS(6, 6) | |
96 | -#define KEY_N POS(3, 6) | |
97 | -#define KEY_O POS(5, 4) | |
98 | -#define KEY_P POS(5, 3) | |
99 | -#define KEY_Q POS(1, 2) | |
100 | -#define KEY_R POS(1, 4) | |
101 | -#define KEY_S POS(2, 3) | |
102 | -#define KEY_T POS(1, 5) | |
103 | -#define KEY_U POS(5, 5) | |
104 | -#define KEY_V POS(3, 4) | |
105 | -#define KEY_W POS(2, 2) | |
106 | -#define KEY_X POS(3, 2) | |
107 | -#define KEY_Y POS(1, 6) | |
108 | -#define KEY_Z POS(3, 1) | |
109 | -/* Maps keysyms to hm1k keyboard codes. | |
110 | - * For example, keysym 13 (return) maps to row 6, column 1, which we | |
111 | - * encode as 6 | (1 << 3). | |
112 | - * Codes for which we don't have a key are marked as 0xff. | |
113 | - */ | |
114 | 52 | /* TODO: Add control, shift, and the arrow keys. */ |
115 | 53 | const uint8_t key_map[] = { |
116 | 54 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
@@ -814,330 +752,3 @@ | ||
814 | 752 | add_ticks(s, op_cycles[op]); |
815 | 753 | ops[op](s, op); |
816 | 754 | } |
817 | - | |
818 | -typedef struct { | |
819 | - xcb_connection_t *xcb; | |
820 | - xcb_screen_t *screen; | |
821 | - xcb_window_t win; | |
822 | - unsigned int scale; | |
823 | - unsigned int xoffset; | |
824 | - unsigned int yoffset; | |
825 | - xcb_gcontext_t gc; | |
826 | - xcb_pixmap_t pixmap; | |
827 | - uint8_t keymap[256]; | |
828 | -} xcb_data; | |
829 | - | |
830 | -static void xcb_generate_pixmap(xcb_data *gui) { | |
831 | - uint32_t values[1]; | |
832 | - unsigned int i, j, n; | |
833 | - xcb_rectangle_t rects[8]; | |
834 | - | |
835 | - xcb_create_pixmap(gui->xcb, | |
836 | - gui->screen->root_depth, | |
837 | - gui->pixmap, | |
838 | - gui->screen->root, | |
839 | - gui->scale * 8, | |
840 | - gui->scale * 256); | |
841 | - | |
842 | - values[0] = gui->screen->black_pixel; | |
843 | - xcb_change_gc(gui->xcb, gui->gc, XCB_GC_FOREGROUND, values); | |
844 | - rects[0].x = 0; | |
845 | - rects[0].y = 0; | |
846 | - rects[0].width = gui->scale * 8; | |
847 | - rects[0].height = gui->scale * 256; | |
848 | - xcb_poly_fill_rectangle(gui->xcb, gui->pixmap, gui->gc, 1, rects); | |
849 | - | |
850 | - values[0] = gui->screen->white_pixel; | |
851 | - xcb_change_gc(gui->xcb, gui->gc, XCB_GC_FOREGROUND, values); | |
852 | - for (i = 0; i < 256; i++) { | |
853 | - n = 0; | |
854 | - for (j = 0; j < 8; j++) { | |
855 | - if (i & (0x80 >> j)) { | |
856 | - rects[n].x = j * gui->scale; | |
857 | - rects[n].y = i * gui->scale; | |
858 | - rects[n].width = gui->scale; | |
859 | - rects[n].height = gui->scale; | |
860 | - ++n; | |
861 | - } | |
862 | - } | |
863 | - if (n > 0) { | |
864 | - xcb_poly_fill_rectangle(gui->xcb, gui->pixmap, gui->gc, n, rects); | |
865 | - } | |
866 | - } | |
867 | -} | |
868 | - | |
869 | -static void get_window_size(xcb_data *gui, | |
870 | - unsigned int *width, | |
871 | - unsigned int *height) { | |
872 | - xcb_get_geometry_reply_t *geometry; | |
873 | - xcb_get_geometry_cookie_t cookie = xcb_get_geometry(gui->xcb, gui->win); | |
874 | - geometry = xcb_get_geometry_reply(gui->xcb, cookie, NULL); | |
875 | - *width = geometry->width; | |
876 | - *height = geometry->height; | |
877 | - free(geometry); | |
878 | -} | |
879 | - | |
880 | -static void resize(xcb_data *gui, | |
881 | - unsigned int width, | |
882 | - unsigned int height) { | |
883 | - unsigned int scale = height / 220; | |
884 | - gui->scale = width / 340; | |
885 | - if (scale < gui->scale) gui->scale = scale; | |
886 | - gui->xoffset = (width - scale * 320) / 2; | |
887 | - gui->yoffset = (height - scale * 200) / 2; | |
888 | - xcb_generate_pixmap(gui); | |
889 | -} | |
890 | - | |
891 | -static int init_xcb(xcb_data *gui) { | |
892 | - xcb_screen_iterator_t xcb_screen_iterator; | |
893 | - const xcb_setup_t *xcb_setup; | |
894 | - xcb_keysym_t *keysyms; | |
895 | - int i, idx; | |
896 | - uint32_t values[2]; | |
897 | - unsigned int width, height; | |
898 | - xcb_get_keyboard_mapping_cookie_t gkm_cookie; | |
899 | - xcb_get_keyboard_mapping_reply_t *keyboard_mapping; | |
900 | - | |
901 | - gui->pixmap = 0; | |
902 | - gui->xcb = xcb_connect(NULL, NULL); | |
903 | - if (xcb_connection_has_error(gui->xcb)) goto error; | |
904 | - | |
905 | - xcb_setup = xcb_get_setup(gui->xcb); | |
906 | - xcb_screen_iterator = xcb_setup_roots_iterator(xcb_setup); | |
907 | - gui->screen = xcb_screen_iterator.data; | |
908 | - | |
909 | - gui->gc = xcb_generate_id(gui->xcb); | |
910 | - values[0] = gui->screen->white_pixel; | |
911 | - xcb_create_gc(gui->xcb, gui->gc, gui->screen->root, XCB_GC_FOREGROUND, values); | |
912 | - | |
913 | - gui->win = xcb_generate_id(gui->xcb); | |
914 | - values[0] = gui->screen->black_pixel; | |
915 | - values[1] = XCB_EVENT_MASK_EXPOSURE | |
916 | - | XCB_EVENT_MASK_KEY_PRESS | |
917 | - | XCB_EVENT_MASK_KEY_RELEASE | |
918 | - | XCB_EVENT_MASK_STRUCTURE_NOTIFY; | |
919 | - xcb_create_window(gui->xcb, | |
920 | - /* depth */ XCB_COPY_FROM_PARENT, | |
921 | - gui->win, | |
922 | - gui->screen->root, | |
923 | - 0, 0, | |
924 | - 1440, 960, | |
925 | - /* border */ 0, | |
926 | - XCB_WINDOW_CLASS_INPUT_OUTPUT, | |
927 | - gui->screen->root_visual, | |
928 | - XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, | |
929 | - values); | |
930 | - xcb_map_window(gui->xcb, gui->win); | |
931 | - gui->pixmap = xcb_generate_id(gui->xcb); | |
932 | - get_window_size(gui, &width, &height); | |
933 | - resize(gui, width, height); | |
934 | - gkm_cookie = xcb_get_keyboard_mapping( | |
935 | - gui->xcb, | |
936 | - xcb_setup->min_keycode, | |
937 | - xcb_setup->max_keycode - xcb_setup->min_keycode + 1); | |
938 | - xcb_flush(gui->xcb); | |
939 | - if (xcb_connection_has_error(gui->xcb)) goto error; | |
940 | - | |
941 | - memset(gui->keymap, 0xff, 256); | |
942 | - keyboard_mapping = xcb_get_keyboard_mapping_reply(gui->xcb, gkm_cookie, NULL); | |
943 | - keysyms = xcb_get_keyboard_mapping_keysyms(keyboard_mapping); | |
944 | - for (i = 0, idx = xcb_setup->min_keycode; | |
945 | - i < xcb_get_keyboard_mapping_keysyms_length(keyboard_mapping); | |
946 | - i += keyboard_mapping->keysyms_per_keycode, idx++) { | |
947 | - if (keysyms[i] < 128) { | |
948 | - gui->keymap[idx] = key_map[keysyms[i]]; | |
949 | - } else { | |
950 | - switch (keysyms[i]) { | |
951 | - case 0xff08: | |
952 | - gui->keymap[idx] = KEY_BS; | |
953 | - break; | |
954 | - case 0xff09: | |
955 | - gui->keymap[idx] = KEY_TAB; | |
956 | - break; | |
957 | - case 0xff0a: | |
958 | - case 0xff0d: | |
959 | - gui->keymap[idx] = KEY_RET; | |
960 | - break; | |
961 | - case 0xff1b: | |
962 | - gui->keymap[idx] = KEY_ESC; | |
963 | - break; | |
964 | - case 0xff51: | |
965 | - gui->keymap[idx] = KEY_LEFT; | |
966 | - break; | |
967 | - case 0xff52: | |
968 | - gui->keymap[idx] = KEY_UP; | |
969 | - break; | |
970 | - case 0xff53: | |
971 | - gui->keymap[idx] = KEY_RIGHT; | |
972 | - break; | |
973 | - case 0xff54: | |
974 | - gui->keymap[idx] = KEY_DOWN; | |
975 | - break; | |
976 | - case 0xffe1: | |
977 | - case 0xffe2: | |
978 | - gui->keymap[idx] = KEY_SH; | |
979 | - break; | |
980 | - case 0xffe3: | |
981 | - case 0xffe4: | |
982 | - gui->keymap[idx] = KEY_CTL; | |
983 | - break; | |
984 | - } | |
985 | - } | |
986 | - } | |
987 | - /* for (int i = 0; i < 256; i++) { */ | |
988 | - /* printf("keymap[%d]: %u\n", i, gui->keymap[i]); */ | |
989 | - /* } */ | |
990 | - free(keyboard_mapping); | |
991 | - | |
992 | - return 0; | |
993 | - | |
994 | - error: | |
995 | - if (gui->pixmap) { | |
996 | - xcb_free_pixmap(gui->xcb, gui->pixmap); | |
997 | - gui->pixmap = 0; | |
998 | - } | |
999 | - xcb_disconnect(gui->xcb); | |
1000 | - return -1; | |
1001 | -} | |
1002 | - | |
1003 | -static void update_display(xcb_data *gui, const uint8_t *ram) { | |
1004 | - unsigned int x, y; | |
1005 | - for (y = 0; y < 200; y++) { | |
1006 | - for (x = 0; x < 40; x++) { | |
1007 | - xcb_copy_area(gui->xcb, gui->pixmap, gui->win, gui->gc, | |
1008 | - 0, | |
1009 | - ram[0x2000 + (320 * (y >> 3)) + (x << 3) + (y & 7)] * gui->scale, | |
1010 | - (x << 3) * gui->scale + gui->xoffset, | |
1011 | - y * gui->scale + gui->yoffset, | |
1012 | - 8 * gui->scale, gui->scale); | |
1013 | - } | |
1014 | - } | |
1015 | - xcb_flush(gui->xcb); | |
1016 | -} | |
1017 | - | |
1018 | -int main(int argc, char *argv[]) { | |
1019 | - hm1k_state state; | |
1020 | - uint8_t ram[RAM_SIZE]; | |
1021 | - uint8_t rom[ROM_SIZE]; | |
1022 | - bool redraw; | |
1023 | - xcb_generic_event_t *event; | |
1024 | - xcb_data gui; | |
1025 | - | |
1026 | - if (init_xcb(&gui)) { | |
1027 | - FATAL("error initializing xcb"); | |
1028 | - return -1; | |
1029 | - } | |
1030 | - | |
1031 | - randomize(ram, sizeof(ram)); | |
1032 | - | |
1033 | - { | |
1034 | - FILE *f = fopen("rom.bin", "rb"); | |
1035 | - if (!f) { | |
1036 | - perror("rom.bin"); | |
1037 | - fprintf(stderr, | |
1038 | - "Could not open rom.bin." | |
1039 | - " The emulator will not work without it.\n"); | |
1040 | - return 1; | |
1041 | - } | |
1042 | - fread(rom, 1, ROM_SIZE, f); | |
1043 | - fclose(f); | |
1044 | - } | |
1045 | - init_6502(&state, ram); | |
1046 | - state.rom = rom; | |
1047 | - state.io_read[SERIR - IO_BASE] = read_serir; | |
1048 | - state.io_write[SERCR - IO_BASE] = write_sercr; | |
1049 | - state.kbdrow = 0; | |
1050 | - memset(state.keyboard, 0xff, sizeof(state.keyboard)); | |
1051 | - state.io_read[KBDCOL - IO_BASE] = read_kbdcol; | |
1052 | - state.io_write[KBDROW - IO_BASE] = write_kbdrow; | |
1053 | - do { | |
1054 | - FILE *f = fopen("cartridge.bin", "rb"); | |
1055 | - if (!f) break; | |
1056 | - fseek(f, 0, SEEK_END); | |
1057 | - state.cartridge_size = ftell(f); | |
1058 | - rewind(f); | |
1059 | - state.cartridge = malloc(state.cartridge_size); | |
1060 | - if (!state.cartridge) { | |
1061 | - fprintf(stderr, | |
1062 | - "failed to allocate memory (%lu bytes) for cartridge\n", | |
1063 | - (unsigned long) state.cartridge_size); | |
1064 | - perror("malloc"); | |
1065 | - break; | |
1066 | - } | |
1067 | - memset(state.cartridge, 0xff, state.cartridge_size); | |
1068 | - fread(state.cartridge, 1, state.cartridge_size, f); | |
1069 | - fclose(f); | |
1070 | - } while (0); | |
1071 | - reset(&state); | |
1072 | - | |
1073 | - redraw = true; | |
1074 | - for (;;) { | |
1075 | - step_6502(&state); | |
1076 | - if (state.last_sync.tv_sec >= state.next_redraw.tv_sec && | |
1077 | - (state.last_sync.tv_sec > state.next_redraw.tv_sec || | |
1078 | - state.last_sync.tv_nsec > state.next_redraw.tv_nsec)) { | |
1079 | - redraw = true; | |
1080 | - state.next_redraw.tv_nsec += REDRAW_NS; | |
1081 | - if (state.next_redraw.tv_nsec >= 1000000000) { | |
1082 | - ++state.next_redraw.tv_sec; | |
1083 | - state.next_redraw.tv_nsec -= 1000000000; | |
1084 | - } | |
1085 | - } | |
1086 | - | |
1087 | - event = xcb_poll_for_event(gui.xcb); | |
1088 | - if (!event) { | |
1089 | - if (redraw) { | |
1090 | - update_display(&gui, ram); | |
1091 | - redraw = false; | |
1092 | - } | |
1093 | - continue; | |
1094 | - } | |
1095 | - switch (event->response_type & ~0x80) { | |
1096 | - case XCB_EXPOSE: | |
1097 | - redraw = true; | |
1098 | - break; | |
1099 | - case XCB_KEY_PRESS: | |
1100 | - { | |
1101 | - xcb_key_press_event_t *kpe = | |
1102 | - (xcb_key_press_event_t*) event; | |
1103 | - unsigned pos = gui.keymap[kpe->detail]; | |
1104 | - // printf("key press: %u (%u)\n", kpe->detail, pos); | |
1105 | - if (pos != 0xff) { | |
1106 | - if (kpe->state & XCB_MOD_MASK_1) { | |
1107 | - // Special handling when mod1 is active. | |
1108 | - if (pos == KEY_Q) goto leave_event_loop; | |
1109 | - } else { | |
1110 | - state.keyboard[pos & 7] &= ~((1 << (pos >> 3))); | |
1111 | - } | |
1112 | - } | |
1113 | - } | |
1114 | - break; | |
1115 | - case XCB_KEY_RELEASE: | |
1116 | - { | |
1117 | - xcb_key_press_event_t *kpe = | |
1118 | - (xcb_key_press_event_t*) event; | |
1119 | - unsigned pos = gui.keymap[kpe->detail]; | |
1120 | - if (pos != 0xff) state.keyboard[pos & 7] |= (1 << (pos >> 3)); | |
1121 | - } | |
1122 | - break; | |
1123 | - case XCB_CONFIGURE_NOTIFY: | |
1124 | - { | |
1125 | - xcb_configure_notify_event_t *cne = | |
1126 | - (xcb_configure_notify_event_t*) event; | |
1127 | - resize(&gui, cne->width, cne->height); | |
1128 | - redraw = true; | |
1129 | - } | |
1130 | - break; | |
1131 | - case XCB_CLIENT_MESSAGE: | |
1132 | - goto leave_event_loop; | |
1133 | - } | |
1134 | - free(event); | |
1135 | - continue; | |
1136 | - leave_event_loop: | |
1137 | - free(event); | |
1138 | - break; | |
1139 | - } | |
1140 | - | |
1141 | - xcb_disconnect(gui.xcb); | |
1142 | - return 0; | |
1143 | -} |
@@ -0,0 +1,73 @@ | ||
1 | +#ifndef HOMEMICRO_HM1000 | |
2 | +#define HOMEMICRO_HM1000 | |
3 | + | |
4 | +#include <stdint.h> | |
5 | + | |
6 | +#define POS(ROW, COL) (ROW | ((COL) << 3)) | |
7 | +#define KEY_BS POS(4, 1) | |
8 | +#define KEY_CTL POS(2, 0) | |
9 | +#define KEY_TAB POS(1, 1) | |
10 | +#define KEY_RET POS(6, 1) | |
11 | +#define KEY_ESC POS(0, 0) | |
12 | +#define KEY_SH POS(2, 7) | |
13 | +#define KEY_SPC POS(3, 7) | |
14 | +#define KEY_LEFT POS(7, 3) | |
15 | +#define KEY_UP POS(7, 4) | |
16 | +#define KEY_DOWN POS(7, 5) | |
17 | +#define KEY_RIGHT POS(7, 6) | |
18 | +#define KEY_0 POS(4, 3) | |
19 | +#define KEY_1 POS(0, 1) | |
20 | +#define KEY_2 POS(0, 2) | |
21 | +#define KEY_3 POS(0, 3) | |
22 | +#define KEY_4 POS(0, 4) | |
23 | +#define KEY_5 POS(0, 5) | |
24 | +#define KEY_6 POS(0, 6) | |
25 | +#define KEY_7 POS(4, 6) | |
26 | +#define KEY_8 POS(4, 5) | |
27 | +#define KEY_9 POS(4, 4) | |
28 | +#define KEY_BACKTICK POS(1, 0) | |
29 | +#define KEY_MINUS POS(4, 2) | |
30 | +#define KEY_EQUALS POS(4, 0) | |
31 | +#define KEY_LBRACKET POS(5, 2) | |
32 | +#define KEY_RBRACKET POS(5, 0) | |
33 | +#define KEY_BACKSLASH POS(5, 1) | |
34 | +#define KEY_SEMICOLON POS(7, 2) | |
35 | +#define KEY_QUOTE POS(6, 0) | |
36 | +#define KEY_COMMA POS(3, 0) | |
37 | +#define KEY_PERIOD POS(7, 0) | |
38 | +#define KEY_SLASH POS(7, 1) | |
39 | +#define KEY_A POS(2, 1) | |
40 | +#define KEY_B POS(3, 5) | |
41 | +#define KEY_C POS(3, 3) | |
42 | +#define KEY_D POS(2, 4) | |
43 | +#define KEY_E POS(1, 3) | |
44 | +#define KEY_F POS(2, 5) | |
45 | +#define KEY_G POS(2, 6) | |
46 | +#define KEY_H POS(5, 6) | |
47 | +#define KEY_I POS(6, 4) | |
48 | +#define KEY_J POS(6, 5) | |
49 | +#define KEY_K POS(6, 3) | |
50 | +#define KEY_L POS(6, 2) | |
51 | +#define KEY_M POS(6, 6) | |
52 | +#define KEY_N POS(3, 6) | |
53 | +#define KEY_O POS(5, 4) | |
54 | +#define KEY_P POS(5, 3) | |
55 | +#define KEY_Q POS(1, 2) | |
56 | +#define KEY_R POS(1, 4) | |
57 | +#define KEY_S POS(2, 3) | |
58 | +#define KEY_T POS(1, 5) | |
59 | +#define KEY_U POS(5, 5) | |
60 | +#define KEY_V POS(3, 4) | |
61 | +#define KEY_W POS(2, 2) | |
62 | +#define KEY_X POS(3, 2) | |
63 | +#define KEY_Y POS(1, 6) | |
64 | +#define KEY_Z POS(3, 1) | |
65 | +/* Maps keysyms to hm1k keyboard codes. | |
66 | + * For example, keysym 13 (return) maps to row 6, column 1, which we | |
67 | + * encode as 6 | (1 << 3). | |
68 | + * Codes for which we don't have a key are marked as 0xff. | |
69 | + */ | |
70 | +const uint8_t key_map[128]; | |
71 | + | |
72 | +#endif /* ndef HOMEMICRO_HM1000 */ | |
73 | + |
@@ -0,0 +1,131 @@ | ||
1 | +#include "hm1000.h" | |
2 | +#include "xcb.h" | |
3 | + | |
4 | +#include "hm1000.c" | |
5 | + | |
6 | +int main(int argc, char *argv[]) { | |
7 | + hm1k_state state; | |
8 | + uint8_t ram[RAM_SIZE]; | |
9 | + uint8_t rom[ROM_SIZE]; | |
10 | + bool redraw; | |
11 | + xcb_generic_event_t *event; | |
12 | + xcb_data gui; | |
13 | + | |
14 | + if (init_xcb(&gui)) { | |
15 | + FATAL("error initializing xcb"); | |
16 | + return -1; | |
17 | + } | |
18 | + | |
19 | + randomize(ram, sizeof(ram)); | |
20 | + | |
21 | + { | |
22 | + FILE *f = fopen("rom.bin", "rb"); | |
23 | + if (!f) { | |
24 | + perror("rom.bin"); | |
25 | + fprintf(stderr, | |
26 | + "Could not open rom.bin." | |
27 | + " The emulator will not work without it.\n"); | |
28 | + return 1; | |
29 | + } | |
30 | + fread(rom, 1, ROM_SIZE, f); | |
31 | + fclose(f); | |
32 | + } | |
33 | + init_6502(&state, ram); | |
34 | + state.rom = rom; | |
35 | + state.io_read[SERIR - IO_BASE] = read_serir; | |
36 | + state.io_write[SERCR - IO_BASE] = write_sercr; | |
37 | + state.kbdrow = 0; | |
38 | + memset(state.keyboard, 0xff, sizeof(state.keyboard)); | |
39 | + state.io_read[KBDCOL - IO_BASE] = read_kbdcol; | |
40 | + state.io_write[KBDROW - IO_BASE] = write_kbdrow; | |
41 | + do { | |
42 | + FILE *f = fopen("cartridge.bin", "rb"); | |
43 | + if (!f) break; | |
44 | + fseek(f, 0, SEEK_END); | |
45 | + state.cartridge_size = ftell(f); | |
46 | + rewind(f); | |
47 | + state.cartridge = malloc(state.cartridge_size); | |
48 | + if (!state.cartridge) { | |
49 | + fprintf(stderr, | |
50 | + "failed to allocate memory (%lu bytes) for cartridge\n", | |
51 | + (unsigned long) state.cartridge_size); | |
52 | + perror("malloc"); | |
53 | + break; | |
54 | + } | |
55 | + memset(state.cartridge, 0xff, state.cartridge_size); | |
56 | + fread(state.cartridge, 1, state.cartridge_size, f); | |
57 | + fclose(f); | |
58 | + } while (0); | |
59 | + reset(&state); | |
60 | + | |
61 | + redraw = true; | |
62 | + for (;;) { | |
63 | + step_6502(&state); | |
64 | + if (state.last_sync.tv_sec >= state.next_redraw.tv_sec && | |
65 | + (state.last_sync.tv_sec > state.next_redraw.tv_sec || | |
66 | + state.last_sync.tv_nsec > state.next_redraw.tv_nsec)) { | |
67 | + redraw = true; | |
68 | + state.next_redraw.tv_nsec += REDRAW_NS; | |
69 | + if (state.next_redraw.tv_nsec >= 1000000000) { | |
70 | + ++state.next_redraw.tv_sec; | |
71 | + state.next_redraw.tv_nsec -= 1000000000; | |
72 | + } | |
73 | + } | |
74 | + | |
75 | + event = xcb_poll_for_event(gui.xcb); | |
76 | + if (!event) { | |
77 | + if (redraw) { | |
78 | + update_display(&gui, ram); | |
79 | + redraw = false; | |
80 | + } | |
81 | + continue; | |
82 | + } | |
83 | + switch (event->response_type & ~0x80) { | |
84 | + case XCB_EXPOSE: | |
85 | + redraw = true; | |
86 | + break; | |
87 | + case XCB_KEY_PRESS: | |
88 | + { | |
89 | + xcb_key_press_event_t *kpe = | |
90 | + (xcb_key_press_event_t*) event; | |
91 | + unsigned pos = gui.keymap[kpe->detail]; | |
92 | + // printf("key press: %u (%u)\n", kpe->detail, pos); | |
93 | + if (pos != 0xff) { | |
94 | + if (kpe->state & XCB_MOD_MASK_1) { | |
95 | + // Special handling when mod1 is active. | |
96 | + if (pos == KEY_Q) goto leave_event_loop; | |
97 | + } else { | |
98 | + state.keyboard[pos & 7] &= ~((1 << (pos >> 3))); | |
99 | + } | |
100 | + } | |
101 | + } | |
102 | + break; | |
103 | + case XCB_KEY_RELEASE: | |
104 | + { | |
105 | + xcb_key_press_event_t *kpe = | |
106 | + (xcb_key_press_event_t*) event; | |
107 | + unsigned pos = gui.keymap[kpe->detail]; | |
108 | + if (pos != 0xff) state.keyboard[pos & 7] |= (1 << (pos >> 3)); | |
109 | + } | |
110 | + break; | |
111 | + case XCB_CONFIGURE_NOTIFY: | |
112 | + { | |
113 | + xcb_configure_notify_event_t *cne = | |
114 | + (xcb_configure_notify_event_t*) event; | |
115 | + resize(&gui, cne->width, cne->height); | |
116 | + redraw = true; | |
117 | + } | |
118 | + break; | |
119 | + case XCB_CLIENT_MESSAGE: | |
120 | + goto leave_event_loop; | |
121 | + } | |
122 | + free(event); | |
123 | + continue; | |
124 | + leave_event_loop: | |
125 | + free(event); | |
126 | + break; | |
127 | + } | |
128 | + | |
129 | + xcb_disconnect(gui.xcb); | |
130 | + return 0; | |
131 | +} |
@@ -0,0 +1,193 @@ | ||
1 | +#include "xcb.h" | |
2 | +#include "hm1000.h" | |
3 | + | |
4 | +#include <stdlib.h> | |
5 | +#include <string.h> | |
6 | + | |
7 | +void get_window_size(xcb_data *gui, | |
8 | + unsigned int *width, | |
9 | + unsigned int *height) { | |
10 | + xcb_get_geometry_reply_t *geometry; | |
11 | + xcb_get_geometry_cookie_t cookie = xcb_get_geometry(gui->xcb, gui->win); | |
12 | + geometry = xcb_get_geometry_reply(gui->xcb, cookie, NULL); | |
13 | + *width = geometry->width; | |
14 | + *height = geometry->height; | |
15 | + free(geometry); | |
16 | +} | |
17 | + | |
18 | +int init_xcb(xcb_data *gui) { | |
19 | + xcb_screen_iterator_t xcb_screen_iterator; | |
20 | + const xcb_setup_t *xcb_setup; | |
21 | + xcb_keysym_t *keysyms; | |
22 | + int i, idx; | |
23 | + uint32_t values[2]; | |
24 | + unsigned int width, height; | |
25 | + xcb_get_keyboard_mapping_cookie_t gkm_cookie; | |
26 | + xcb_get_keyboard_mapping_reply_t *keyboard_mapping; | |
27 | + | |
28 | + gui->pixmap = 0; | |
29 | + gui->xcb = xcb_connect(NULL, NULL); | |
30 | + if (xcb_connection_has_error(gui->xcb)) goto error; | |
31 | + | |
32 | + xcb_setup = xcb_get_setup(gui->xcb); | |
33 | + xcb_screen_iterator = xcb_setup_roots_iterator(xcb_setup); | |
34 | + gui->screen = xcb_screen_iterator.data; | |
35 | + | |
36 | + gui->gc = xcb_generate_id(gui->xcb); | |
37 | + values[0] = gui->screen->white_pixel; | |
38 | + xcb_create_gc(gui->xcb, gui->gc, gui->screen->root, XCB_GC_FOREGROUND, values); | |
39 | + | |
40 | + gui->win = xcb_generate_id(gui->xcb); | |
41 | + values[0] = gui->screen->black_pixel; | |
42 | + values[1] = XCB_EVENT_MASK_EXPOSURE | |
43 | + | XCB_EVENT_MASK_KEY_PRESS | |
44 | + | XCB_EVENT_MASK_KEY_RELEASE | |
45 | + | XCB_EVENT_MASK_STRUCTURE_NOTIFY; | |
46 | + xcb_create_window(gui->xcb, | |
47 | + /* depth */ XCB_COPY_FROM_PARENT, | |
48 | + gui->win, | |
49 | + gui->screen->root, | |
50 | + 0, 0, | |
51 | + 1440, 960, | |
52 | + /* border */ 0, | |
53 | + XCB_WINDOW_CLASS_INPUT_OUTPUT, | |
54 | + gui->screen->root_visual, | |
55 | + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, | |
56 | + values); | |
57 | + xcb_map_window(gui->xcb, gui->win); | |
58 | + gui->pixmap = xcb_generate_id(gui->xcb); | |
59 | + get_window_size(gui, &width, &height); | |
60 | + resize(gui, width, height); | |
61 | + gkm_cookie = xcb_get_keyboard_mapping( | |
62 | + gui->xcb, | |
63 | + xcb_setup->min_keycode, | |
64 | + xcb_setup->max_keycode - xcb_setup->min_keycode + 1); | |
65 | + xcb_flush(gui->xcb); | |
66 | + if (xcb_connection_has_error(gui->xcb)) goto error; | |
67 | + | |
68 | + memset(gui->keymap, 0xff, 256); | |
69 | + keyboard_mapping = xcb_get_keyboard_mapping_reply(gui->xcb, gkm_cookie, NULL); | |
70 | + keysyms = xcb_get_keyboard_mapping_keysyms(keyboard_mapping); | |
71 | + for (i = 0, idx = xcb_setup->min_keycode; | |
72 | + i < xcb_get_keyboard_mapping_keysyms_length(keyboard_mapping); | |
73 | + i += keyboard_mapping->keysyms_per_keycode, idx++) { | |
74 | + if (keysyms[i] < 128) { | |
75 | + gui->keymap[idx] = key_map[keysyms[i]]; | |
76 | + } else { | |
77 | + switch (keysyms[i]) { | |
78 | + case 0xff08: | |
79 | + gui->keymap[idx] = KEY_BS; | |
80 | + break; | |
81 | + case 0xff09: | |
82 | + gui->keymap[idx] = KEY_TAB; | |
83 | + break; | |
84 | + case 0xff0a: | |
85 | + case 0xff0d: | |
86 | + gui->keymap[idx] = KEY_RET; | |
87 | + break; | |
88 | + case 0xff1b: | |
89 | + gui->keymap[idx] = KEY_ESC; | |
90 | + break; | |
91 | + case 0xff51: | |
92 | + gui->keymap[idx] = KEY_LEFT; | |
93 | + break; | |
94 | + case 0xff52: | |
95 | + gui->keymap[idx] = KEY_UP; | |
96 | + break; | |
97 | + case 0xff53: | |
98 | + gui->keymap[idx] = KEY_RIGHT; | |
99 | + break; | |
100 | + case 0xff54: | |
101 | + gui->keymap[idx] = KEY_DOWN; | |
102 | + break; | |
103 | + case 0xffe1: | |
104 | + case 0xffe2: | |
105 | + gui->keymap[idx] = KEY_SH; | |
106 | + break; | |
107 | + case 0xffe3: | |
108 | + case 0xffe4: | |
109 | + gui->keymap[idx] = KEY_CTL; | |
110 | + break; | |
111 | + } | |
112 | + } | |
113 | + } | |
114 | + /* for (int i = 0; i < 256; i++) { */ | |
115 | + /* printf("keymap[%d]: %u\n", i, gui->keymap[i]); */ | |
116 | + /* } */ | |
117 | + free(keyboard_mapping); | |
118 | + | |
119 | + return 0; | |
120 | + | |
121 | + error: | |
122 | + if (gui->pixmap) { | |
123 | + xcb_free_pixmap(gui->xcb, gui->pixmap); | |
124 | + gui->pixmap = 0; | |
125 | + } | |
126 | + xcb_disconnect(gui->xcb); | |
127 | + return -1; | |
128 | +} | |
129 | + | |
130 | +void resize(xcb_data *gui, | |
131 | + unsigned int width, | |
132 | + unsigned int height) { | |
133 | + unsigned int scale = height / 220; | |
134 | + gui->scale = width / 340; | |
135 | + if (scale < gui->scale) gui->scale = scale; | |
136 | + gui->xoffset = (width - scale * 320) / 2; | |
137 | + gui->yoffset = (height - scale * 200) / 2; | |
138 | + xcb_generate_pixmap(gui); | |
139 | +} | |
140 | + | |
141 | +void update_display(xcb_data *gui, const uint8_t *ram) { | |
142 | + unsigned int x, y; | |
143 | + for (y = 0; y < 200; y++) { | |
144 | + for (x = 0; x < 40; x++) { | |
145 | + xcb_copy_area(gui->xcb, gui->pixmap, gui->win, gui->gc, | |
146 | + 0, | |
147 | + ram[0x2000 + (320 * (y >> 3)) + (x << 3) + (y & 7)] * gui->scale, | |
148 | + (x << 3) * gui->scale + gui->xoffset, | |
149 | + y * gui->scale + gui->yoffset, | |
150 | + 8 * gui->scale, gui->scale); | |
151 | + } | |
152 | + } | |
153 | + xcb_flush(gui->xcb); | |
154 | +} | |
155 | + | |
156 | +void xcb_generate_pixmap(xcb_data *gui) { | |
157 | + uint32_t values[1]; | |
158 | + unsigned int i, j, n; | |
159 | + xcb_rectangle_t rects[8]; | |
160 | + | |
161 | + xcb_create_pixmap(gui->xcb, | |
162 | + gui->screen->root_depth, | |
163 | + gui->pixmap, | |
164 | + gui->screen->root, | |
165 | + gui->scale * 8, | |
166 | + gui->scale * 256); | |
167 | + | |
168 | + values[0] = gui->screen->black_pixel; | |
169 | + xcb_change_gc(gui->xcb, gui->gc, XCB_GC_FOREGROUND, values); | |
170 | + rects[0].x = 0; | |
171 | + rects[0].y = 0; | |
172 | + rects[0].width = gui->scale * 8; | |
173 | + rects[0].height = gui->scale * 256; | |
174 | + xcb_poly_fill_rectangle(gui->xcb, gui->pixmap, gui->gc, 1, rects); | |
175 | + | |
176 | + values[0] = gui->screen->white_pixel; | |
177 | + xcb_change_gc(gui->xcb, gui->gc, XCB_GC_FOREGROUND, values); | |
178 | + for (i = 0; i < 256; i++) { | |
179 | + n = 0; | |
180 | + for (j = 0; j < 8; j++) { | |
181 | + if (i & (0x80 >> j)) { | |
182 | + rects[n].x = j * gui->scale; | |
183 | + rects[n].y = i * gui->scale; | |
184 | + rects[n].width = gui->scale; | |
185 | + rects[n].height = gui->scale; | |
186 | + ++n; | |
187 | + } | |
188 | + } | |
189 | + if (n > 0) { | |
190 | + xcb_poly_fill_rectangle(gui->xcb, gui->pixmap, gui->gc, n, rects); | |
191 | + } | |
192 | + } | |
193 | +} |
@@ -0,0 +1,28 @@ | ||
1 | +#ifndef HOMEMICRO_EMULATOR_XCB | |
2 | +#define HOMEMICRO_EMULATOR_XCB | |
3 | + | |
4 | +#include <xcb/xcb.h> | |
5 | + | |
6 | +typedef struct { | |
7 | + xcb_connection_t *xcb; | |
8 | + xcb_screen_t *screen; | |
9 | + xcb_window_t win; | |
10 | + unsigned int scale; | |
11 | + unsigned int xoffset; | |
12 | + unsigned int yoffset; | |
13 | + xcb_gcontext_t gc; | |
14 | + xcb_pixmap_t pixmap; | |
15 | + uint8_t keymap[256]; | |
16 | +} xcb_data; | |
17 | + | |
18 | +void get_window_size(xcb_data *gui, | |
19 | + unsigned int *width, | |
20 | + unsigned int *height); | |
21 | +int init_xcb(xcb_data *gui); | |
22 | +void resize(xcb_data *gui, | |
23 | + unsigned int width, | |
24 | + unsigned int height); | |
25 | +void update_display(xcb_data *gui, const uint8_t *ram); | |
26 | +void xcb_generate_pixmap(xcb_data *gui); | |
27 | + | |
28 | +#endif /* ndef HOMEMICRO_EMULATOR_XCB */ |