diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19c1f46..6b91a12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,16 +21,19 @@ add_executable(usbretro_pce) # For the usbretro_pce target add_executable(usbretro_ngc) # For the usbretro_ngc target add_executable(usbretro_xb1) # For the usbretro_xb1 target add_executable(usbretro_nuon) # For the usbretro_nuon target +add_executable(usbretro_loopy) # For the usbretro_loopy target target_compile_definitions(usbretro_pce PRIVATE CONFIG_PCE=1) target_compile_definitions(usbretro_ngc PRIVATE CONFIG_NGC=1) target_compile_definitions(usbretro_xb1 PRIVATE CONFIG_XB1=1) target_compile_definitions(usbretro_nuon PRIVATE CONFIG_NUON=1) +target_compile_definitions(usbretro_loopy PRIVATE CONFIG_LOOPY=1) target_compile_options(usbretro_pce PRIVATE -O3) # Set optimization level to O3 for your_target target_compile_options(usbretro_ngc PRIVATE -O3) # Set optimization level to O3 for your_target target_compile_options(usbretro_xb1 PRIVATE -O3) # Set optimization level to O3 for your_target target_compile_options(usbretro_nuon PRIVATE -O3) # Set optimization level to O3 for your_target +target_compile_options(usbretro_loopy PRIVATE -O3) # Set optimization level to O3 for your_target if(CMAKE_BUILD_TYPE MATCHES Debug) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") @@ -42,6 +45,7 @@ pico_add_extra_outputs(usbretro_pce) pico_add_extra_outputs(usbretro_ngc) pico_add_extra_outputs(usbretro_xb1) pico_add_extra_outputs(usbretro_nuon) +pico_add_extra_outputs(usbretro_loopy) pico_generate_pio_header(usbretro_pce ${CMAKE_CURRENT_LIST_DIR}/console/pcengine/plex.pio ) pico_generate_pio_header(usbretro_pce ${CMAKE_CURRENT_LIST_DIR}/console/pcengine/clock.pio ) @@ -53,6 +57,8 @@ pico_generate_pio_header(usbretro_xb1 ${CMAKE_CURRENT_LIST_DIR}/common/ws2812.pi pico_generate_pio_header(usbretro_nuon ${CMAKE_CURRENT_LIST_DIR}/console/nuon/polyface_read.pio ) pico_generate_pio_header(usbretro_nuon ${CMAKE_CURRENT_LIST_DIR}/console/nuon/polyface_send.pio ) pico_generate_pio_header(usbretro_nuon ${CMAKE_CURRENT_LIST_DIR}/common/ws2812.pio ) +pico_generate_pio_header(usbretro_loopy ${CMAKE_CURRENT_LIST_DIR}/console/loopy/loopy.pio ) +pico_generate_pio_header(usbretro_loopy ${CMAKE_CURRENT_LIST_DIR}/common/ws2812.pio ) ## SET TARGE SOURCES set(COMMON_SOURCES @@ -106,6 +112,11 @@ target_sources(usbretro_nuon PUBLIC # For NUON ${CMAKE_CURRENT_SOURCE_DIR}/console/nuon/nuon.c ) +target_sources(usbretro_loopy PUBLIC # For (Casio) LOOPY + ${COMMON_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/console/loopy/loopy.c +) + ## SET TARGET INCLUDE DIRS target_include_directories(usbretro_pce PUBLIC # FOR PCE ${CMAKE_CURRENT_SOURCE_DIR} @@ -128,6 +139,11 @@ target_include_directories(usbretro_nuon PUBLIC # FOR NUON ${CMAKE_CURRENT_SOURCE_DIR}/common ) +target_include_directories(usbretro_loopy PUBLIC # FOR LOOPY + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/common +) + ## SET TARGET LINK LIBS set(COMMON_LIBRARIES pico_stdlib @@ -156,3 +172,8 @@ target_link_libraries(usbretro_nuon PRIVATE # FOR NUON ${COMMON_LIBRARIES} pico_bit_ops ) + +target_link_libraries(usbretro_loopy PRIVATE # FOR LOOPY + ${COMMON_LIBRARIES} + pico_bit_ops +) diff --git a/src/common/ws2812.c b/src/common/ws2812.c index 69aa80f..b6c4ed0 100644 --- a/src/common/ws2812.c +++ b/src/common/ws2812.c @@ -100,6 +100,16 @@ void pattern_purples(uint len, uint t) { } } +void pattern_pinks(uint len, uint t) { + int max = 100; // let's not draw too much current! + t %= max; + for (int i = 0; i < len; ++i) { + uint8_t intensity = t; // Adjust the intensity value for a darker effect + put_pixel(urgb_u32(intensity / 2, 0, intensity / 1)); // Dark purple color (red + blue) + if (++t >= max) t = 0; + } +} + void pattern_reds(uint len, uint t) { int max = 100; // let's not draw too much current! t %= max; @@ -136,6 +146,10 @@ void pattern_purple(uint len, uint t) { put_pixel(urgb_u32(6, 0, 64)); // purple } +void pattern_pink(uint len, uint t) { + put_pixel(urgb_u32(64, 20, 32)); // purple +} + void pattern_yellow(uint len, uint t) { put_pixel(urgb_u32(64, 64, 0)); // yellow } @@ -226,6 +240,14 @@ const struct { {pattern_green, "Green"}, // 3 controllers {pattern_purple, "Purple"}, // 4 controllers {pattern_yellow, "Yellow"}, // 5 controllers +#else +#ifdef CONFIG_LOOPY + {pattern_pinks, "Pinks"}, // 0 controllers + {pattern_pink, "Pink"}, // 1 controller + {pattern_blue, "Blue"}, // 2 controllers + {pattern_red, "Red"}, // 3 controllers + {pattern_green, "Green"}, // 4 controllers + {pattern_yellow, "Yellow"}, // 5 controllers #else//CONFIG_PCE {pattern_blues, "Blues"}, // 0 controllers {pattern_blue, "Blue"}, // 1 controller @@ -235,6 +257,7 @@ const struct { {pattern_yellow, "Yellow"}, // 5 controllers #endif #endif +#endif #endif {pattern_random, "Random data"},// fun {pattern_sparkle, "Sparkles"}, diff --git a/src/console/loopy/loopy.c b/src/console/loopy/loopy.c new file mode 100644 index 0000000..dbfb5ed --- /dev/null +++ b/src/console/loopy/loopy.c @@ -0,0 +1,363 @@ +// loopy.c + +#include "loopy.h" + +uint32_t output_word = 0; + +// init for casio loopy communication +void loopy_init() +{ + stdio_init_all(); + + pio = pio0; // Both state machines can run on the same PIO processor + + gpio_init(ROW0_PIN); + gpio_init(ROW1_PIN); + gpio_init(ROW2_PIN); + gpio_init(ROW3_PIN); + gpio_init(ROW4_PIN); + gpio_init(ROW5_PIN); + + gpio_set_dir(ROW0_PIN, GPIO_IN); + gpio_set_dir(ROW1_PIN, GPIO_IN); + gpio_set_dir(ROW2_PIN, GPIO_IN); + gpio_set_dir(ROW3_PIN, GPIO_IN); + gpio_set_dir(ROW4_PIN, GPIO_IN); + gpio_set_dir(ROW5_PIN, GPIO_IN); + + unsigned short int i; + for (i = 0; i < 8; ++i) { + gpio_init(BIT0_PIN + i); + gpio_set_dir(BIT0_PIN + i, GPIO_OUT); + gpio_put(BIT0_PIN + i, 0); + } + + // Load the plex (multiplex output) program, and configure a free state machine + // to run the program. + + // uint offset = pio_add_program(pio1, &loopy_program); + // sm1 = pio_claim_unused_sm(pio, true); + // loopy_program_init(pio, sm1, offset, ROW0_PIN, BIT0_PIN); + + // uint offset2 = pio_add_program(pio, &loopy_program); + // sm2 = pio_claim_unused_sm(pio, true); + // loopy_program_init(pio, sm2, offset2, ROW1_PIN, BIT0_PIN); + + // uint offset3 = pio_add_program(pio, &loopy_program); + // sm3 = pio_claim_unused_sm(pio, true); + // loopy_program_init(pio, sm3, offset3, ROW2_PIN, BIT0_PIN); + + output_word = 0; // no buttons pushed +} + +// +// core1_entry - inner-loop for the second core +// +void __not_in_flash_func(core1_entry)(void) +{ + static bool rx_bit = 0; + + while (1) + { + // + // rx_bit = pio_sm_get(pio, sm1); + + // assume data is already formatted in output_word and push it to the state machine + // pio_sm_put(pio, sm1, output_word & 0x0f); + // pio_sm_put(pio, sm2, (output_word << 4) & 0x0f); + // pio_sm_put(pio, sm3, (output_word << 8) & 0x0f); + // TODO: implement gamepad, mouse, multi-tap output with PIO. + + int16_t player_1 = (players[0].output_buttons & 0xffff); + int16_t player_2 = (players[1].output_buttons & 0xffff); + int16_t player_3 = (players[2].output_buttons & 0xffff); + int16_t player_4 = (players[3].output_buttons & 0xffff); + bool isMouse = (!(player_1 & 0x000f)) || (!(player_2 & 0x000f)) || + (!(player_3 & 0x000f)) || (!(player_4 & 0x000f)); + + if (!isMouse) { + // Gamepad output + // + // bit0 bit1 bit2 bit3 + // ROW0 Presence Start Trigger-L Trigger-R + // ROW1 A D C B + // ROW2 Dpad-Up Dpad-Down Dpad-Left Dpad-Right + // + + if (gpio_get(ROW0_PIN)) { + // Player 1 - ROW0 + gpio_put(BIT0_PIN, 1); // Presence + gpio_put(BIT1_PIN, ((player_1 & 0x0080) == 0) ? 1 : 0); // Start + gpio_put(BIT2_PIN, ((player_1 & 0x4000) == 0) ? 1 : 0); // L + gpio_put(BIT3_PIN, ((player_1 & 0x8000) == 0) ? 1 : 0); // R + + // Player 2 - ROW0 + gpio_put(BIT4_PIN, 1); // Presence + gpio_put(BIT5_PIN, ((player_2 & 0x0080) == 0) ? 1 : 0); // Start + gpio_put(BIT6_PIN, ((player_2 & 0x4000) == 0) ? 1 : 0); // L + gpio_put(BIT7_PIN, ((player_2 & 0x8000) == 0) ? 1 : 0); // R + + } else if (gpio_get(ROW1_PIN)) { + // Player 1 - ROW1 + gpio_put(BIT0_PIN, ((player_1 & 0x0020) == 0) ? 1 : 0); // A + gpio_put(BIT1_PIN, ((player_1 & 0x2000) == 0) ? 1 : 0); // D + gpio_put(BIT2_PIN, ((player_1 & 0x1000) == 0) ? 1 : 0); // C + gpio_put(BIT3_PIN, ((player_1 & 0x0010) == 0) ? 1 : 0); // B + + // Player 2 - ROW1 + gpio_put(BIT4_PIN, ((player_2 & 0x0020) == 0) ? 1 : 0); // A + gpio_put(BIT5_PIN, ((player_2 & 0x2000) == 0) ? 1 : 0); // D + gpio_put(BIT6_PIN, ((player_2 & 0x1000) == 0) ? 1 : 0); // C + gpio_put(BIT7_PIN, ((player_2 & 0x0010) == 0) ? 1 : 0); // B + + } else if (gpio_get(ROW2_PIN)) { + // Player 1 - ROW2 + gpio_put(BIT0_PIN, ((player_1 & 0x0001) == 0) ? 1 : 0); // Up + gpio_put(BIT1_PIN, ((player_1 & 0x0004) == 0) ? 1 : 0); // Down + gpio_put(BIT2_PIN, ((player_1 & 0x0008) == 0) ? 1 : 0); // Left + gpio_put(BIT3_PIN, ((player_1 & 0x0002) == 0) ? 1 : 0); // Right + + // Player 2 - ROW2 + gpio_put(BIT4_PIN, ((player_2 & 0x0001) == 0) ? 1 : 0); // Up + gpio_put(BIT5_PIN, ((player_2 & 0x0004) == 0) ? 1 : 0); // Down + gpio_put(BIT6_PIN, ((player_2 & 0x0008) == 0) ? 1 : 0); // Left + gpio_put(BIT7_PIN, ((player_2 & 0x0002) == 0) ? 1 : 0); // Right + + } else if (gpio_get(ROW3_PIN)) { + // Player 3 - ROW0 + gpio_put(BIT0_PIN, 1); // Presence + gpio_put(BIT1_PIN, ((player_3 & 0x0080) == 0) ? 1 : 0); // Start + gpio_put(BIT2_PIN, ((player_3 & 0x4000) == 0) ? 1 : 0); // L + gpio_put(BIT3_PIN, ((player_3 & 0x8000) == 0) ? 1 : 0); // R + + // Player 4 - ROW0 + gpio_put(BIT4_PIN, 1); // Presence + gpio_put(BIT5_PIN, ((player_4 & 0x0080) == 0) ? 1 : 0); // Start + gpio_put(BIT6_PIN, ((player_4 & 0x4000) == 0) ? 1 : 0); // L + gpio_put(BIT7_PIN, ((player_4 & 0x8000) == 0) ? 1 : 0); // R + + } else if (gpio_get(ROW4_PIN)) { + // Player 3 - ROW1 + gpio_put(BIT0_PIN, ((player_3 & 0x0020) == 0) ? 1 : 0); // A + gpio_put(BIT1_PIN, ((player_3 & 0x2000) == 0) ? 1 : 0); // D + gpio_put(BIT2_PIN, ((player_3 & 0x1000) == 0) ? 1 : 0); // C + gpio_put(BIT3_PIN, ((player_3 & 0x0010) == 0) ? 1 : 0); // B + + // Player 4 - ROW1 + gpio_put(BIT4_PIN, ((player_4 & 0x0020) == 0) ? 1 : 0); // A + gpio_put(BIT5_PIN, ((player_4 & 0x2000) == 0) ? 1 : 0); // D + gpio_put(BIT6_PIN, ((player_4 & 0x1000) == 0) ? 1 : 0); // C + gpio_put(BIT7_PIN, ((player_4 & 0x0010) == 0) ? 1 : 0); // B + + } else if (gpio_get(ROW5_PIN)) { + // Player 3 - ROW2 + gpio_put(BIT0_PIN, ((player_3 & 0x0001) == 0) ? 1 : 0); // Up + gpio_put(BIT1_PIN, ((player_3 & 0x0004) == 0) ? 1 : 0); // Down + gpio_put(BIT2_PIN, ((player_3 & 0x0008) == 0) ? 1 : 0); // Left + gpio_put(BIT3_PIN, ((player_3 & 0x0002) == 0) ? 1 : 0); // Right + + // Player 4 - ROW2 + gpio_put(BIT4_PIN, ((player_4 & 0x0001) == 0) ? 1 : 0); // Up + gpio_put(BIT5_PIN, ((player_4 & 0x0004) == 0) ? 1 : 0); // Down + gpio_put(BIT6_PIN, ((player_4 & 0x0008) == 0) ? 1 : 0); // Left + gpio_put(BIT7_PIN, ((player_4 & 0x0002) == 0) ? 1 : 0); // Right + } else { + // Time between ROWs + gpio_put(BIT0_PIN, 0); + gpio_put(BIT1_PIN, 0); + gpio_put(BIT2_PIN, 0); + gpio_put(BIT3_PIN, 0); + + gpio_put(BIT4_PIN, 0); + gpio_put(BIT5_PIN, 0); + gpio_put(BIT6_PIN, 0); + gpio_put(BIT7_PIN, 0); + } + + // TODO: construct 48-bit output_word with this logic to reduce steps at this phase. + } else { + // Mouse output + // + // bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 + // [X encoder raw] [Y encoder raw] Left N/C Right Presence + // + + gpio_put(BIT0_PIN, ((player_1 & 0x0008) == 0) ? 1 : 0); // X + gpio_put(BIT1_PIN, ((player_1 & 0x0004) == 0) ? 1 : 0); // X + gpio_put(BIT2_PIN, ((player_1 & 0x0001) == 0) ? 1 : 0); // Y + gpio_put(BIT3_PIN, ((player_1 & 0x0004) == 0) ? 1 : 0); // Y + + gpio_put(BIT4_PIN, ((player_1 & 0x0020) == 0) ? 0 : 1); // Left + gpio_put(BIT5_PIN, 0); + gpio_put(BIT6_PIN, ((player_1 & 0x0010) == 0) ? 0 : 1); // Right + gpio_put(BIT7_PIN, 1); // Presence + + // TODO: handle multiple mice + controllers. + } + + update_output(); + + unsigned short int i; + for (i = 0; i < MAX_PLAYERS; ++i) { + // decrement outputs from globals + players[i].global_x = (players[i].global_x - players[i].output_analog_1x); + players[i].global_y = (players[i].global_y - players[i].output_analog_1y); + + players[i].output_analog_1x = 0; + players[i].output_analog_1y = 0; + players[i].output_buttons = players[i].global_buttons & players[i].altern_buttons; + } + } +} + +// +// update_output - updates output_word with loopy data that is sent +// to the console based on ROW state and device type +// +void __not_in_flash_func(update_output)(void) +{ + int8_t bytes[4] = { 0 }; + + // unsigned short int i; + // for (i = 0; i < MAX_PLAYERS; ++i) + // { + // } + + if (playersCount < 1) + { + bytes[0] = 0x00; + bytes[1] = 0x00; + bytes[2] = 0x00; + } else { + bytes[0] = 0x01; + bytes[1] = players[0].output_buttons & 0x0f; + bytes[2] = players[0].output_buttons & 0xf0; + } + + bool isMouse = !(players[0].output_buttons & 0x000f); + + // mouse x/y states + if (isMouse) + { + // TODO: + int8_t mouse_byte = 0b10000000; + + bytes[0] = mouse_byte; + bytes[1] = mouse_byte; + bytes[2] = mouse_byte; + } + + output_word = ((bytes[0] & 0xff)) | // ROW0 + ((bytes[1] & 0xff) << 8) | // ROW1 + ((bytes[2] & 0xff) << 16); // ROW2 + + codes_task(); +} + + +// +// post_globals - accumulate the many intermediate mouse scans (~1ms) +// into an accumulator which will be reported back to loopy +// +void __not_in_flash_func(post_globals)( + uint8_t dev_addr, int8_t instance, uint32_t buttons, + uint8_t analog_1x, uint8_t analog_1y, uint8_t analog_2x, + uint8_t analog_2y, uint8_t analog_l, uint8_t analog_r, + uint32_t keys, uint8_t quad_x) +{ + // for merging extra device instances into the root instance (ex: joycon charging grip) + bool is_extra = (instance == -1); + if (is_extra) instance = 0; + + int player_index = find_player_index(dev_addr, instance); + uint16_t buttons_pressed = (~(buttons | 0x0800)) || keys; + if (player_index < 0 && buttons_pressed) + { + printf("[add player] [%d, %d]\n", dev_addr, instance); + player_index = add_player(dev_addr, instance); + } + + // printf("[player_index] [%d] [%d, %d]\n", player_index, dev_addr, instance); + + if (player_index >= 0) + { + // map analog to dpad movement here + uint8_t dpad_offset = 32; + if (analog_1x) + { + if (analog_1x > 128 + dpad_offset) buttons &= ~(0x02); // right + else if (analog_1x < 128 - dpad_offset) buttons &= ~(0x08); // left + } + if (analog_1y) + { + if (analog_1y > 128 + dpad_offset) buttons &= ~(0x01); // up + else if (analog_1y < 128 - dpad_offset) buttons &= ~(0x04); // down + } + + // extra instance buttons to merge with root player + if (is_extra) + { + players[0].altern_buttons = buttons; + } + else + { + players[player_index].global_buttons = buttons; + } + + players[player_index].output_buttons = players[player_index].global_buttons & players[player_index].altern_buttons; + + // basic socd (up priority, left+right neutral) + if (((~players[player_index].output_buttons) & 0x01) && ((~players[player_index].output_buttons) & 0x04)) { + players[player_index].output_buttons ^= 0x04; + } + if (((~players[player_index].output_buttons) & 0x02) && ((~players[player_index].output_buttons) & 0x08)) { + players[player_index].output_buttons ^= 0x0a; + } + + update_output(); + } +} + +// +// post_mouse_globals - accumulate the many intermediate mouse scans (~1ms) +// into an accumulator which will be reported back to loopy +// +void __not_in_flash_func(post_mouse_globals)( + uint8_t dev_addr, int8_t instance, uint16_t buttons, + uint8_t delta_x, uint8_t delta_y, uint8_t quad_x) +{ + // for merging extra device instances into the root instance (ex: joycon charging grip) + bool is_extra = (instance == -1); + if (is_extra) instance = 0; + + int player_index = find_player_index(dev_addr, instance); + uint16_t buttons_pressed = (~(buttons | 0x0f00)); + if (player_index < 0 && buttons_pressed) + { + printf("[add player] [%d, %d]\n", dev_addr, instance); + player_index = add_player(dev_addr, instance); + } + + // printf("[player_index] [%d] [%d, %d]\n", player_index, dev_addr, instance); + + if (player_index >= 0) + { + players[player_index].global_buttons = buttons; + + if (delta_x >= 128) + players[player_index].global_x = players[player_index].global_x - (256-delta_x); + else + players[player_index].global_x = players[player_index].global_x + delta_x; + + if (delta_y >= 128) + players[player_index].global_y = players[player_index].global_y - (256-delta_y); + else + players[player_index].global_y = players[player_index].global_y + delta_y; + + players[player_index].output_analog_1x = players[player_index].global_x; + players[player_index].output_analog_1y = players[player_index].global_y; + players[player_index].output_buttons = players[player_index].global_buttons & players[player_index].altern_buttons; + + update_output(); + } +} diff --git a/src/console/loopy/loopy.h b/src/console/loopy/loopy.h new file mode 100644 index 0000000..031e72a --- /dev/null +++ b/src/console/loopy/loopy.h @@ -0,0 +1,56 @@ +// loopy.h + +#ifndef LOOPY_H +#define LOOPY_H + +#include +// #include + +#include "tusb.h" +#include "loopy.pio.h" +#include "globals.h" + +// Define constants +#undef MAX_PLAYERS +#define MAX_PLAYERS 4 // Loopy supports up to 4 players + +// The pinout when looking into the front of the console is as follows +// (pin numbers taken from mainboard markings): +// +// 9 16 +// ROW1 bit0 bit3 bit4 bit5 ROW5 ROW3 GND +// ROW0 ROW2 bit1 bit2 bit6 bit7 ROW4 VCC +// 1 8 +// + +// ADAFRUIT_KB2040 // build for Adafruit KB2040 board +#define ROW0_PIN 26 +#define ROW1_PIN ROW0_PIN + 1 // 27 +#define ROW2_PIN ROW0_PIN + 2 // 28 +#define ROW3_PIN ROW0_PIN + 3 // 29 +#define ROW4_PIN 18 +#define ROW5_PIN 19 +#define BIT0_PIN 2 // Note - out pins must be a consecutive 'out' group +#define BIT1_PIN BIT0_PIN + 1 +#define BIT2_PIN BIT0_PIN + 2 +#define BIT3_PIN BIT0_PIN + 3 +#define BIT4_PIN BIT0_PIN + 4 +#define BIT5_PIN BIT0_PIN + 5 +#define BIT6_PIN BIT0_PIN + 6 +#define BIT7_PIN BIT0_PIN + 7 + +PIO pio; +uint sm1, sm2, sm3; // sm1 = ROW0, sm2 = ROW1, sm3 = ROW2 + +// Function declarations +void loopy_init(void); +void __not_in_flash_func(core1_entry)(void); +void __not_in_flash_func(update_output)(void); +void __not_in_flash_func(post_globals)(uint8_t dev_addr, int8_t instance, + uint32_t buttons, uint8_t analog_1x, uint8_t analog_1y, + uint8_t analog_2x, uint8_t analog_2y, uint8_t analog_l, + uint8_t analog_r, uint32_t keys, uint8_t quad_x); +void __not_in_flash_func(post_mouse_globals)(uint8_t dev_addr, int8_t instance, + uint16_t buttons, uint8_t delta_x, uint8_t delta_y, uint8_t quad_x); + +#endif // LOOPY_H diff --git a/src/console/loopy/loopy.pio b/src/console/loopy/loopy.pio new file mode 100644 index 0000000..0e0c324 --- /dev/null +++ b/src/console/loopy/loopy.pio @@ -0,0 +1,53 @@ +; +; By Robrt Dale Smith (c) 2022 +; +; +; Interfacing for a Casio Loopy controllers +; + +.program loopy + +; Wait for the jump pin to go high +wait_for_trigger: + jmp pin, continue + jmp wait_for_trigger + +; Output the first 4 bits of the word from the CPU +continue: + out pins, 4 + +; Loop back to wait for the next trigger + jmp wait_for_trigger + +% c-sdk { +static inline void loopy_program_init(PIO pio, uint sm, uint offset, uint jmppin, uint outpin) { + pio_sm_config c = loopy_program_get_default_config(offset); + + // Connect these GPIOs to this PIO block + pio_gpio_init(pio, jmppin); + + pio_gpio_init(pio, outpin); + pio_gpio_init(pio, outpin + 1); + pio_gpio_init(pio, outpin + 2); + pio_gpio_init(pio, outpin + 3); + + // No need to set INPINs; we need a JMP pin though + + // Set the JMP pin to the provided `jmppin` parameter. + sm_config_set_jmp_pin (&c, jmppin); + + // Set the OUT pin to the provided `outpin` parameter. This is where the data is sent out + sm_config_set_out_pins(&c, outpin, 4); + + sm_config_set_out_shift( + &c, + false, // Shift-to-right = false + false, // Autopull disabled + 32 // Doesn't matter in this case as autopull is disabled + ); + + // Load our configuration, and start the program from the beginning + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); +} +%} diff --git a/src/main.c b/src/main.c index 984f15c..a408fbc 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,8 @@ // include console specific handling #ifdef CONFIG_NGC #include "console/gamecube/gamecube.h" +#elif CONFIG_LOOPY +#include "console/loopy/loopy.h" #elif CONFIG_NUON #include "console/nuon/nuon.h" #elif CONFIG_PCE @@ -83,8 +85,10 @@ int main(void) board_init(); +#ifndef CONFIG_LOOPY // pause briefly for stability before starting activity sleep_ms(250); +#endif hid_app_init(); // init hid device interfaces @@ -97,6 +101,9 @@ int main(void) #ifdef CONFIG_NGC printf("GAMECUBE"); ngc_init(); +#elif CONFIG_LOOPY + printf("LOOPY"); + loopy_init(); #elif CONFIG_NUON printf("NUON"); nuon_init();