tama_p1.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <input/input.h>
  4. #include <storage/storage.h>
  5. #include <stdlib.h>
  6. #include <stm32wbxx_ll_tim.h>
  7. #include "tamalib/tamalib.h"
  8. #include "tama.h"
  9. #include "compiled/assets_icons.h"
  10. TamaApp* g_ctx;
  11. FuriMutex* g_state_mutex;
  12. bool portrait_mode = false;
  13. bool in_menu = false;
  14. int speed = 1;
  15. const int max_speed = 4;
  16. int menu_cursor = 0;
  17. const int menu_items = 4;
  18. static const Icon* icons_list[] = {
  19. &I_icon_0,
  20. &I_icon_1,
  21. &I_icon_2,
  22. &I_icon_3,
  23. &I_icon_4,
  24. &I_icon_5,
  25. &I_icon_6,
  26. &I_icon_7,
  27. };
  28. // static void draw_landscape(Canvas* const canvas, void* cb_ctx)
  29. static void draw_landscape(Canvas* const canvas) {
  30. // FURI_LOG_D(TAG, "Drawing frame");
  31. // Calculate positioning
  32. uint16_t canv_width = canvas_width(canvas);
  33. uint16_t canv_height = canvas_height(canvas);
  34. uint16_t lcd_matrix_scaled_width = 32 * TAMA_SCREEN_SCALE_FACTOR;
  35. uint16_t lcd_matrix_scaled_height = 16 * TAMA_SCREEN_SCALE_FACTOR;
  36. // uint16_t lcd_matrix_top = 0;
  37. uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2;
  38. uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2;
  39. uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN;
  40. uint16_t lcd_icon_upper_left = lcd_matrix_left;
  41. uint16_t lcd_icon_lower_top = lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN;
  42. uint16_t lcd_icon_lower_left = lcd_matrix_left;
  43. uint16_t lcd_icon_spacing_horiz =
  44. (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE;
  45. uint16_t y = lcd_matrix_top;
  46. for(uint8_t row = 0; row < 16; ++row) {
  47. uint16_t x = lcd_matrix_left;
  48. uint32_t row_pixels = g_ctx->framebuffer[row];
  49. for(uint8_t col = 0; col < 32; ++col) {
  50. if(row_pixels & 1) {
  51. canvas_draw_box(canvas, x, y, TAMA_SCREEN_SCALE_FACTOR, TAMA_SCREEN_SCALE_FACTOR);
  52. }
  53. x += TAMA_SCREEN_SCALE_FACTOR;
  54. row_pixels >>= 1;
  55. }
  56. y += TAMA_SCREEN_SCALE_FACTOR;
  57. }
  58. // Start drawing icons
  59. uint8_t lcd_icons = g_ctx->icons;
  60. // Draw top icons
  61. y = lcd_icon_upper_top;
  62. // y = 64 - TAMA_LCD_ICON_SIZE;
  63. uint16_t x_ic = lcd_icon_upper_left;
  64. for(uint8_t i = 0; i < 4; ++i) {
  65. if(lcd_icons & 1) {
  66. canvas_draw_icon(canvas, x_ic, y, icons_list[i]);
  67. }
  68. // x_ic += TAMA_LCD_ICON_SIZE + 4;
  69. x_ic += lcd_icon_spacing_horiz;
  70. lcd_icons >>= 1;
  71. }
  72. // Draw bottom icons
  73. y = lcd_icon_lower_top;
  74. x_ic = lcd_icon_lower_left;
  75. for(uint8_t i = 4; i < 8; ++i) {
  76. // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE);
  77. if(lcd_icons & 1) {
  78. canvas_draw_icon(canvas, x_ic, y, icons_list[i]);
  79. }
  80. x_ic += lcd_icon_spacing_horiz;
  81. lcd_icons >>= 1;
  82. }
  83. }
  84. // static void draw_portrait(Canvas* const canvas, void* cb_ctx)
  85. static void draw_portrait(Canvas* const canvas) {
  86. // FURI_LOG_D(TAG, "Drawing frame");
  87. // Calculate positioning
  88. // uint16_t canv_width = canvas_width(canvas);
  89. uint16_t canv_height = canvas_height(canvas);
  90. uint16_t lcd_matrix_scaled_width = 32 * TAMA_SCREEN_SCALE_FACTOR;
  91. uint16_t lcd_matrix_scaled_height = 16 * TAMA_SCREEN_SCALE_FACTOR;
  92. // uint16_t lcd_matrix_top = 0;
  93. uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2;
  94. // uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2;
  95. uint16_t lcd_matrix_left = 64 - TAMA_LCD_ICON_SIZE;
  96. uint16_t lcd_icon_upper_left = lcd_matrix_left;
  97. uint16_t lcd_icon_lower_left = lcd_matrix_left;
  98. uint16_t lcd_icon_spacing_horiz =
  99. (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE;
  100. uint16_t y = lcd_matrix_top; // 64
  101. for(uint8_t row = 0; row < 16; ++row) {
  102. uint16_t x = 128; // lcd_matrix_left
  103. uint32_t row_pixels = g_ctx->framebuffer[row];
  104. for(uint8_t col = 0; col < 32; ++col) {
  105. if(row_pixels & 1) {
  106. canvas_draw_box(
  107. canvas, y + 32, x - 66, TAMA_SCREEN_SCALE_FACTOR, TAMA_SCREEN_SCALE_FACTOR);
  108. }
  109. x -= TAMA_SCREEN_SCALE_FACTOR;
  110. row_pixels >>= 1;
  111. }
  112. y += TAMA_SCREEN_SCALE_FACTOR;
  113. }
  114. // Start drawing icons
  115. uint8_t lcd_icons = g_ctx->icons;
  116. // Draw top icons
  117. // y = lcd_icon_upper_top;
  118. y = 30;
  119. // y = 64 - TAMA_LCD_ICON_SIZE;
  120. uint16_t x_ic = lcd_icon_upper_left;
  121. // uint16_t x_ic = 64 - TAMA_LCD_ICON_SIZE;
  122. for(uint8_t i = 0; i < 4; ++i) {
  123. if(lcd_icons & 1) {
  124. canvas_draw_icon(canvas, y, x_ic, icons_list[i]);
  125. }
  126. x_ic -= lcd_icon_spacing_horiz; // TAMA_LCD_ICON_SIZE + 4;
  127. lcd_icons >>= 1;
  128. }
  129. // Draw bottom icons
  130. y = 84; // lcd_icon_lower_top
  131. x_ic = lcd_icon_lower_left; // 64 - TAMA_LCD_ICON_SIZE
  132. for(uint8_t i = 4; i < 8; ++i) {
  133. // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE);
  134. if(lcd_icons & 1) {
  135. canvas_draw_icon(canvas, y, x_ic, icons_list[i]);
  136. }
  137. x_ic -= lcd_icon_spacing_horiz;
  138. lcd_icons >>= 1;
  139. }
  140. }
  141. // static void draw_menu_portrait(Canvas* const canvas, void* cb_ctx) {}
  142. static void draw_menu_landscape(Canvas* const canvas) {
  143. canvas_draw_frame(canvas, 0, 0, 128, 64);
  144. canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignCenter, "Menu");
  145. canvas_draw_line(canvas, 0, 10, 128, 10);
  146. switch(menu_cursor) {
  147. case 0:
  148. canvas_draw_triangle(canvas, 4, 15, 6, 6, CanvasDirectionLeftToRight);
  149. break;
  150. case 1:
  151. canvas_draw_triangle(canvas, 4, 25, 6, 6, CanvasDirectionLeftToRight);
  152. break;
  153. case 2:
  154. canvas_draw_triangle(canvas, 4, 35, 6, 6, CanvasDirectionLeftToRight);
  155. break;
  156. case menu_items - 1:
  157. canvas_draw_triangle(canvas, 4, 45, 6, 6, CanvasDirectionLeftToRight);
  158. break;
  159. }
  160. canvas_draw_str(canvas, 12, 20, "A+C (mute/change time)");
  161. if(portrait_mode) {
  162. canvas_draw_str(canvas, 12, 30, "Orientation: Portrait");
  163. } else {
  164. canvas_draw_str(canvas, 12, 30, "Orientation: Landscape");
  165. }
  166. switch(speed) {
  167. case 0: // freeze menu too
  168. canvas_draw_str(canvas, 12, 40, "Speed: 0x");
  169. break;
  170. case 1:
  171. canvas_draw_str(canvas, 12, 40, "Speed: 1x");
  172. break;
  173. case 2:
  174. canvas_draw_str(canvas, 12, 40, "Speed: 2x");
  175. break;
  176. case 3:
  177. canvas_draw_str(canvas, 12, 40, "Speed: 3x");
  178. break;
  179. case max_speed:
  180. canvas_draw_str(canvas, 12, 40, "Speed: 4x (max)");
  181. break;
  182. default:
  183. canvas_draw_str(canvas, 12, 40, "Speed ?x");
  184. break;
  185. }
  186. canvas_draw_str(canvas, 12, 50, "Close menu");
  187. }
  188. static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) {
  189. furi_assert(cb_ctx);
  190. FuriMutex* const mutex = cb_ctx;
  191. if(furi_mutex_acquire(mutex, 25) != FuriStatusOk) return;
  192. if(g_ctx->rom == NULL) {
  193. canvas_set_font(canvas, FontPrimary);
  194. canvas_draw_str(canvas, 30, 30, "No ROM");
  195. } else if(g_ctx->halted) {
  196. canvas_set_font(canvas, FontPrimary);
  197. canvas_draw_str(canvas, 30, 30, "Halted");
  198. } else {
  199. if(in_menu) {
  200. if(portrait_mode) {
  201. // draw_menu_portrait(canvas);
  202. draw_menu_landscape(canvas);
  203. } else {
  204. draw_menu_landscape(canvas);
  205. }
  206. } else if(portrait_mode) {
  207. draw_portrait(canvas);
  208. } else {
  209. draw_landscape(canvas);
  210. }
  211. }
  212. furi_mutex_release(mutex);
  213. }
  214. static void tama_p1_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  215. furi_assert(event_queue);
  216. TamaEvent event = {.type = EventTypeInput, .input = *input_event};
  217. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  218. }
  219. static void tama_p1_update_timer_callback(FuriMessageQueue* event_queue) {
  220. furi_assert(event_queue);
  221. TamaEvent event = {.type = EventTypeTick};
  222. furi_message_queue_put(event_queue, &event, 0);
  223. }
  224. static void tama_p1_load_state() {
  225. state_t* state;
  226. uint8_t buf[4];
  227. bool error = false;
  228. state = tamalib_get_state();
  229. Storage* storage = furi_record_open(RECORD_STORAGE);
  230. File* file = storage_file_alloc(storage);
  231. if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
  232. storage_file_read(file, &buf, 4);
  233. if(buf[0] != (uint8_t)STATE_FILE_MAGIC[0] || buf[1] != (uint8_t)STATE_FILE_MAGIC[1] ||
  234. buf[2] != (uint8_t)STATE_FILE_MAGIC[2] || buf[3] != (uint8_t)STATE_FILE_MAGIC[3]) {
  235. FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH);
  236. error = true;
  237. }
  238. storage_file_read(file, &buf, 1);
  239. if(buf[0] != STATE_FILE_VERSION) {
  240. FURI_LOG_E(TAG, "FATAL: Unsupported version");
  241. error = true;
  242. }
  243. if(!error) {
  244. FURI_LOG_D(TAG, "Reading save.bin");
  245. storage_file_read(file, &buf, 2);
  246. *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8);
  247. storage_file_read(file, &buf, 2);
  248. *(state->x) = buf[0] | ((buf[1] & 0xF) << 8);
  249. storage_file_read(file, &buf, 2);
  250. *(state->y) = buf[0] | ((buf[1] & 0xF) << 8);
  251. storage_file_read(file, &buf, 1);
  252. *(state->a) = buf[0] & 0xF;
  253. storage_file_read(file, &buf, 1);
  254. *(state->b) = buf[0] & 0xF;
  255. storage_file_read(file, &buf, 1);
  256. *(state->np) = buf[0] & 0x1F;
  257. storage_file_read(file, &buf, 1);
  258. *(state->sp) = buf[0];
  259. storage_file_read(file, &buf, 1);
  260. *(state->flags) = buf[0] & 0xF;
  261. storage_file_read(file, &buf, 4);
  262. *(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
  263. storage_file_read(file, &buf, 4);
  264. *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) |
  265. (buf[3] << 24);
  266. storage_file_read(file, &buf, 4);
  267. *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) |
  268. (buf[3] << 24);
  269. storage_file_read(file, &buf, 1);
  270. *(state->prog_timer_enabled) = buf[0] & 0x1;
  271. storage_file_read(file, &buf, 1);
  272. *(state->prog_timer_data) = buf[0];
  273. storage_file_read(file, &buf, 1);
  274. *(state->prog_timer_rld) = buf[0];
  275. storage_file_read(file, &buf, 4);
  276. *(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
  277. FURI_LOG_D(TAG, "Restoring Interupts");
  278. for(uint32_t i = 0; i < INT_SLOT_NUM; i++) {
  279. storage_file_read(file, &buf, 1);
  280. state->interrupts[i].factor_flag_reg = buf[0] & 0xF;
  281. storage_file_read(file, &buf, 1);
  282. state->interrupts[i].mask_reg = buf[0] & 0xF;
  283. storage_file_read(file, &buf, 1);
  284. state->interrupts[i].triggered = buf[0] & 0x1;
  285. }
  286. /* First 640 half bytes correspond to the RAM */
  287. FURI_LOG_D(TAG, "Restoring RAM");
  288. for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
  289. storage_file_read(file, &buf, 1);
  290. SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF);
  291. }
  292. /* I/Os are from 0xF00 to 0xF7F */
  293. FURI_LOG_D(TAG, "Restoring I/O");
  294. for(uint32_t i = 0; i < MEM_IO_SIZE; i++) {
  295. storage_file_read(file, &buf, 1);
  296. SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF);
  297. }
  298. FURI_LOG_D(TAG, "Refreshing Hardware");
  299. tamalib_refresh_hw();
  300. }
  301. }
  302. storage_file_close(file);
  303. storage_file_free(file);
  304. furi_record_close(RECORD_STORAGE);
  305. }
  306. static void tama_p1_save_state() {
  307. // Saving state
  308. FURI_LOG_D(TAG, "Saving Gamestate");
  309. uint8_t buf[4];
  310. state_t* state;
  311. uint32_t offset = 0;
  312. state = tamalib_get_state();
  313. Storage* storage = furi_record_open(RECORD_STORAGE);
  314. File* file = storage_file_alloc(storage);
  315. if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  316. buf[0] = (uint8_t)STATE_FILE_MAGIC[0];
  317. buf[1] = (uint8_t)STATE_FILE_MAGIC[1];
  318. buf[2] = (uint8_t)STATE_FILE_MAGIC[2];
  319. buf[3] = (uint8_t)STATE_FILE_MAGIC[3];
  320. offset += storage_file_write(file, &buf, sizeof(buf));
  321. buf[0] = STATE_FILE_VERSION & 0xFF;
  322. offset += storage_file_write(file, &buf, 1);
  323. buf[0] = *(state->pc) & 0xFF;
  324. buf[1] = (*(state->pc) >> 8) & 0x1F;
  325. offset += storage_file_write(file, &buf, 2);
  326. buf[0] = *(state->x) & 0xFF;
  327. buf[1] = (*(state->x) >> 8) & 0xF;
  328. offset += storage_file_write(file, &buf, 2);
  329. buf[0] = *(state->y) & 0xFF;
  330. buf[1] = (*(state->y) >> 8) & 0xF;
  331. offset += storage_file_write(file, &buf, 2);
  332. buf[0] = *(state->a) & 0xF;
  333. offset += storage_file_write(file, &buf, 1);
  334. buf[0] = *(state->b) & 0xF;
  335. offset += storage_file_write(file, &buf, 1);
  336. buf[0] = *(state->np) & 0x1F;
  337. offset += storage_file_write(file, &buf, 1);
  338. buf[0] = *(state->sp) & 0xFF;
  339. offset += storage_file_write(file, &buf, 1);
  340. buf[0] = *(state->flags) & 0xF;
  341. offset += storage_file_write(file, &buf, 1);
  342. buf[0] = *(state->tick_counter) & 0xFF;
  343. buf[1] = (*(state->tick_counter) >> 8) & 0xFF;
  344. buf[2] = (*(state->tick_counter) >> 16) & 0xFF;
  345. buf[3] = (*(state->tick_counter) >> 24) & 0xFF;
  346. offset += storage_file_write(file, &buf, sizeof(buf));
  347. buf[0] = *(state->clk_timer_timestamp) & 0xFF;
  348. buf[1] = (*(state->clk_timer_timestamp) >> 8) & 0xFF;
  349. buf[2] = (*(state->clk_timer_timestamp) >> 16) & 0xFF;
  350. buf[3] = (*(state->clk_timer_timestamp) >> 24) & 0xFF;
  351. offset += storage_file_write(file, &buf, sizeof(buf));
  352. buf[0] = *(state->prog_timer_timestamp) & 0xFF;
  353. buf[1] = (*(state->prog_timer_timestamp) >> 8) & 0xFF;
  354. buf[2] = (*(state->prog_timer_timestamp) >> 16) & 0xFF;
  355. buf[3] = (*(state->prog_timer_timestamp) >> 24) & 0xFF;
  356. offset += storage_file_write(file, &buf, sizeof(buf));
  357. buf[0] = *(state->prog_timer_enabled) & 0x1;
  358. offset += storage_file_write(file, &buf, 1);
  359. buf[0] = *(state->prog_timer_data) & 0xFF;
  360. offset += storage_file_write(file, &buf, 1);
  361. buf[0] = *(state->prog_timer_rld) & 0xFF;
  362. offset += storage_file_write(file, &buf, 1);
  363. buf[0] = *(state->call_depth) & 0xFF;
  364. buf[1] = (*(state->call_depth) >> 8) & 0xFF;
  365. buf[2] = (*(state->call_depth) >> 16) & 0xFF;
  366. buf[3] = (*(state->call_depth) >> 24) & 0xFF;
  367. offset += storage_file_write(file, &buf, sizeof(buf));
  368. for(uint32_t i = 0; i < INT_SLOT_NUM; i++) {
  369. buf[0] = state->interrupts[i].factor_flag_reg & 0xF;
  370. offset += storage_file_write(file, &buf, 1);
  371. buf[0] = state->interrupts[i].mask_reg & 0xF;
  372. offset += storage_file_write(file, &buf, 1);
  373. buf[0] = state->interrupts[i].triggered & 0x1;
  374. offset += storage_file_write(file, &buf, 1);
  375. }
  376. /* First 640 half bytes correspond to the RAM */
  377. for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) {
  378. buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF;
  379. offset += storage_file_write(file, &buf, 1);
  380. }
  381. /* I/Os are from 0xF00 to 0xF7F */
  382. for(uint32_t i = 0; i < MEM_IO_SIZE; i++) {
  383. buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF;
  384. offset += storage_file_write(file, &buf, 1);
  385. }
  386. }
  387. storage_file_close(file);
  388. storage_file_free(file);
  389. furi_record_close(RECORD_STORAGE);
  390. FURI_LOG_D(TAG, "Finished Writing %lu", offset);
  391. }
  392. static int32_t tama_p1_worker(void* context) {
  393. bool running = true;
  394. FuriMutex* mutex = context;
  395. while(furi_mutex_acquire(mutex, FuriWaitForever) != FuriStatusOk) furi_delay_tick(1);
  396. cpu_sync_ref_timestamp();
  397. LL_TIM_EnableCounter(TIM2);
  398. tama_p1_load_state();
  399. while(running) {
  400. if(furi_thread_flags_get()) {
  401. running = false;
  402. } else {
  403. // FURI_LOG_D(TAG, "Stepping");
  404. // for (int i = 0; i < 100; ++i)
  405. tamalib_step();
  406. }
  407. }
  408. LL_TIM_DisableCounter(TIM2);
  409. furi_mutex_release(mutex);
  410. return 0;
  411. }
  412. static void tama_p1_init(TamaApp* const ctx) {
  413. g_ctx = ctx;
  414. memset(ctx, 0, sizeof(TamaApp));
  415. tama_p1_hal_init(&ctx->hal);
  416. // Load ROM
  417. Storage* storage = furi_record_open(RECORD_STORAGE);
  418. FileInfo fi;
  419. if(storage_common_stat(storage, TAMA_ROM_PATH, &fi) == FSE_OK) {
  420. File* rom_file = storage_file_alloc(storage);
  421. if(storage_file_open(rom_file, TAMA_ROM_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
  422. ctx->rom = malloc((size_t)fi.size);
  423. uint8_t* buf_ptr = ctx->rom;
  424. size_t read = 0;
  425. while(read < fi.size) {
  426. size_t to_read = fi.size - read;
  427. if(to_read > UINT16_MAX) to_read = UINT16_MAX;
  428. uint16_t now_read = storage_file_read(rom_file, buf_ptr, (uint16_t)to_read);
  429. read += now_read;
  430. buf_ptr += now_read;
  431. }
  432. // Reorder endianess of ROM
  433. for(size_t i = 0; i < fi.size; i += 2) {
  434. uint8_t b = ctx->rom[i];
  435. ctx->rom[i] = ctx->rom[i + 1];
  436. ctx->rom[i + 1] = b & 0xF;
  437. }
  438. }
  439. storage_file_close(rom_file);
  440. storage_file_free(rom_file);
  441. }
  442. furi_record_close(RECORD_STORAGE);
  443. if(ctx->rom != NULL) {
  444. // Init TIM2
  445. // 64KHz
  446. LL_TIM_InitTypeDef tim_init = {
  447. .Prescaler = 999,
  448. .CounterMode = LL_TIM_COUNTERMODE_UP,
  449. .Autoreload = 0xFFFFFFFF,
  450. };
  451. LL_TIM_Init(TIM2, &tim_init);
  452. LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
  453. LL_TIM_DisableCounter(TIM2);
  454. LL_TIM_SetCounter(TIM2, 0);
  455. // Init TamaLIB
  456. tamalib_register_hal(&ctx->hal);
  457. tamalib_init((u12_t*)ctx->rom, NULL, 64000);
  458. tamalib_set_speed(speed);
  459. // TODO: implement fast forwarding
  460. ctx->fast_forward_done = true;
  461. // Start stepping thread
  462. ctx->thread = furi_thread_alloc();
  463. furi_thread_set_name(ctx->thread, "TamaLIB");
  464. furi_thread_set_stack_size(ctx->thread, 1024);
  465. furi_thread_set_callback(ctx->thread, tama_p1_worker);
  466. furi_thread_set_context(ctx->thread, g_state_mutex);
  467. furi_thread_start(ctx->thread);
  468. }
  469. }
  470. static void tama_p1_deinit(TamaApp* const ctx) {
  471. if(ctx->rom != NULL) {
  472. tamalib_release();
  473. furi_thread_free(ctx->thread);
  474. free(ctx->rom);
  475. }
  476. }
  477. int32_t tama_p1_app(void* p) {
  478. UNUSED(p);
  479. TamaApp* ctx = malloc(sizeof(TamaApp));
  480. g_state_mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
  481. tama_p1_init(ctx);
  482. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TamaEvent));
  483. ViewPort* view_port = view_port_alloc();
  484. view_port_draw_callback_set(view_port, tama_p1_draw_callback, g_state_mutex);
  485. view_port_input_callback_set(view_port, tama_p1_input_callback, event_queue);
  486. Gui* gui = furi_record_open(RECORD_GUI);
  487. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  488. FuriTimer* timer =
  489. furi_timer_alloc(tama_p1_update_timer_callback, FuriTimerTypePeriodic, event_queue);
  490. furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30);
  491. // portrait_mode = false;
  492. // in_menu = false;
  493. // menu_cursor = 2;
  494. for(bool running = true; running;) {
  495. TamaEvent event;
  496. FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
  497. if(event_status == FuriStatusOk) {
  498. // Local override with acquired context
  499. if(furi_mutex_acquire(g_state_mutex, FuriWaitForever) != FuriStatusOk) continue;
  500. if(event.type == EventTypeTick) {
  501. // FURI_LOG_D(TAG, "EventTypeTick");
  502. view_port_update(view_port);
  503. } else if(event.type == EventTypeInput) {
  504. FURI_LOG_D(
  505. TAG,
  506. "EventTypeInput: %ld %d %d",
  507. event.input.sequence,
  508. event.input.key,
  509. event.input.type);
  510. InputType input_type = event.input.type;
  511. btn_state_t tama_btn_state = 0;
  512. if(input_type == InputTypePress)
  513. tama_btn_state = BTN_STATE_PRESSED;
  514. else if(input_type == InputTypeRelease)
  515. tama_btn_state = BTN_STATE_RELEASED;
  516. if(in_menu) {
  517. if(event.input.key == InputKeyUp && event.input.type == InputTypePress) {
  518. if(menu_cursor > 0) {
  519. menu_cursor -= 1;
  520. } else {
  521. menu_cursor = menu_items - 1;
  522. }
  523. } else if(event.input.key == InputKeyDown && event.input.type == InputTypePress) {
  524. if(menu_cursor < menu_items - 1) {
  525. menu_cursor += 1;
  526. } else {
  527. menu_cursor = 0;
  528. }
  529. } else if(event.input.key == InputKeyOk) {
  530. switch(menu_cursor) {
  531. case 0:
  532. // mute tamagotchi
  533. tamalib_set_button(BTN_LEFT, tama_btn_state);
  534. tamalib_set_button(BTN_RIGHT, tama_btn_state);
  535. break;
  536. case 1:
  537. // portrait_mode = true;
  538. if(event.input.type == InputTypePress) portrait_mode = !portrait_mode;
  539. break;
  540. case 2:
  541. if(event.input.type == InputTypePress) {
  542. if(speed == 2) { // skip 3x
  543. speed = 4;
  544. } else if(speed < max_speed) {
  545. speed++;
  546. } else {
  547. speed = 1;
  548. }
  549. tamalib_set_speed(speed);
  550. }
  551. break;
  552. case menu_items - 1:
  553. default:
  554. in_menu = false;
  555. break;
  556. }
  557. } else if(event.input.key == InputKeyBack) {
  558. in_menu = false;
  559. }
  560. } else { // out of menu
  561. if(input_type == InputTypePress || input_type == InputTypeRelease) {
  562. if(portrait_mode) {
  563. if(event.input.key == InputKeyDown) {
  564. tamalib_set_button(BTN_LEFT, tama_btn_state);
  565. } else if(event.input.key == InputKeyOk) {
  566. tamalib_set_button(BTN_MIDDLE, tama_btn_state);
  567. } else if(event.input.key == InputKeyRight) {
  568. tamalib_set_button(BTN_MIDDLE, tama_btn_state);
  569. } else if(event.input.key == InputKeyUp) {
  570. tamalib_set_button(BTN_RIGHT, tama_btn_state);
  571. } else if(event.input.key == InputKeyLeft) {
  572. in_menu = true;
  573. } else if(
  574. event.input.key == InputKeyBack &&
  575. event.input.type == InputTypeShort) {
  576. tama_p1_save_state();
  577. }
  578. } else {
  579. if(event.input.key == InputKeyLeft) {
  580. tamalib_set_button(BTN_LEFT, tama_btn_state);
  581. } else if(event.input.key == InputKeyOk) {
  582. tamalib_set_button(BTN_MIDDLE, tama_btn_state);
  583. } else if(event.input.key == InputKeyDown) {
  584. tamalib_set_button(BTN_MIDDLE, tama_btn_state);
  585. } else if(event.input.key == InputKeyRight) {
  586. tamalib_set_button(BTN_RIGHT, tama_btn_state);
  587. } else if(event.input.key == InputKeyUp) {
  588. in_menu = true;
  589. } else if(
  590. event.input.key == InputKeyBack &&
  591. event.input.type == InputTypePress) {
  592. tama_p1_save_state();
  593. }
  594. }
  595. }
  596. }
  597. if(event.input.key == InputKeyBack && event.input.type == InputTypeLong &&
  598. !in_menu) {
  599. furi_timer_stop(timer);
  600. running = false;
  601. tama_p1_save_state();
  602. }
  603. }
  604. furi_mutex_release(g_state_mutex);
  605. } else {
  606. // Timeout
  607. // FURI_LOG_D(TAG, "Timed out");
  608. }
  609. }
  610. if(ctx->rom != NULL) {
  611. furi_thread_flags_set(furi_thread_get_id(ctx->thread), 1);
  612. furi_thread_join(ctx->thread);
  613. }
  614. furi_timer_free(timer);
  615. view_port_enabled_set(view_port, false);
  616. gui_remove_view_port(gui, view_port);
  617. furi_record_close(RECORD_GUI);
  618. view_port_free(view_port);
  619. furi_message_queue_free(event_queue);
  620. furi_mutex_free(g_state_mutex);
  621. tama_p1_deinit(ctx);
  622. free(ctx);
  623. return 0;
  624. }