trade.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. #include <furi_hal_light.h>
  2. #include <furi.h>
  3. #include <gui/view.h>
  4. #include <pokemon_icons.h>
  5. #include "../pokemon_app.h"
  6. #define GAME_BOY_CLK gpio_ext_pb2
  7. #define GAME_BOY_SI gpio_ext_pc3
  8. #define GAME_BOY_SO gpio_ext_pb3
  9. #define DELAY_MICROSECONDS 15
  10. #define PKMN_BLANK 0x00
  11. #define ITEM_1_HIGHLIGHTED 0xD0
  12. #define ITEM_2_HIGHLIGHTED 0xD1
  13. #define ITEM_3_HIGHLIGHTED 0xD2
  14. #define ITEM_1_SELECTED 0xD4
  15. #define ITEM_2_SELECTED 0xD5
  16. #define ITEM_3_SELECTED 0xD6
  17. #define PKMN_MASTER 0x01
  18. #define PKMN_SLAVE 0x02
  19. #define PKMN_CONNECTED 0x60
  20. #define PKMN_WAIT 0x7F
  21. #define PKMN_ACTION 0x60
  22. #define PKMN_TRADE_CENTRE ITEM_1_SELECTED
  23. #define PKMN_COLOSSEUM ITEM_2_SELECTED
  24. #define PKMN_BREAK_LINK ITEM_3_SELECTED
  25. #define TRADE_CENTRE_WAIT 0xFD
  26. struct trade_model {
  27. bool trading;
  28. bool connected;
  29. render_gameboy_state_t gameboy_status;
  30. uint8_t curr_pokemon;
  31. const PokemonTable* pokemon_table;
  32. FuriTimer* draw_timer;
  33. };
  34. typedef unsigned char byte;
  35. typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t;
  36. typedef enum {
  37. INIT,
  38. READY_TO_GO,
  39. SEEN_FIRST_WAIT,
  40. SENDING_RANDOM_DATA,
  41. WAITING_TO_SEND_DATA,
  42. START_SENDING_DATA,
  43. SENDING_DATA,
  44. DATA_SENT,
  45. SENDING_PATCH_DATA,
  46. TRADE_PENDING,
  47. TRADE_CONFIRMATION,
  48. DONE
  49. } trade_centre_state_t;
  50. /* TODO: Convert all of these to be maintained in a struct in the Trade context */
  51. uint8_t out_data = 0; // Should be able to be made as part of view model or static in used function
  52. uint8_t in_data = 0; //Should be able to be made as part of view model, is used in multiple funcs
  53. uint8_t shift = 0; //Should be able to be made as part of view model, is used in multiple funcs
  54. uint32_t time = 0; //Should be able to be made static in used function
  55. volatile int counter = 0; // Should be able to be made static in used function
  56. volatile bool procesing = true; // Review this vars use, it could potentially be removed
  57. volatile connection_state_t connection_state =
  58. NOT_CONNECTED; // Should be made in to view model struct
  59. volatile trade_centre_state_t trade_centre_state =
  60. INIT; // Should be able to be made part of view model
  61. unsigned char INPUT_BLOCK[405]; // Put this in pokemon_fap? Not sure yet
  62. void screen_gameboy_connect(Canvas* const canvas) {
  63. canvas_draw_frame(canvas, 0, 0, 128, 64);
  64. canvas_draw_icon(canvas, 1, 21, &I_Connect_me_62x31);
  65. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  66. canvas_draw_icon(canvas, 80, 0, &I_game_boy);
  67. canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
  68. canvas_draw_str(canvas, 18, 13, "Connect GB");
  69. }
  70. void screen_gameboy_connected(Canvas* const canvas) {
  71. canvas_draw_frame(canvas, 0, 0, 128, 64);
  72. canvas_draw_icon(canvas, 1, 21, &I_Connected_62x31);
  73. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  74. canvas_draw_icon(canvas, 80, 0, &I_game_boy);
  75. canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
  76. canvas_draw_str(canvas, 18, 13, "Connected!");
  77. }
  78. int time_in_seconds = 0;
  79. static void trade_draw_callback(Canvas* canvas, void* model) {
  80. const char* gameboy_status_text = NULL;
  81. struct trade_model* view_model = model;
  82. uint8_t curr_pokemon = view_model->curr_pokemon;
  83. canvas_clear(canvas);
  84. if(!view_model->trading) {
  85. if(!view_model->connected) {
  86. furi_hal_light_set(LightGreen, 0x00);
  87. furi_hal_light_set(LightBlue, 0x00);
  88. furi_hal_light_set(LightRed, 0xff);
  89. screen_gameboy_connect(canvas);
  90. } else {
  91. furi_hal_light_set(LightGreen, 0xff);
  92. furi_hal_light_set(LightBlue, 0x00);
  93. furi_hal_light_set(LightRed, 0x00);
  94. screen_gameboy_connected(canvas);
  95. }
  96. } else {
  97. switch(view_model->gameboy_status) {
  98. case GAMEBOY_TRADING:
  99. furi_hal_light_set(LightGreen, 0x00);
  100. furi_hal_light_set(LightRed, 0x00);
  101. if(time_in_seconds % 2 == 1) {
  102. furi_hal_light_set(LightBlue, 0xff);
  103. canvas_draw_icon(canvas, 0, 0, &I_gb_step_1);
  104. } else {
  105. furi_hal_light_set(LightBlue, 0x00);
  106. canvas_draw_icon(canvas, 0, 0, &I_gb_step_2);
  107. }
  108. break;
  109. case GAMEBOY_READY:
  110. case GAMEBOY_WAITING:
  111. case GAMEBOY_SEND:
  112. canvas_draw_icon(canvas, 38, 11, view_model->pokemon_table[curr_pokemon].icon);
  113. break;
  114. default:
  115. // Default state added to eliminated enum warning
  116. break;
  117. }
  118. canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
  119. canvas_draw_frame(canvas, 0, 0, 128, 64);
  120. canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);
  121. switch(view_model->gameboy_status) {
  122. case GAMEBOY_READY:
  123. gameboy_status_text = "READY";
  124. break;
  125. case GAMEBOY_WAITING:
  126. gameboy_status_text = "WAITING";
  127. break;
  128. case GAMEBOY_TRADE_READY:
  129. gameboy_status_text = "READY";
  130. break;
  131. case GAMEBOY_SEND:
  132. gameboy_status_text = "DEAL...";
  133. break;
  134. case GAMEBOY_PENDING:
  135. gameboy_status_text = "PENDING...";
  136. break;
  137. case GAMEBOY_TRADING:
  138. gameboy_status_text = "TRADING...";
  139. break;
  140. default:
  141. gameboy_status_text = "INITIAL";
  142. break;
  143. }
  144. canvas_draw_str(canvas, 48, 12, gameboy_status_text);
  145. canvas_draw_icon(canvas, 27, 1, &I_red_16x15);
  146. time_in_seconds = (int)DWT->CYCCNT / (72000000.0f / 4); // 250ms
  147. }
  148. }
  149. uint32_t micros() {
  150. return DWT->CYCCNT / 64;
  151. }
  152. /* Get the response byte from the link partner, updating the connection
  153. * state if needed.
  154. */
  155. byte getConnectResponse(byte in) {
  156. byte ret;
  157. switch(in) {
  158. case PKMN_CONNECTED:
  159. connection_state = CONNECTED;
  160. ret = PKMN_CONNECTED;
  161. break;
  162. case PKMN_MASTER:
  163. ret = PKMN_SLAVE;
  164. break;
  165. case PKMN_BLANK:
  166. ret = PKMN_BLANK;
  167. break;
  168. default:
  169. connection_state = NOT_CONNECTED;
  170. ret = PKMN_BREAK_LINK;
  171. break;
  172. }
  173. return ret;
  174. }
  175. /* Figure out what the pokemon game is requesting and move to that mode.
  176. */
  177. byte getMenuResponse(byte in) {
  178. /* TODO: Find out what this byte means */
  179. byte response = 0x00;
  180. switch(in) {
  181. case PKMN_CONNECTED:
  182. response = PKMN_CONNECTED;
  183. break;
  184. case PKMN_TRADE_CENTRE:
  185. connection_state = TRADE_CENTRE;
  186. break;
  187. case PKMN_COLOSSEUM:
  188. connection_state = COLOSSEUM;
  189. break;
  190. case PKMN_BREAK_LINK:
  191. case PKMN_MASTER:
  192. connection_state = NOT_CONNECTED;
  193. response = PKMN_BREAK_LINK;
  194. break;
  195. default:
  196. response = in;
  197. break;
  198. }
  199. return response;
  200. }
  201. byte getTradeCentreResponse(byte in, void* context) {
  202. PokemonFap* pokemon_fap = (PokemonFap*)context;
  203. uint8_t* trade_block_flat = (uint8_t*)pokemon_fap->trade_block;
  204. render_gameboy_state_t gameboy_status;
  205. byte send = in;
  206. furi_assert(context);
  207. with_view_model(
  208. pokemon_fap->trade_view,
  209. struct trade_model * model,
  210. { gameboy_status = model->gameboy_status; },
  211. false);
  212. switch(trade_centre_state) {
  213. case INIT:
  214. // TODO: What does this value of in mean?
  215. if(in == 0x00) {
  216. // TODO: What does counter signify here?
  217. if(counter == 5) {
  218. trade_centre_state = READY_TO_GO;
  219. // CLICK EN LA MESA
  220. gameboy_status = GAMEBOY_READY;
  221. }
  222. counter++;
  223. }
  224. break;
  225. case READY_TO_GO:
  226. if((in & 0xF0) == 0xF0) trade_centre_state = SEEN_FIRST_WAIT;
  227. break;
  228. case SEEN_FIRST_WAIT:
  229. if((in & 0xF0) != 0xF0) {
  230. counter = 0;
  231. trade_centre_state = SENDING_RANDOM_DATA;
  232. }
  233. break;
  234. case SENDING_RANDOM_DATA:
  235. if((in & 0xF0) == 0xF0) {
  236. if(counter == 5) {
  237. trade_centre_state = WAITING_TO_SEND_DATA;
  238. gameboy_status = GAMEBOY_WAITING;
  239. }
  240. counter++;
  241. }
  242. break;
  243. case WAITING_TO_SEND_DATA:
  244. if((in & 0xF0) != 0xF0) {
  245. counter = 0;
  246. INPUT_BLOCK[counter] = in;
  247. send = trade_block_flat[counter];
  248. counter++;
  249. trade_centre_state = SENDING_DATA;
  250. }
  251. break;
  252. case SENDING_DATA:
  253. INPUT_BLOCK[counter] = in;
  254. send = trade_block_flat[counter];
  255. counter++;
  256. if(counter == 405) //TODO: replace with sizeof struct rather than static number
  257. trade_centre_state = SENDING_PATCH_DATA;
  258. break;
  259. case SENDING_PATCH_DATA:
  260. if(in == 0xFD) {
  261. counter = 0;
  262. send = 0xFD;
  263. } else {
  264. counter++;
  265. if(counter == 197) // TODO: What is this magic value?
  266. trade_centre_state = TRADE_PENDING;
  267. }
  268. break;
  269. case TRADE_PENDING:
  270. /* TODO: What are these states */
  271. if(in == 0x6F) {
  272. trade_centre_state = READY_TO_GO;
  273. send = 0x6F;
  274. gameboy_status = GAMEBOY_TRADE_READY;
  275. } else if((in & 0x60) == 0x60) {
  276. send = 0x60; // first pokemon
  277. gameboy_status = GAMEBOY_SEND;
  278. } else if(in == 0x00) {
  279. send = 0;
  280. trade_centre_state = TRADE_CONFIRMATION;
  281. }
  282. break;
  283. case TRADE_CONFIRMATION:
  284. if(in == 0x61) {
  285. trade_centre_state = TRADE_PENDING;
  286. gameboy_status = GAMEBOY_PENDING;
  287. } else if((in & 0x60) == 0x60) {
  288. trade_centre_state = DONE;
  289. }
  290. break;
  291. case DONE:
  292. if(in == 0x00) {
  293. send = 0;
  294. trade_centre_state = INIT;
  295. gameboy_status = GAMEBOY_TRADING;
  296. }
  297. break;
  298. default:
  299. // Do Nothing
  300. break;
  301. }
  302. with_view_model(
  303. pokemon_fap->trade_view,
  304. struct trade_model * model,
  305. { model->gameboy_status = gameboy_status; },
  306. false);
  307. return send;
  308. }
  309. void transferBit(void* context) {
  310. PokemonFap* pokemon_fap = (PokemonFap*)context;
  311. furi_assert(context);
  312. bool connected;
  313. bool trading;
  314. with_view_model(
  315. pokemon_fap->trade_view,
  316. struct trade_model * model,
  317. {
  318. connected = model->connected;
  319. trading = model->trading;
  320. },
  321. false);
  322. byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
  323. in_data |= raw_data << (7 - shift);
  324. if(++shift > 7) {
  325. shift = 0;
  326. switch(connection_state) {
  327. case NOT_CONNECTED:
  328. connected = false;
  329. out_data = getConnectResponse(in_data);
  330. break;
  331. case CONNECTED:
  332. connected = true;
  333. out_data = getMenuResponse(in_data);
  334. break;
  335. case TRADE_CENTRE:
  336. out_data = getTradeCentreResponse(in_data, pokemon_fap);
  337. break;
  338. default:
  339. out_data = in_data;
  340. break;
  341. }
  342. in_data = 0; // TODO: I don't think this is necessary?
  343. }
  344. while(procesing && !furi_hal_gpio_read(&GAME_BOY_CLK))
  345. ;
  346. furi_hal_gpio_write(&GAME_BOY_SO, out_data & 0x80 ? true : false);
  347. furi_delay_us(
  348. DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
  349. // TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15
  350. if(trade_centre_state == READY_TO_GO) trading = true;
  351. out_data = out_data << 1;
  352. with_view_model(
  353. pokemon_fap->trade_view,
  354. struct trade_model * model,
  355. {
  356. model->connected = connected;
  357. model->trading = trading;
  358. },
  359. false);
  360. }
  361. void input_clk_gameboy(void* context) {
  362. furi_assert(context);
  363. if(time > 0) {
  364. // if there is no response from the master in 120 microseconds, the counters are reset
  365. if(micros() - time > 120) {
  366. // IDLE & Reset
  367. in_data = 0;
  368. shift = 0;
  369. }
  370. }
  371. transferBit(context);
  372. time = micros();
  373. }
  374. void trade_draw_timer_callback(void* context) {
  375. PokemonFap* pokemon_fap = (PokemonFap*)context;
  376. with_view_model(
  377. pokemon_fap->trade_view, struct trade_model * model, { UNUSED(model); }, true);
  378. }
  379. void trade_enter_callback(void* context) {
  380. PokemonFap* pokemon_fap = (PokemonFap*)context;
  381. furi_assert(context);
  382. with_view_model(
  383. pokemon_fap->trade_view,
  384. struct trade_model * model,
  385. {
  386. model->trading = false;
  387. model->connected = false;
  388. model->gameboy_status = GAMEBOY_INITIAL;
  389. model->pokemon_table = pokemon_fap->pokemon_table;
  390. model->curr_pokemon = (uint8_t)pokemon_fap->curr_pokemon;
  391. model->draw_timer =
  392. furi_timer_alloc(trade_draw_timer_callback, FuriTimerTypePeriodic, pokemon_fap);
  393. /* Every 100 ms, trigger a draw update */
  394. furi_timer_start(model->draw_timer, 100);
  395. },
  396. true);
  397. // B3 (Pin6) / SO (2)
  398. furi_hal_gpio_write(&GAME_BOY_SO, false);
  399. furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  400. // B2 (Pin5) / SI (3)
  401. furi_hal_gpio_write(&GAME_BOY_SI, false);
  402. furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
  403. // // C3 (Pin7) / CLK (5)
  404. furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeInterruptRise, GpioPullNo, GpioSpeedVeryHigh);
  405. furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
  406. furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, pokemon_fap);
  407. }
  408. void disconnect_pin(const GpioPin* pin) {
  409. furi_hal_gpio_init(pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  410. furi_hal_gpio_write(pin, true);
  411. }
  412. void trade_exit_callback(void* context) {
  413. PokemonFap* pokemon_fap = (PokemonFap*)context;
  414. furi_assert(context);
  415. procesing = false;
  416. furi_hal_light_set(LightGreen, 0x00);
  417. furi_hal_light_set(LightBlue, 0x00);
  418. furi_hal_light_set(LightRed, 0x00);
  419. /* Stop the timer, and deallocate it as the enter callback allocates it on entry */
  420. with_view_model(
  421. pokemon_fap->trade_view,
  422. struct trade_model * model,
  423. { furi_timer_free(model->draw_timer); },
  424. false);
  425. }
  426. View* trade_alloc(PokemonFap* pokemon_fap) {
  427. View* view;
  428. view = view_alloc();
  429. procesing = true;
  430. view_set_context(view, pokemon_fap);
  431. view_allocate_model(view, ViewModelTypeLockFree, sizeof(struct trade_model));
  432. view_set_draw_callback(view, trade_draw_callback);
  433. view_set_enter_callback(view, trade_enter_callback);
  434. view_set_exit_callback(view, trade_exit_callback);
  435. return view;
  436. }
  437. void trade_free(PokemonFap* pokemon_fap) {
  438. furi_assert(pokemon_fap);
  439. // Free resources
  440. procesing = false;
  441. furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
  442. disconnect_pin(&GAME_BOY_CLK);
  443. view_free_model(pokemon_fap->trade_view);
  444. view_free(pokemon_fap->trade_view);
  445. }