coleco.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #include <furi.h>
  2. #include <furi_hal_gpio.h>
  3. #include <gui/gui.h>
  4. #include <gui/modules/button_panel.h>
  5. const GpioPin* const pin_up = &gpio_ext_pa4;
  6. const GpioPin* const pin_down = &gpio_ext_pb2;
  7. const GpioPin* const pin_right = &gpio_ext_pa7;
  8. const GpioPin* const pin_left = &gpio_ext_pa6;
  9. const GpioPin* const pin_fire = &gpio_ext_pc3;
  10. const GpioPin* const pin_mode = &gpio_ext_pc1;
  11. typedef enum
  12. {
  13. EventTypeTick,
  14. EventTypeKey,
  15. } EventType;
  16. typedef struct
  17. {
  18. EventType type;
  19. InputEvent input;
  20. } PluginEvent;
  21. typedef struct
  22. {
  23. bool mode;
  24. bool button_up;
  25. bool button_down;
  26. bool button_right;
  27. bool button_left;
  28. bool button_fire;
  29. unsigned int button_code;
  30. ButtonPanel* button_panel;
  31. bool dpad;
  32. } Coleco;
  33. static void output_update(Coleco* coleco)
  34. {
  35. // TODO: check mode, possibly flipped
  36. if (coleco->mode)
  37. {
  38. furi_hal_gpio_write(pin_up, !coleco->button_up);
  39. furi_hal_gpio_write(pin_down, !coleco->button_down);
  40. furi_hal_gpio_write(pin_right, !coleco->button_right);
  41. furi_hal_gpio_write(pin_left, !coleco->button_left);
  42. }
  43. else
  44. {
  45. // TODO: check ordering of bits
  46. furi_hal_gpio_write(pin_left, (coleco->button_code & 1));
  47. furi_hal_gpio_write(pin_down, (coleco->button_code & 2));
  48. furi_hal_gpio_write(pin_right, (coleco->button_code & 4));
  49. furi_hal_gpio_write(pin_up, (coleco->button_code & 8));
  50. }
  51. furi_hal_gpio_write(pin_fire, !coleco->button_fire);
  52. }
  53. static void mode_isr(void* context)
  54. {
  55. Coleco* coleco = acquire_mutex((ValueMutex*)context, 25);
  56. if (coleco == NULL)
  57. {
  58. return;
  59. }
  60. coleco->mode = furi_hal_gpio_read(&gpio_ext_pc1);
  61. output_update(coleco);
  62. }
  63. static void render_callback(Canvas* const canvas, void* context)
  64. {
  65. Coleco* coleco = acquire_mutex((ValueMutex*)context, 25);
  66. if (coleco == NULL)
  67. {
  68. return;
  69. }
  70. canvas_set_font(canvas, FontPrimary);
  71. canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, "ColecoVision");
  72. if (coleco->dpad)
  73. {
  74. canvas_set_font(canvas, FontSecondary);
  75. canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignBottom, "D-pad");
  76. }
  77. release_mutex((ValueMutex*)context, coleco);
  78. }
  79. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue)
  80. {
  81. furi_assert(event_queue);
  82. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  83. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  84. }
  85. static Coleco* coleco_alloc()
  86. {
  87. Coleco* coleco = malloc(sizeof(Coleco));
  88. coleco->button_panel = button_panel_alloc();
  89. coleco->mode = true;
  90. coleco->button_up = false;
  91. coleco->button_down = false;
  92. coleco->button_right = false;
  93. coleco->button_left = false;
  94. coleco->button_fire = false;
  95. coleco->button_code = 0x0F;
  96. coleco->dpad = false;
  97. // configure output pins
  98. furi_hal_gpio_init(pin_up, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  99. furi_hal_gpio_init(pin_down, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  100. furi_hal_gpio_init(pin_right, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  101. furi_hal_gpio_init(pin_left, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  102. furi_hal_gpio_init(pin_fire, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  103. output_update(coleco);
  104. // configure mode pin
  105. furi_hal_gpio_init(pin_mode, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh);
  106. return coleco;
  107. }
  108. static void coleco_free(Coleco* coleco)
  109. {
  110. furi_assert(coleco);
  111. button_panel_free(coleco->button_panel);
  112. free(coleco);
  113. }
  114. int32_t coleco_app(void* p)
  115. {
  116. UNUSED(p);
  117. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  118. Coleco* coleco = coleco_alloc();
  119. ValueMutex coleco_mutex;
  120. if (!init_mutex(&coleco_mutex, coleco, sizeof(Coleco)))
  121. {
  122. FURI_LOG_E("Coleco", "cannot create mutex\r\n");
  123. coleco_free(coleco);
  124. return 255;
  125. }
  126. // set system callbacks
  127. ViewPort* view_port = view_port_alloc();
  128. view_port_draw_callback_set(view_port, render_callback, &coleco_mutex);
  129. view_port_input_callback_set(view_port, input_callback, event_queue);
  130. // open GUI and register view_port
  131. Gui* gui = furi_record_open("gui");
  132. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  133. // enable mode ISR
  134. furi_hal_gpio_add_int_callback(pin_mode, mode_isr, &coleco_mutex);
  135. PluginEvent event;
  136. for (bool processing = true; processing;)
  137. {
  138. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  139. Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex);
  140. if (event_status == FuriStatusOk)
  141. {
  142. // press events
  143. if (event.type == EventTypeKey)
  144. {
  145. switch (event.input.key)
  146. {
  147. case InputKeyUp:
  148. if (coleco->dpad)
  149. {
  150. if (event.input.type == InputTypePress)
  151. {
  152. coleco->button_up = true;
  153. }
  154. else if (event.input.type == InputTypeRelease)
  155. {
  156. coleco->button_up = false;
  157. }
  158. }
  159. else // FIXME: hack to allow the 1 to be pressed
  160. {
  161. if (event.input.type == InputTypePress)
  162. {
  163. coleco->button_code = 0x0D;
  164. }
  165. else if (event.input.type == InputTypeRelease)
  166. {
  167. coleco->button_code = 0x0F;
  168. }
  169. }
  170. break;
  171. case InputKeyDown:
  172. if (coleco->dpad)
  173. {
  174. if (event.input.type == InputTypePress)
  175. {
  176. coleco->button_down = true;
  177. }
  178. else if (event.input.type == InputTypeRelease)
  179. {
  180. coleco->button_down = false;
  181. }
  182. }
  183. break;
  184. case InputKeyRight:
  185. if (coleco->dpad)
  186. {
  187. if (event.input.type == InputTypePress)
  188. {
  189. coleco->button_right = true;
  190. }
  191. else if (event.input.type == InputTypeRelease)
  192. {
  193. coleco->button_right = false;
  194. }
  195. }
  196. break;
  197. case InputKeyLeft:
  198. if (coleco->dpad)
  199. {
  200. if (event.input.type == InputTypePress)
  201. {
  202. coleco->button_left = true;
  203. }
  204. else if (event.input.type == InputTypeRelease)
  205. {
  206. coleco->button_left = false;
  207. }
  208. }
  209. break;
  210. case InputKeyOk:
  211. if (coleco->dpad)
  212. {
  213. if (event.input.type == InputTypePress)
  214. {
  215. coleco->button_fire = true;
  216. }
  217. else if (event.input.type == InputTypeRelease)
  218. {
  219. coleco->button_fire = false;
  220. }
  221. }
  222. else
  223. {
  224. coleco->dpad = true;
  225. }
  226. break;
  227. case InputKeyBack:
  228. if (event.input.type == InputTypePress)
  229. {
  230. if (coleco->dpad)
  231. {
  232. coleco->dpad = false;
  233. }
  234. else
  235. {
  236. processing = false;
  237. }
  238. }
  239. break;
  240. default:
  241. break;
  242. }
  243. }
  244. }
  245. else
  246. {
  247. FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout");
  248. }
  249. output_update(coleco);
  250. view_port_update(view_port);
  251. release_mutex(&coleco_mutex, coleco);
  252. }
  253. furi_hal_gpio_remove_int_callback(pin_mode);
  254. view_port_enabled_set(view_port, false);
  255. gui_remove_view_port(gui, view_port);
  256. furi_record_close("gui");
  257. view_port_free(view_port);
  258. furi_message_queue_free(event_queue);
  259. delete_mutex(&coleco_mutex);
  260. coleco_free(coleco);
  261. return 0;
  262. }