schip.c 15 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] = {1, 2, 3, 12, 4, 5, 6, 13, 7, 8, 9, 14, 10, 0, 11, 15};
  65. bool run = true;
  66. FuriString* file_path;
  67. static void app_draw_callback(Canvas* canvas, void* ctx) {
  68. UNUSED(ctx);
  69. canvas_clear(canvas);
  70. canvas_set_font(canvas, FontSecondary);
  71. if(keyboard_open) {
  72. for(uint8_t x = 0; x < KEYPAD_WIDTH; x++)
  73. for(uint8_t y = 0; y < KEYPAD_HEIGHT; y++)
  74. canvas_draw_frame(canvas, 32 + x * 16, y * 16, 16, 16);
  75. canvas_draw_frame(canvas, 33 + keyboard_x * 16, 1 + keyboard_y * 16, 14, 14);
  76. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 0, "1");
  77. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 0, "2");
  78. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 0, "3");
  79. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 0, "C");
  80. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 1, "4");
  81. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 1, "5");
  82. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 1, "6");
  83. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 1, "D");
  84. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 2, "7");
  85. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 2, "8");
  86. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 2, "9");
  87. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 2, "E");
  88. canvas_draw_str(canvas, 38 + 16 * 0, 12 + 16 * 3, "A");
  89. canvas_draw_str(canvas, 38 + 16 * 1, 12 + 16 * 3, "0");
  90. canvas_draw_str(canvas, 38 + 16 * 2, 12 + 16 * 3, "B");
  91. canvas_draw_str(canvas, 38 + 16 * 3, 12 + 16 * 3, "F");
  92. } else {
  93. for(uint8_t x = 0; x < SCREEN_WIDTH; x++)
  94. for(uint8_t y = 0; y < SCREEN_HEIGHT; y++)
  95. if(screen[x][y]) canvas_draw_dot(canvas, x, y);
  96. }
  97. }
  98. static void app_input_callback(InputEvent* input_event, void* ctx) {
  99. furi_assert(ctx);
  100. FuriMessageQueue* event_queue = ctx;
  101. furi_message_queue_put(event_queue, input_event, FuriWaitForever);
  102. }
  103. static bool nthbit(uint8_t n, uint8_t k) {
  104. return (n & (1 << k)) >> k == 1;
  105. }
  106. static bool toggle_pixel(uint8_t x, uint8_t y) {
  107. bool collision = false;
  108. if(hires) {
  109. collision = screen[x][y];
  110. screen[x][y] = !screen[x][y];
  111. } else {
  112. collision = screen[x * 2][y * 2];
  113. screen[x * 2][y * 2] = !screen[x * 2][y * 2];
  114. screen[x * 2 + 1][y * 2] = !screen[x * 2 + 1][y * 2];
  115. screen[x * 2][y * 2 + 1] = !screen[x * 2][y * 2 + 1];
  116. screen[x * 2 + 1][y * 2 + 1] = !screen[x * 2 + 1][y * 2 + 1];
  117. }
  118. return collision;
  119. }
  120. // TODO fix this:
  121. /*
  122. : draw-title
  123. draw-x := 16
  124. draw-y := 16
  125. dir := 32
  126. i := sprite-title
  127. loop
  128. sprite draw-x draw-y 0
  129. i += dir
  130. draw-y += 16
  131. if draw-y > TITLE_HEIGHT then draw-x += 16
  132. if draw-y > TITLE_HEIGHT then draw-y := 16
  133. if draw-x != TITLE_WIDTH then
  134. again
  135. dir := key
  136. ;
  137. */
  138. FuriTimer* timer;
  139. static void tick(void* context) {
  140. UNUSED(context);
  141. if(!run) return;
  142. furi_check(PC < MEM_SIZE - 1);
  143. uint8_t d1 = memory[PC] / 16;
  144. uint8_t d2 = memory[PC] % 16;
  145. uint8_t d3 = memory[PC + 1] / 16;
  146. uint8_t d4 = memory[PC + 1] % 16;
  147. uint16_t d5 = d2 * 256 + d3 * 16 + d4; // I
  148. uint16_t d6 = d1 * 4096 + d2 * 256 + d3 * 16 + d4; // Whole instruction
  149. // FURI_LOG_T(TAG, "Executing instruction %x at address %x", d6, PC);
  150. uint8_t d7 = d3 * 16 + d4; // Byte
  151. bool next = true;
  152. if(d1 == 0x0 && d2 == 0x0 && d3 == 0xc) {
  153. // TODO: scroll down d4 px
  154. } else if(d6 == 0x00e0) {
  155. for(uint8_t x = 0; x < SCREEN_WIDTH; x++)
  156. for(uint8_t y = 0; y < SCREEN_HEIGHT; y++) screen[x][y] = false;
  157. } else if(d6 == 0x00ee) {
  158. PC = stack[stack_pointer--];
  159. } else if(d6 == 0x00fb) {
  160. // TODO: scroll right 4 px
  161. } else if(d6 == 0x00fc) {
  162. // TODO: scroll left 4 px
  163. } else if(d6 == 0x00fd) {
  164. run = false;
  165. } else if(d6 == 0x00fe) {
  166. hires = false;
  167. } else if(d6 == 0x00ff) {
  168. hires = true;
  169. } else if(d1 == 0x0 || d1 == 0x1) {
  170. PC = d5;
  171. next = false;
  172. } else if(d1 == 0x2) {
  173. stack[++stack_pointer] = PC;
  174. PC = d5;
  175. next = false;
  176. } else if(d1 == 0x3) {
  177. if(registers[d2] == d7) PC += 2;
  178. } else if(d1 == 0x4) {
  179. if(registers[d2] != d7) PC += 2;
  180. } else if(d1 == 0x5) {
  181. if(registers[d2] == registers[d3]) PC += 2;
  182. } else if(d1 == 0x6) {
  183. registers[d2] = d7;
  184. } else if(d1 == 0x7) {
  185. registers[d2] += d7;
  186. } else if(d1 == 0x8 && d4 == 0x0) {
  187. registers[d2] = registers[d3];
  188. } else if(d1 == 0x8 && d4 == 0x1) {
  189. registers[d2] |= registers[d3];
  190. } else if(d1 == 0x8 && d4 == 0x2) {
  191. registers[d2] &= registers[d3];
  192. } else if(d1 == 0x8 && d4 == 0x3) {
  193. registers[d2] ^= registers[d3];
  194. } else if(d1 == 0x8 && d4 == 0x4) {
  195. uint16_t v = registers[d2] + registers[d3];
  196. registers[0xf] = v > 0xff;
  197. registers[d2] = v % 0x100;
  198. } else if(d1 == 0x8 && d4 == 0x5) {
  199. registers[0xf] = registers[d2] > registers[d3];
  200. registers[d2] -= registers[d3];
  201. } else if(d1 == 0x8 && d4 == 0x6) {
  202. registers[0xf] = registers[d2] % 2 == 1;
  203. registers[d2] >>= 1;
  204. } else if(d1 == 0x8 && d4 == 0x7) {
  205. registers[0xf] = registers[d3] > registers[d2];
  206. registers[d2] = registers[d3] - registers[d2];
  207. } else if(d1 == 0x8 && d4 == 0xe) {
  208. registers[0xf] = registers[d2] / 128 == 1;
  209. registers[d2] <<= 1;
  210. } else if(d1 == 0x9) {
  211. if(registers[d2] != registers[d3]) PC += 2;
  212. } else if(d1 == 0xa) {
  213. I = d5;
  214. } else if(d1 == 0xb) {
  215. PC = d5 + registers[0x0];
  216. next = false;
  217. } else if(d1 == 0xc) {
  218. registers[d2] = (rand() % 0x100) & d7;
  219. } else if(d1 == 0xd) {
  220. bool collision = false;
  221. if(d4 == 0)
  222. for(uint8_t i = 0; i < 16; i++) {
  223. for(uint8_t j = 0; j < 8; j++)
  224. if(nthbit(memory[I + i * 2], 7 - j))
  225. collision |= toggle_pixel(registers[d2] + j, registers[d3] + i);
  226. for(uint8_t j = 0; j < 8; j++)
  227. if(nthbit(memory[I + i * 2 + 1], 7 - j))
  228. collision |= toggle_pixel(registers[d2] + 8 + j, registers[d3] + i);
  229. }
  230. else
  231. for(uint8_t i = 0; i < d4; i++) {
  232. for(uint8_t j = 0; j < 8; j++)
  233. if(nthbit(memory[I + i], 7 - j))
  234. 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++) memory[I + i] = registers[i];
  267. } else if(d1 == 0xf && d7 == 0x65) {
  268. for(uint8_t i = 0; i <= d2; i++) registers[i] = memory[I + i];
  269. } else if(d1 == 0xf && d7 == 0x75) {
  270. for(uint8_t i = 0; i <= d2; i++) rpl[I + i] = registers[i];
  271. } else if(d1 == 0xf && d7 == 0x85) {
  272. for(uint8_t i = 0; i <= d2; i++) registers[i] = rpl[I + i];
  273. }
  274. if(next) PC += 2;
  275. }
  276. FuriTimer* timer_delay;
  277. static void tick_delay(void* context) {
  278. UNUSED(context);
  279. if(time_delay > 0) time_delay--;
  280. }
  281. FuriTimer* timer_sound;
  282. //TODO: fix sound
  283. /*bool speaker_on = false;
  284. static void tick_sound(void* context) {
  285. UNUSED(context);
  286. if(time_sound > 0) time_sound--;
  287. if(time_sound > 0 && !speaker_on) {
  288. furi_hal_speaker_start(800, 1.f);
  289. speaker_on = true;
  290. } else if(time_sound == 0 && speaker_on) {
  291. furi_hal_speaker_stop();
  292. speaker_on = false;
  293. }
  294. }*/
  295. static void tick_sound(void* context) {
  296. UNUSED(context);
  297. if(time_sound > 0) time_sound--;
  298. if(time_sound > 0)
  299. furi_hal_vibro_on(true);
  300. else
  301. furi_hal_vibro_on(false);
  302. }
  303. int32_t schip_main(void* p) {
  304. for(int i = 0; i < FONT_SIZE; i++) memory[i] = font[i];
  305. UNUSED(p);
  306. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  307. // Configure view port
  308. ViewPort* view_port = view_port_alloc();
  309. view_port_draw_callback_set(view_port, app_draw_callback, view_port);
  310. view_port_input_callback_set(view_port, app_input_callback, event_queue);
  311. // Register view port in GUI
  312. Gui* gui = furi_record_open(RECORD_GUI);
  313. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  314. InputEvent event;
  315. // void* speaker = (void*)furi_hal_speaker_acquire(1000);
  316. Storage* storage = furi_record_open(RECORD_STORAGE);
  317. storage_simply_mkdir(storage, FILES_PATH);
  318. file_path = furi_string_alloc_set_str(FILES_PATH);
  319. DialogsFileBrowserOptions browser_options;
  320. dialog_file_browser_set_basic_options(&browser_options, FILE_EXTENSION, NULL);
  321. browser_options.base_path = FILES_PATH;
  322. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  323. bool running = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
  324. furi_record_close(RECORD_DIALOGS);
  325. if(running) {
  326. File* file = storage_file_alloc(storage);
  327. furi_check(storage_file_open(
  328. file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING));
  329. uint8_t read_byte;
  330. uint16_t read_size;
  331. uint16_t p = ROM_START;
  332. do {
  333. read_size = storage_file_read(file, &read_byte, 1);
  334. memory[p] = read_byte;
  335. p++;
  336. } while(read_size > 0);
  337. storage_file_close(file);
  338. storage_file_free(file);
  339. }
  340. furi_record_close(RECORD_STORAGE);
  341. // We have to round up to 17 here
  342. timer = furi_timer_alloc(tick, FuriTimerTypePeriodic, NULL);
  343. furi_timer_start(timer, 1);
  344. timer_delay = furi_timer_alloc(tick_delay, FuriTimerTypePeriodic, NULL);
  345. furi_timer_start(timer_delay, 17);
  346. timer_sound = furi_timer_alloc(tick_sound, FuriTimerTypePeriodic, NULL);
  347. furi_timer_start(timer_sound, 17);
  348. while(running) {
  349. if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
  350. if(event.type == InputTypeLong && event.key == InputKeyBack) running = false;
  351. if(event.type == InputTypePress && event.key == InputKeyBack)
  352. keyboard_open = !keyboard_open;
  353. if(keyboard_open) {
  354. if(event.type == InputTypePress && event.key == InputKeyLeft) keyboard_x--;
  355. if(event.type == InputTypePress && event.key == InputKeyDown) keyboard_y++;
  356. if(event.type == InputTypePress && event.key == InputKeyRight) keyboard_x++;
  357. if(event.type == InputTypePress && event.key == InputKeyUp) keyboard_y--;
  358. keyboard_x %= KEYPAD_WIDTH;
  359. keyboard_y %= KEYPAD_HEIGHT;
  360. if(event.type == InputTypePress && event.key == InputKeyOk)
  361. buttons[keypad[keyboard_y * 4 + keyboard_x]] = true;
  362. if(event.type == InputTypeRelease && event.key == InputKeyOk)
  363. buttons[keypad[keyboard_y * 4 + keyboard_x]] = false;
  364. } else {
  365. if(event.type == InputTypePress && event.key == InputKeyUp) buttons[5] = true;
  366. if(event.type == InputTypeRelease && event.key == InputKeyUp) buttons[5] = false;
  367. if(event.type == InputTypePress && event.key == InputKeyLeft) buttons[7] = true;
  368. if(event.type == InputTypeRelease && event.key == InputKeyLeft) buttons[7] = false;
  369. if(event.type == InputTypePress && event.key == InputKeyDown) buttons[8] = true;
  370. if(event.type == InputTypeRelease && event.key == InputKeyDown) buttons[8] = false;
  371. if(event.type == InputTypePress && event.key == InputKeyRight) buttons[9] = true;
  372. if(event.type == InputTypeRelease && event.key == InputKeyRight)
  373. 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. }