schip.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <dialogs/dialogs.h>
  6. #include <storage/storage.h>
  7. #define TAG "schip"
  8. #define FONT_SIZE 180
  9. #define MEM_SIZE 0x1000
  10. #define SCREEN_WIDTH 128
  11. #define SCREEN_HEIGHT 64
  12. #define REGISTERS_COUNT 16
  13. #define STACK_SIZE 16
  14. #define KEYPAD_WIDTH 4
  15. #define KEYPAD_HEIGHT 4
  16. #define BUTTONS_COUNT 16
  17. #define ROM_START 0x200
  18. #define FILES_PATH "/ext/schip"
  19. #define FILE_EXTENSION ".ch8"
  20. #define RPL_COUNT 8
  21. const uint8_t font[FONT_SIZE] = {
  22. 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
  23. 0x20, 0x60, 0x20, 0x20, 0x70, // 1
  24. 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
  25. 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
  26. 0x90, 0x90, 0xF0, 0x10, 0x10, // 4
  27. 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
  28. 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
  29. 0xF0, 0x10, 0x20, 0x40, 0x40, // 7
  30. 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
  31. 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
  32. 0xF0, 0x90, 0xF0, 0x90, 0x90, // A
  33. 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
  34. 0xF0, 0x80, 0x80, 0x80, 0xF0, // C
  35. 0xE0, 0x90, 0x90, 0x90, 0xE0, // D
  36. 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
  37. 0xF0, 0x80, 0xF0, 0x80, 0x80, // F
  38. 0x3C, 0x7E, 0xE7, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, // 0h
  39. 0x18, 0x38, 0x58, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, // 1h
  40. 0x3E, 0x7F, 0xC3, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, // 2h
  41. 0x3C, 0x7E, 0xC3, 0x03, 0x0E, 0x0E, 0x03, 0xC3, 0x7E, 0x3C, // 3h
  42. 0x06, 0x0E, 0x1E, 0x36, 0x66, 0xC6, 0xFF, 0xFF, 0x06, 0x06, // 4h
  43. 0xFF, 0xFF, 0xC0, 0xC0, 0xFC, 0xFE, 0x03, 0xC3, 0x7E, 0x3C, // 5h
  44. 0x3E, 0x7C, 0xC0, 0xC0, 0xFC, 0xFE, 0xC3, 0xC3, 0x7E, 0x3C, // 6h
  45. 0xFF, 0xFF, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x60, // 7h
  46. 0x3C, 0x7E, 0xC3, 0xC3, 0x7E, 0x7E, 0xC3, 0xC3, 0x7E, 0x3C, // 8h
  47. 0x3C, 0x7E, 0xC3, 0xC3, 0x7F, 0x3F, 0x03, 0x03, 0x3E, 0x7C // 9h
  48. };
  49. uint8_t memory[MEM_SIZE];
  50. bool screen[SCREEN_WIDTH][SCREEN_HEIGHT];
  51. bool hires = false;
  52. uint8_t registers[REGISTERS_COUNT];
  53. uint16_t I = 0;
  54. uint16_t PC = ROM_START;
  55. uint8_t time_delay = 0;
  56. uint8_t time_sound = 0;
  57. uint16_t stack[STACK_SIZE];
  58. uint8_t stack_pointer;
  59. uint8_t rpl[RPL_COUNT];
  60. bool keyboard_open = false;
  61. uint8_t keyboard_x = 0;
  62. uint8_t keyboard_y = 0;
  63. bool buttons[BUTTONS_COUNT];
  64. uint8_t keypad[BUTTONS_COUNT] = {
  65. 1, 2, 3, 12,
  66. 4, 5, 6, 13,
  67. 7, 8, 9, 14,
  68. 10, 0, 11, 15
  69. };
  70. bool run = true;
  71. FuriString* file_path;
  72. static void app_draw_callback(Canvas* canvas, void* ctx) {
  73. UNUSED(ctx);
  74. canvas_clear(canvas);
  75. canvas_set_font(canvas, FontSecondary);
  76. if(keyboard_open) {
  77. for(uint8_t x = 0; x < KEYPAD_WIDTH; x++)
  78. for(uint8_t y = 0; y < KEYPAD_HEIGHT; y++)
  79. canvas_draw_frame(canvas, 32 + x * 16, y * 16, 16, 16);
  80. canvas_draw_frame(canvas, 33 + keyboard_x * 16, 1 + keyboard_y * 16, 14, 14);
  81. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 0, "1");
  82. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 0, "2");
  83. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 0, "3");
  84. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 0, "C");
  85. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 1, "4");
  86. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 1, "5");
  87. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 1, "6");
  88. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 1, "D");
  89. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 2, "7");
  90. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 2, "8");
  91. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 2, "9");
  92. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 2, "E");
  93. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 3, "A");
  94. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 3, "0");
  95. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 3, "B");
  96. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 3, "F");
  97. } else {
  98. for(uint8_t x = 0; x < SCREEN_WIDTH; x++)
  99. for(uint8_t y = 0; y < SCREEN_HEIGHT; y++)
  100. if(screen[x][y]) canvas_draw_dot(canvas, x, y);
  101. }
  102. }
  103. static void app_input_callback(InputEvent* input_event, void* ctx) {
  104. furi_assert(ctx);
  105. FuriMessageQueue* event_queue = ctx;
  106. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  107. }
  108. static bool nthbit(uint8_t n, uint8_t k) {
  109. return (n & ( 1 << k )) >> k == 1;
  110. }
  111. static bool toggle_pixel(uint8_t x, uint8_t y) {
  112. bool collision = false;
  113. if(hires) {
  114. collision = screen[x][y];
  115. screen[x][y] = !screen[x][y];
  116. } else {
  117. collision = screen[x * 2][y * 2];
  118. screen[x * 2][y * 2] = !screen[x * 2][y * 2];
  119. screen[x * 2 + 1][y * 2] = !screen[x * 2 + 1][y * 2];
  120. screen[x * 2][y * 2 + 1] = !screen[x * 2][y * 2 + 1];
  121. screen[x * 2 + 1][y * 2 + 1] = !screen[x * 2 + 1][y * 2 + 1];
  122. }
  123. return collision;
  124. }
  125. // TODO fix this:
  126. /*
  127. : draw-title
  128. draw-x := 16
  129. draw-y := 16
  130. dir := 32
  131. i := sprite-title
  132. loop
  133. sprite draw-x draw-y 0
  134. i += dir
  135. draw-y += 16
  136. if draw-y > TITLE_HEIGHT then draw-x += 16
  137. if draw-y > TITLE_HEIGHT then draw-y := 16
  138. if draw-x != TITLE_WIDTH then
  139. again
  140. dir := key
  141. ;
  142. */
  143. FuriTimer* timer;
  144. static void tick(void* context) {
  145. UNUSED(context);
  146. if(!run) return;
  147. furi_check(PC < MEM_SIZE - 1);
  148. uint8_t d1 = memory[PC] / 16;
  149. uint8_t d2 = memory[PC] % 16;
  150. uint8_t d3 = memory[PC + 1] / 16;
  151. uint8_t d4 = memory[PC + 1] % 16;
  152. uint16_t d5 = d2 * 256 + d3 * 16 + d4; // I
  153. uint16_t d6 = d1 * 4096 + d2 * 256 + d3 * 16 + d4; // Whole instruction
  154. // FURI_LOG_T(TAG, "Executing instruction %x at address %x", d6, PC);
  155. uint8_t d7 = d3 * 16 + d4; // Byte
  156. bool next = true;
  157. if(d1 == 0x0 && d2 == 0x0 && d3 == 0xc) {
  158. // TODO: scroll down d4 px
  159. } else if(d6 == 0x00e0) {
  160. for(uint8_t x = 0; x < SCREEN_WIDTH; x++)
  161. for(uint8_t y = 0; y < SCREEN_HEIGHT; y++)
  162. screen[x][y] = false;
  163. } else if(d6 == 0x00ee) {
  164. PC = stack[stack_pointer--];
  165. } else if(d6 == 0x00fb) {
  166. // TODO: scroll right 4 px
  167. } else if(d6 == 0x00fc) {
  168. // TODO: scroll left 4 px
  169. } else if(d6 == 0x00fd) {
  170. run = false;
  171. } else if(d6 == 0x00fe) {
  172. hires = false;
  173. } else if(d6 == 0x00ff) {
  174. hires = true;
  175. } else if(d1 == 0x0 || d1 == 0x1) {
  176. PC = d5;
  177. next = false;
  178. } else if(d1 == 0x2) {
  179. stack[++stack_pointer] = PC;
  180. PC = d5;
  181. next = false;
  182. } else if(d1 == 0x3) {
  183. if(registers[d2] == d7) PC += 2;
  184. } else if(d1 == 0x4) {
  185. if(registers[d2] != d7) PC += 2;
  186. } else if(d1 == 0x5) {
  187. if(registers[d2] == registers[d3]) PC += 2;
  188. } else if(d1 == 0x6) {
  189. registers[d2] = d7;
  190. } else if(d1 == 0x7) {
  191. registers[d2] += d7;
  192. } else if(d1 == 0x8 && d4 == 0x0) {
  193. registers[d2] = registers[d3];
  194. } else if(d1 == 0x8 && d4 == 0x1) {
  195. registers[d2] |= registers[d3];
  196. } else if(d1 == 0x8 && d4 == 0x2) {
  197. registers[d2] &= registers[d3];
  198. } else if(d1 == 0x8 && d4 == 0x3) {
  199. registers[d2] ^= registers[d3];
  200. } else if(d1 == 0x8 && d4 == 0x4) {
  201. uint16_t v = registers[d2] + registers[d3];
  202. registers[0xf] = v > 0xff;
  203. registers[d2] = v % 0x100;
  204. } else if(d1 == 0x8 && d4 == 0x5) {
  205. registers[0xf] = registers[d2] > registers[d3];
  206. registers[d2] -= registers[d3];
  207. } else if(d1 == 0x8 && d4 == 0x6) {
  208. registers[0xf] = registers[d2] % 2 == 1;
  209. registers[d2] >>= 1;
  210. } else if(d1 == 0x8 && d4 == 0x7) {
  211. registers[0xf] = registers[d3] > registers[d2];
  212. registers[d2] = registers[d3] - registers[d2];
  213. } else if(d1 == 0x8 && d4 == 0xe) {
  214. registers[0xf] = registers[d2] / 128 == 1;
  215. registers[d2] <<= 1;
  216. } else if(d1 == 0x9) {
  217. if(registers[d2] != registers[d3]) PC += 2;
  218. } else if(d1 == 0xa) {
  219. I = d5;
  220. } else if(d1 == 0xb) {
  221. PC = d5 + registers[0x0];
  222. next = false;
  223. } else if(d1 == 0xc) {
  224. registers[d2] = (rand() % 0x100) & d7;
  225. } else if(d1 == 0xd) {
  226. bool collision = false;
  227. if(d4 == 0) for(uint8_t i = 0; i < 16; i++) {
  228. for(uint8_t j = 0; j < 8; j++)
  229. if(nthbit(memory[I + i * 2], 7 - j)) collision |= toggle_pixel(registers[d2] + j, registers[d3] + i);
  230. for(uint8_t j = 0; j < 8; j++)
  231. if(nthbit(memory[I + i * 2 + 1], 7 - j)) collision |= toggle_pixel(registers[d2] + 8 + j, registers[d3] + i);
  232. } else for(uint8_t i = 0; i < d4; i++) {
  233. for(uint8_t j = 0; j < 8; j++)
  234. if(nthbit(memory[I + i], 7 - j)) collision |= toggle_pixel(registers[d2] + j, registers[d3] + i);
  235. }
  236. registers[0xf] = collision;
  237. } else if(d1 == 0xe && d7 == 0x9e) {
  238. if(buttons[registers[d2]]) PC += 2;
  239. } else if(d1 == 0xe && d7 == 0xa1) {
  240. if(!buttons[registers[d2]]) PC += 2;
  241. } else if(d1 == 0xf && d7 == 0x07) {
  242. registers[d2] = time_delay;
  243. } else if(d1 == 0xf && d7 == 0x0a) {
  244. next = false;
  245. for(uint8_t i = 0; i < BUTTONS_COUNT; i++)
  246. if(buttons[i]) {
  247. next = true;
  248. registers[d2] = i;
  249. break;
  250. }
  251. } else if(d1 == 0xf && d7 == 0x15) {
  252. time_delay = registers[d2];
  253. } else if(d1 == 0xf && d7 == 0x18) {
  254. time_sound = registers[d2];
  255. } else if(d1 == 0xf && d7 == 0x1e) {
  256. I += registers[d2];
  257. } else if(d1 == 0xf && d7 == 0x29) {
  258. I = 5 * registers[d2];
  259. } else if(d1 == 0xf && d7 == 0x30) {
  260. I = 10 * (registers[d2] - 0x10) + 80;
  261. } else if(d1 == 0xf && d7 == 0x33) {
  262. memory[I] = registers[d2] / 100;
  263. memory[I + 1] = (registers[d2] / 10) % 10;
  264. memory[I + 2] = registers[d2] % 10;
  265. } else if(d1 == 0xf && d7 == 0x55) {
  266. for(uint8_t i = 0; i <= d2; i++)
  267. memory[I + i] = registers[i];
  268. } else if(d1 == 0xf && d7 == 0x65) {
  269. for(uint8_t i = 0; i <= d2; i++)
  270. registers[i] = memory[I + i];
  271. } else if(d1 == 0xf && d7 == 0x75) {
  272. for(uint8_t i = 0; i <= d2; i++)
  273. rpl[I + i] = registers[i];
  274. } else if(d1 == 0xf && d7 == 0x85) {
  275. for(uint8_t i = 0; i <= d2; i++)
  276. registers[i] = rpl[I + i];
  277. }
  278. if(next) PC += 2;
  279. }
  280. FuriTimer* timer_delay;
  281. static void tick_delay(void* context) {
  282. UNUSED(context);
  283. if(time_delay > 0) time_delay--;
  284. }
  285. FuriTimer* timer_sound;
  286. //TODO: fix sound
  287. /*bool speaker_on = false;
  288. static void tick_sound(void* context) {
  289. UNUSED(context);
  290. if(time_sound > 0) time_sound--;
  291. if(time_sound > 0 && !speaker_on) {
  292. furi_hal_speaker_start(800, 1.f);
  293. speaker_on = true;
  294. } else if(time_sound == 0 && speaker_on) {
  295. furi_hal_speaker_stop();
  296. speaker_on = false;
  297. }
  298. }*/
  299. static void tick_sound(void* context) {
  300. UNUSED(context);
  301. if(time_sound > 0) time_sound--;
  302. if(time_sound > 0)
  303. furi_hal_vibro_on(true);
  304. else
  305. furi_hal_vibro_on(false);
  306. }
  307. int32_t schip_main(void* p) {
  308. for(int i = 0; i < FONT_SIZE; i++)
  309. memory[i] = font[i];
  310. UNUSED(p);
  311. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  312. // Configure view port
  313. ViewPort* view_port = view_port_alloc();
  314. view_port_draw_callback_set(view_port, app_draw_callback, view_port);
  315. view_port_input_callback_set(view_port, app_input_callback, event_queue);
  316. // Register view port in GUI
  317. Gui* gui = furi_record_open(RECORD_GUI);
  318. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  319. InputEvent event;
  320. // void* speaker = (void*)furi_hal_speaker_acquire(1000);
  321. Storage* storage = furi_record_open(RECORD_STORAGE);
  322. storage_simply_mkdir(storage, FILES_PATH);
  323. file_path = furi_string_alloc_set_str(FILES_PATH);
  324. DialogsFileBrowserOptions browser_options;
  325. dialog_file_browser_set_basic_options(&browser_options, FILE_EXTENSION, NULL);
  326. browser_options.base_path = FILES_PATH;
  327. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  328. bool running = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
  329. furi_record_close(RECORD_DIALOGS);
  330. if(running) {
  331. File* file = storage_file_alloc(storage);
  332. furi_check(storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING));
  333. uint8_t read_byte;
  334. uint16_t read_size;
  335. uint16_t p = ROM_START;
  336. do {
  337. read_size = storage_file_read(file, &read_byte, 1);
  338. memory[p] = read_byte;
  339. p++;
  340. } while(read_size > 0);
  341. storage_file_close(file);
  342. storage_file_free(file);
  343. }
  344. furi_record_close(RECORD_STORAGE);
  345. // We have to round up to 17 here
  346. timer = furi_timer_alloc(tick, FuriTimerTypePeriodic, NULL);
  347. furi_timer_start(timer, 1);
  348. timer_delay = furi_timer_alloc(tick_delay, FuriTimerTypePeriodic, NULL);
  349. furi_timer_start(timer_delay, 17);
  350. timer_sound = furi_timer_alloc(tick_sound, FuriTimerTypePeriodic, NULL);
  351. furi_timer_start(timer_sound, 17);
  352. while(running) {
  353. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  354. if(event.type == InputTypeLong && event.key == InputKeyBack) running = false;
  355. if(event.type == InputTypePress && event.key == InputKeyBack) keyboard_open = !keyboard_open;
  356. if(keyboard_open) {
  357. if(event.type == InputTypePress && event.key == InputKeyLeft) keyboard_x--;
  358. if(event.type == InputTypePress && event.key == InputKeyDown) keyboard_y++;
  359. if(event.type == InputTypePress && event.key == InputKeyRight) keyboard_x++;
  360. if(event.type == InputTypePress && event.key == InputKeyUp) keyboard_y--;
  361. keyboard_x %= KEYPAD_WIDTH;
  362. keyboard_y %= KEYPAD_HEIGHT;
  363. if(event.type == InputTypePress && event.key == InputKeyOk) buttons[keypad[keyboard_y * 4 + keyboard_x]] = true;
  364. if(event.type == InputTypeRelease && event.key == InputKeyOk) buttons[keypad[keyboard_y * 4 + keyboard_x]] = false;
  365. } else {
  366. if(event.type == InputTypePress && event.key == InputKeyUp) buttons[5] = true;
  367. if(event.type == InputTypeRelease && event.key == InputKeyUp) buttons[5] = false;
  368. if(event.type == InputTypePress && event.key == InputKeyLeft) buttons[7] = true;
  369. if(event.type == InputTypeRelease && event.key == InputKeyLeft) buttons[7] = false;
  370. if(event.type == InputTypePress && event.key == InputKeyDown) buttons[8] = true;
  371. if(event.type == InputTypeRelease && event.key == InputKeyDown) buttons[8] = false;
  372. if(event.type == InputTypePress && event.key == InputKeyRight) buttons[9] = true;
  373. if(event.type == InputTypeRelease && event.key == InputKeyRight) buttons[9] = false;
  374. if(event.type == InputTypePress && event.key == InputKeyOk) buttons[6] = true;
  375. if(event.type == InputTypeRelease && event.key == InputKeyOk) buttons[6] = false;
  376. }
  377. }
  378. view_port_update(view_port);
  379. }
  380. furi_timer_stop(timer);
  381. furi_timer_free(timer);
  382. furi_timer_stop(timer_delay);
  383. furi_timer_free(timer_delay);
  384. furi_timer_stop(timer_sound);
  385. furi_timer_free(timer_sound);
  386. // furi_hal_speaker_release(speaker);
  387. view_port_enabled_set(view_port, false);
  388. gui_remove_view_port(gui, view_port);
  389. view_port_free(view_port);
  390. furi_message_queue_free(event_queue);
  391. furi_record_close(RECORD_GUI);
  392. return 0;
  393. }