Skip to content

Instantly share code, notes, and snippets.

@tschak909
Created July 26, 2025 15:06
Show Gist options
  • Select an option

  • Save tschak909/9951e1a9b58e61216b7e0b3a1ce31190 to your computer and use it in GitHub Desktop.

Select an option

Save tschak909/9951e1a9b58e61216b7e0b3a1ce31190 to your computer and use it in GitHub Desktop.
RP2040 C code for decoding CP-1600 bus signals.
// Receives 19 bits: DB0–DB15 + BDIR + BC2 + BC1
.program cp1600_bus_rx
.define PINS_TO_SAMPLE 19 ; # of pins to sample.
.wrap_target
wait 1 pin 16 ; Wait for BDIR == 1 (CPU driving bus)
nop [31] ; 32 cycles - wait for bus to settle.
in pins, PINS_TO_SAMPLE ; Read pins
in null, 13
push block
wait 0 pin 16 ; Wait for BDIR == 0 (CPU released bus)
.wrap
// Transmits 16 bits onto DB0–15 during DTB
.program cp1600_bus_tx
.wrap_target
wait 0 pin 16 ; BDIR == 0
wait 1 pin 17 ; BC2 == 1
wait 1 pin 18 ; BC1 == 1 => DTB
out pins, 16 ; Output data onto DB0–15
.wrap
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/vreg.h"
#include "bus_decode.pio.h"
#include "cart.h"
// Constants and Macros ///////////////////////////////////////
#define PIN_DATA_BASE 0 // DB0–DB15 = GPIO 0–15
#define PIN_BDIR 16
#define PIN_BC2 17
#define PIN_BC1 18
#define PIN_CTRL_BASE 16 // control pins start at GPIO 16
#define PINS_TO_SAMPLE 19 // also change in bus_decode.pio !
#define SYS_CLOCK_KHZ 250000 // 250 MHz for overclocking
#define CLOCK_DIVIDER 1.0f // No clock divider
// Bus State Functions ///////////////////////////////////////
void bus_nact(uint16_t data) {
}
void bus_bar(uint16_t data) {
// Handle BAR phase
}
void bus_iab(uint16_t data)
{
// Handle IAB phase
}
void bus_dws(uint16_t data)
{
// Handle DWS phase
}
void bus_adar(uint16_t data)
{
// Handle ADAR phase
}
void bus_dw(uint16_t data)
{
// Handle DW phase
}
void bus_dtb(uint16_t data)
{
// Handle DTB phase
}
void bus_intak(uint16_t data)
{
// Handle INTAK phase
}
/**
* Dispatch function for bus phases.
* Each function corresponds to a bus phase.
*/
void (*bus_dispatch_by_state[8])(uint16_t) =
{
bus_nact, // 0: NACT
bus_bar, // 1: BAR
bus_iab, // 2: IAB
bus_dws, // 3: DWS
bus_adar, // 4: ADAR
bus_dw, // 5: DW
bus_dtb, // 6: DTB
bus_intak // 7: INTAK
};
// Configuration Functions ///////////////////////////////////////
void overclock() {
set_sys_clock_khz(SYS_CLOCK_KHZ, true);
vreg_set_voltage(VREG_VOLTAGE_1_30);
}
void init_cp1600_pio(PIO pio, uint sm_rx, uint sm_tx) {
// --- Load programs ---
uint offset_rx = pio_add_program(pio, &cp1600_bus_rx_program);
uint offset_tx = pio_add_program(pio, &cp1600_bus_tx_program);
// --- RX SM (read from CP-1600) ---
pio_sm_config c_rx = cp1600_bus_rx_program_get_default_config(offset_rx);
sm_config_set_in_pins(&c_rx, PIN_DATA_BASE);
sm_config_set_clkdiv(&c_rx, CLOCK_DIVIDER); // Full speed
sm_config_set_in_shift(&c_rx, false, false, PINS_TO_SAMPLE);
pio_sm_init(pio, sm_rx, offset_rx, &c_rx);
pio_sm_set_enabled(pio, sm_rx, true);
// --- TX SM (write to CP-1600) ---
pio_sm_config c_tx = cp1600_bus_tx_program_get_default_config(offset_tx);
sm_config_set_out_pins(&c_tx, PIN_DATA_BASE, 16);
sm_config_set_clkdiv(&c_tx, CLOCK_DIVIDER);
sm_config_set_out_shift(&c_tx, false, true, 16); // Auto-pull 16 bits
pio_sm_init(pio, sm_tx, offset_tx, &c_tx);
pio_sm_set_enabled(pio, sm_tx, true);
}
void init_cp1600_pins() {
for (int i = 0; i <= 18; i++) {
gpio_init(i);
gpio_set_dir(i, GPIO_IN); // Default to input
gpio_pull_down(i); // Safe default
}
// Data lines (0–15) will become output only when TX active
}
// Bus Reading and Writing Functions ///////////////////////////////////////
// Reading from RX FIFO (BAR, DW, etc.)
bool read_cp1600(PIO pio, uint sm_rx, uint32_t* value) {
if (!pio_sm_is_rx_fifo_empty(pio, sm_rx)) {
*value = pio_sm_get(pio, sm_rx);
return true;
}
return false;
}
// Writing to TX FIFO (to respond to DTB phase)
bool write_cp1600(PIO pio, uint sm_tx, uint16_t data) {
if (!pio_sm_is_tx_fifo_full(pio, sm_tx)) {
pio_sm_put(pio, sm_tx, data);
return true;
}
return false;
}
// Main Function /////////////////////////////////////////////////////////////
int main() {
overclock();
stdio_init_all();
init_cp1600_pins();
PIO pio = pio0;
uint sm_rx = 0, sm_tx = 1;
init_cp1600_pio(pio, sm_rx, sm_tx);
while (1) {
union _bus_value {
uint32_t raw;
struct {
uint16_t data;
uint8_t phase;
uint8_t reserved; // Padding for alignment
};
} busval;
if (read_cp1600(pio, sm_rx, &busval.raw)) {
// Dispatch to the appropriate bus phase handler
if (busval.phase < 8) {
bus_dispatch_by_state[busval.phase](busval.data);
} else {
printf("Unknown bus phase: %d\n", busval.phase);
}
} else {
// No data available, continue waiting
continue;
}
// Optionally respond with data during DTB phase
write_cp1600(pio, sm_tx, 0xDEAD);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment