diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100755
index 0000000..2e6c3d8
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,135 @@
+####
+# USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+# MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+# phone or ASID supporting player
+#
+# CMakeLists.txt
+# This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+# File author: LouD
+#
+# Copyright (c) 2024 LouD
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+####
+cmake_minimum_required(VERSION 3.17)
+
+set(PROJECT_NAME usbsidpico) # projectname
+set(PROJECT_VERSION "0.0.1-ALPHA")
+set(CMAKE_BUILD_TYPE Debug) # build with debug symbols
+set(TINYUSB_PATH ${PICO_SDK_PATH}/lib/tinyusb)
+
+### Executables
+list(APPEND FILENAMES
+ ${PROJECT_NAME}-singlesid
+ ${PROJECT_NAME}-singleskpico
+ ${PROJECT_NAME}-dualsid
+ ${PROJECT_NAME}-skpico-singlesid
+ ${PROJECT_NAME}-dualskpico
+ ${PROJECT_NAME}-dualskpico-mixed)
+list(APPEND SIDTYPES 0 1 2 3 4 5)
+
+# default environment options
+set(SOURCEFILES
+ ${CMAKE_CURRENT_LIST_DIR}/src/usbsid.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/gpio.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/midi.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/asid.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/mcu.c
+ ${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c)
+# COMPILE_OPTS -Wall -Werror -Wno-maybe-uninitialized -save-temps -fverbose-asm)
+set(COMPILE_OPTS PRIVATE -Wno-maybe-uninitialized -fverbose-asm)
+set(TARGET_LL
+ pico_stdlib
+ pico_unique_id
+ pico_multicore
+ pico_stdio_usb
+ pico_usb_reset_interface
+ tinyusb_device
+ tinyusb_board
+ hardware_clocks
+ hardware_resets
+ hardware_uart
+ hardware_pwm
+ hardware_pio)
+set(PIO ${CMAKE_CURRENT_LIST_DIR}/src/clock.pio)
+set(PIO_RGB ${CMAKE_CURRENT_LIST_DIR}/src/ws2812.pio)
+set(TARGET_INCLUDE_DIRS PRIVATE
+ .
+ src
+ $ENV{PICO_SDK_PATH}/lib/tinyusb/hw
+ $ENV{PICO_SDK_PATH}/lib/tinyusb/src)
+
+# rp2040 sdk
+include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
+include($ENV{PICO_EXTRAS_PATH}/external/pico_extras_import.cmake)
+
+# project
+project(${PROJECT_NAME} C CXX ASM)
+
+# init sdk
+pico_sdk_init()
+
+include_directories(".")
+
+foreach(FILENAME SIDTYPE IN ZIP_LISTS FILENAMES SIDTYPES)
+ # set filename
+ set(BUILD ${FILENAME})
+ message(STATUS "Building ${FILENAME} with SIDTYPE=${SIDTYPE}")
+ # executable
+ add_executable(${BUILD} ${SOURCEFILES})
+ # pio addition
+ pico_generate_pio_header(${BUILD} ${PIO})
+ # source files to compile
+ target_sources(${BUILD} PUBLIC ${SOURCEFILES})
+ target_compile_options(${BUILD} ${COMPILE_OPTS})
+ target_compile_definitions(${BUILD} PRIVATE USBSID SIDTYPES=${SIDTYPE})
+ # tell the linker what libraries to link
+ target_link_libraries(${BUILD} ${TARGET_LL})
+ # target sid types
+ target_include_directories(${BUILD} ${TARGET_INCLUDE_DIRS})
+ pico_set_program_name(${BUILD} "USBSIDPico")
+ pico_set_program_version(${BUILD} $PROJECT_VERSION)
+ # create map/bin/hex/uf2 file in addition to ELF.
+ pico_add_extra_outputs(${BUILD})
+ # enable uart output, disable usb output
+ pico_enable_stdio_uart(${BUILD} 1)
+ pico_enable_stdio_usb(${BUILD} 0)
+endforeach()
+
+foreach(FILENAME SIDTYPE IN ZIP_LISTS FILENAMES SIDTYPES)
+ # override filename for rgb compile
+ set(FILENAME ${FILENAME}-rgb)
+ # set filename
+ set(BUILD ${FILENAME})
+ message(STATUS "Building ${FILENAME} with SIDTYPE=${SIDTYPE}")
+ # executable
+ add_executable(${BUILD} ${SOURCEFILES})
+ # pio addition
+ pico_generate_pio_header(${BUILD} ${PIO})
+ pico_generate_pio_header(${BUILD} ${PIO_RGB})
+ # source files to compile
+ target_sources(${BUILD} PUBLIC ${SOURCEFILES})
+ target_compile_options(${BUILD} ${COMPILE_OPTS})
+ target_compile_definitions(${BUILD} PRIVATE USBSID SIDTYPES=${SIDTYPE} USE_RGB=1)
+ # tell the linker what libraries to link
+ target_link_libraries(${BUILD} ${TARGET_LL})
+ # target sid types
+ target_include_directories(${BUILD} ${TARGET_INCLUDE_DIRS})
+ pico_set_program_name(${BUILD} "USBSIDPico")
+ pico_set_program_version(${BUILD} $PROJECT_VERSION)
+ # create map/bin/hex/uf2 file in addition to ELF.
+ pico_add_extra_outputs(${BUILD})
+ # enable uart output, disable usb output
+ pico_enable_stdio_uart(${BUILD} 1)
+ pico_enable_stdio_usb(${BUILD} 0)
+endforeach()
diff --git a/src/asid.c b/src/asid.c
new file mode 100644
index 0000000..9c4f575
--- /dev/null
+++ b/src/asid.c
@@ -0,0 +1,97 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * asid.c
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * TherapSID by Twisted Electrons: https://github.com/twistedelectrons/TherapSID
+ * TeensyROM by Sensorium Embedded: https://github.com/SensoriumEmbedded/TeensyROM
+ * SID Factory II by Chordian: https://github.com/Chordian/sidfactory2
+ *
+ * Any licensing conditions from either of the above named sources automatically
+ * apply to this code
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "midi.h"
+#include "asid.h"
+#include "usbsid.h"
+
+
+void handle_asid_message(uint8_t sid ,uint8_t* buffer, int size)
+{
+ (void)size; /* unused at the moment */
+
+ unsigned int reg = 0;
+ for (uint8_t mask = 0; mask < 4; mask++) { /* no more then 4 masks */
+ for (uint8_t bit = 0; bit < 7; bit++) { /* each packet has 7 bits ~ stoopid midi */
+ if (buffer[mask + 3] & (1 << bit)) { /* 3 byte message, skip 3 each round and add the bit */
+ uint8_t register_value = buffer[reg + 11]; /* get the value to write from the buffer */
+ if(buffer[mask + 7] & (1 << bit)) { /* if anything higher then 0 */
+ register_value |= 0x80; /* the register_value needs its 8th MSB bit */
+ }
+ uint8_t address = asid_sid_registers[mask * 7 + bit];
+ handle_asidbuffer_task((address |= sid), register_value);
+ reg++;
+ }
+ }
+ }
+}
+
+void decode_asid_message(uint8_t* buffer, int size)
+{
+ switch(buffer[2]) {
+ case 0x4C:
+ /* Play start */
+ midimachine.bus = CLAIMED;
+ break;
+ case 0x4D:
+ /* Play stop */
+ midimachine.bus = FREE;
+ break;
+ case 0x4F:
+ /* Display characters */
+ break;
+ case 0x4E:
+ handle_asid_message(0, buffer, size);
+ break;
+ case 0x50:
+ handle_asid_message(32, buffer, size);
+ break;
+ case 0x51:
+ handle_asid_message(64, buffer, size);
+ break;
+ case 0x60:
+ midimachine.fmopl = 1;
+ #if defined(SIDTYPE1)
+ /* Not implemented yet */
+ #endif
+ default:
+ break;
+ }
+}
+
+void process_sysex(uint8_t* buffer, int size) // NOTE: UNNESCESSARY OVERHEAD IF ASID ONLY
+{
+ if (buffer[1] == 0x2D) { /* 0x2D = ASID sysex message */
+ decode_asid_message(buffer, size);
+ } // room for other sysex protocols
+}
diff --git a/src/asid.h b/src/asid.h
new file mode 100644
index 0000000..9b2d3f5
--- /dev/null
+++ b/src/asid.h
@@ -0,0 +1,79 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * asid.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * TherapSID by Twisted Electrons: https://github.com/twistedelectrons/TherapSID
+ * TeensyROM by Sensorium Embedded: https://github.com/SensoriumEmbedded/TeensyROM
+ * SID Factory II by Chordian: https://github.com/Chordian/sidfactory2
+ *
+ * Any licensing conditions from either of the above named sources automatically
+ * apply to this code
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef _ASID_H_
+#define _ASID_H_
+#pragma once
+
+#include
+#include
+#include
+#include
+
+
+static const uint8_t asid_sid_registers[] =
+{
+ /* register, bit */
+ 0x00, // 0
+ 0x01, // 1
+ 0x02, // 2
+ 0x03, // 3
+ 0x05, // 4
+ 0x06, // 5
+ 0x07, // 6
+ 0x08, // 7
+ 0x09, // 8
+ 0x0a, // 9
+ 0x0c, // 10
+ 0x0d, // 11
+ 0x0e, // 12
+ 0x0f, // 13
+ 0x10, // 14
+ 0x11, // 15
+ 0x13, // 16
+ 0x14, // 17
+ 0x15, // 18
+ 0x16, // 19
+ 0x17, // 20
+ 0x18, // 21
+ 0x04, // 22
+ 0x0b, // 23
+ 0x12, // 24
+ 0x04, // 25 <= secondary for reg 04
+ 0x0b, // 26 <= secondary for reg 11
+ 0x12, // 27 <= secondary for reg 18
+};
+
+void process_sysex(uint8_t *buffer, int size);
+
+#endif /* _ASID_H_ */
diff --git a/src/clock.pio b/src/clock.pio
new file mode 100644
index 0000000..b8b3523
--- /dev/null
+++ b/src/clock.pio
@@ -0,0 +1,51 @@
+;
+; USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+; MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+; phone or ASID supporting player
+;
+; clock.pio
+; This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+; File author: LouD
+;
+; Source: https://forums.raspberrypi.com/viewtopic.php?t=355595#p2131828
+; Source: https://parthssharma.github.io/Pico/PIOSquareWave.html
+;
+; Any licensing conditions from either of the above named sources automatically
+; apply to this code
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, version 2.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see .
+;
+; Description:
+; Repeatedly get one word of data from the TX FIFO, stalling when the FIFO is
+; empty. Write the least significant bit to the OUT pin group.
+;
+.program clock
+
+.wrap_target ;Free 0 cycle unconditional jump
+set pins, 1 ;Drive pin high, binary 1 <--- 250 ns execution time with 4MHz clock
+set pins, 2 ;Drive pin low, binary 0 <--- 250 ns ;total 500 ns period
+.wrap
+
+
+%c-sdk{
+ /* Program to initialize the PIO */
+ static inline void clock_program_init(PIO pio, uint sm, uint offset, uint pin, float clk_div){
+ pio_sm_config c = clock_program_get_default_config(offset); /* Get default configurations for the PIO state machine */
+ sm_config_set_set_pins(&c, pin, 2); /* Set the state machine configurations on the given pin */
+ sm_config_set_clkdiv(&c, clk_div); /* Set the state machine clock divider */
+ pio_gpio_init(pio, pin); /* Setup the function select for a GPIO pin to use output from the given PIO instance */
+ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); /* Use a state machine to set the pin direction for one pins for the PIO instance */
+ pio_sm_init(pio, sm, offset, &c); /* Resets the state machine to a consistent state, and configures it */
+ pio_sm_set_enabled(pio, sm, true); /* Enable or disable a PIO state machine */
+ }
+%}
diff --git a/src/gpio.c b/src/gpio.c
new file mode 100644
index 0000000..043505d
--- /dev/null
+++ b/src/gpio.c
@@ -0,0 +1,180 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * gpio.c
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "usbsid.h"
+
+/* The following 2 lines (var naming changed) are copied from frenetic his SKPico code */
+register uint32_t b asm( "r10" );
+volatile const uint32_t *BUSState = &sio_hw->gpio_in;
+/* https://github.com/frntc/SIDKick-pico */
+
+void initPins(void)
+{
+ for ( int i = 0; i <= 21; i++ ) { /* init pin 0 -> 21 */
+ if (i != TX || i != RX) /* do not init pins 16 & 17 because uart */
+ goto init;
+ init:
+ gpio_set_dir(i, GPIO_OUT);
+ gpio_set_function(i, GPIO_FUNC_SIO);
+ gpio_put(i, 0);
+ }
+
+ /* RES,CS & RW HIGH */
+ gpio_put_masked(INIT_MASK, 0x3C0000); /* 0b111000000000000000000 */
+
+ /* PWM led */
+ gpio_init( BUILTIN_LED );
+ gpio_set_dir(BUILTIN_LED, GPIO_OUT);
+ gpio_set_function(BUILTIN_LED, GPIO_FUNC_PWM);
+ /* Init Vue */
+ int led_pin_slice = pwm_gpio_to_slice_num( BUILTIN_LED );
+ pwm_config configLED = pwm_get_default_config();
+ pwm_config_set_clkdiv( &configLED, 1 );
+ pwm_config_set_wrap( &configLED, 65535 ); /* LED max */
+ pwm_init( led_pin_slice, &configLED, true );
+ gpio_set_drive_strength( BUILTIN_LED, GPIO_DRIVE_STRENGTH_2MA );
+ pwm_set_gpio_level( BUILTIN_LED, 0 ); /* turn off led */
+
+ // read_uS = to_us_since_boot(get_absolute_time());
+ // write_uS = to_us_since_boot(get_absolute_time());
+
+ #if defined(USE_RGB)
+ /* Init RGB */
+ gpio_init( 23 );
+ // gpio_set_function(23, GPIO_FUNC_PWM);
+ gpio_set_dir(23, GPIO_OUT);
+ gpio_set_drive_strength( 23, GPIO_DRIVE_STRENGTH_2MA );
+ #endif
+
+}
+
+void resetPins(void)
+{
+ sio_hw->gpio_clr = BUS_PINMASK;
+ for ( int i = 0; i < 21; i++ ) {
+ gpio_deinit( i );
+ }
+}
+
+void clearBus(void)
+{
+ gpio_clr_mask(ADDRPINS_PINMASK | DATAPINS_PINMASK);
+}
+
+void pauseSID(void)
+{
+ gpio_put_masked(CS_MASK, (bPIN(CS) | bPIN(CS2)));
+}
+
+void resetSID(void)
+{
+ // gpio_put(CS, 1);
+ // gpio_put(RES, 0);
+ // gpio_put_masked(CSRW_MASK, (bPIN(CS) | bPIN(CS2)));
+
+ disableSID();
+ // gpio_put(RES, 0);
+ clearBus();
+ sleep_us(1);
+ enableSID();
+ // gpio_put(RES, 1);
+
+ // enableSID();
+ // gpio_put(RES, 1);
+ // gpio_put(CS, 0);
+ // gpio_put_masked(bPIN(RW), bPIN(RW));
+}
+
+void enableSID(void)
+{
+ // gpio_put(CS, 0);
+ gpio_put(RES, 1);
+ // gpio_put_masked((CSRW_MASK | bPIN(RES)), (bPIN(RES) | bPIN(CS) | bPIN(CS2)));
+ for (int i = 0; i < NUMSIDS; i++) {
+ writeSID(0xD400 | ((0x20 * i) + 0x18), 0x0F); /* Volume to 15 */
+ }
+}
+
+void disableSID(void)
+{
+ for (int i = 0; i < NUMSIDS; i++) {
+ writeSID(0xD400 | ((0x20 * i) + 0x18), 0x0); /* Volume to 0 */
+ }
+ gpio_put(CS, 1);
+ gpio_put(CS2, 1);
+ gpio_put(RES, 0);
+ // gpio_put_masked((CSRW_MASK | bPIN(RES)), (bPIN(CS) | bPIN(CS2)));
+}
+
+uint8_t readSID(uint16_t addr)
+{/* ISSUE: Reading the config tool from the SKPico misses data */
+ uint32_t cs = ((addr & 0xFF) >= 0x40) /* addr in range $D440+ ? */
+ ? bPIN(CS) /* SID1 disable, write CS1 1 HIGH */
+ : bPIN(CS2); /* SID2 disable, write CS2 1 HIGH */
+
+ #if SIDTYPES == SIDTYPE0
+ cs = 0; /* Override ChipSelect */
+ #endif
+
+ b = 0; /* empty g */
+ gpio_set_dir_masked(DATAPINS_PINMASK, 0); /* all datapins to input */
+
+ addr = (addr & ADDR_MASK);
+
+ WAIT_FOR_CPU_HALF_CYCLE /* QUESTION: DO WE ACTUALLY NEED THIS? ~ W/O THIS READING PRG DOESNT WORK PROPERLY */
+ // gpio_put(RW, 1); /* write to high */
+ uint16_t pinval = (addr << bADDR); /* addr pin value to write */
+ // gpio_put_masked(ADDRPINS_PINMASK, pinval); /* 0b0111111100000000 */
+ gpio_put_masked((CSRW_MASK | ADDRPINS_PINMASK), (bPIN(RW) | cs | pinval)); /* 0b0111111100000000 */
+ // gpio_put(cs, 0); /* chipselect low */
+ sleep_us(0.238); /* 238 nano seconds as per datasheet */
+ b = *BUSState; /* read complete bus */
+ gpio_put_masked((CSRW_MASK | ADDRPINS_PINMASK | DATAPINS_PINMASK), (bPIN(CS) | bPIN(CS2))); /* reset bus for writing */
+
+ gpio_set_dir_masked(DATAPINS_PINMASK, 0xFF); /* all datapins to output */
+
+ return (b & DATAPINS_PINMASK);
+}
+
+void writeSID(uint16_t addr, uint8_t val)
+{/* ISSUE: Notes are sometimes off !? */
+ uint32_t cs = ((addr & 0xFF) >= 0x40) /* addr in range $D440+ ? */
+ ? bPIN(CS) /* SID1 disable, write CS1 1 HIGH */
+ : bPIN(CS2); /* SID2 disable, write CS2 1 HIGH */
+
+ #if SIDTYPES == SIDTYPE0
+ cs = 0; /* Override ChipSelect */
+ #endif
+
+ addr = (addr & ADDR_MASK); /* create addr values */
+ val = (val & DATA_MASK); /* create data values */
+
+ uint32_t pinval = ((addr << bADDR) | val | cs); /* create pico pin values */
+
+ WAIT_FOR_VIC_HALF_CYCLE /* QUESTION: DO WE ACTUALLY NEED THIS? */
+ gpio_put_masked((CSRW_MASK | ADDRPINS_PINMASK | DATAPINS_PINMASK), pinval); /* write cs, rw, addr and data */
+ sleep_us(0.360); /* Sleep for exactly 360 ns ~ write pulse width + hold time */
+ gpio_put_masked(CSRW_MASK, (bPIN(RW) | bPIN(CS) | bPIN(CS2))); /* write cs and rw */
+}
diff --git a/src/gpio.h b/src/gpio.h
new file mode 100644
index 0000000..eca32f0
--- /dev/null
+++ b/src/gpio.h
@@ -0,0 +1,136 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * gpio.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef _SIDGPIO_H_
+#define _SIDGPIO_H_
+#pragma once
+
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include
+#include
+#include
+
+#include "pico/stdlib.h"
+#include "pico/types.h"
+#include "hardware/structs/sio.h" /* Pico SIO structs */
+#include "hardware/pwm.h" /* Hardware Pulse Width Modulation (PWM) API */
+
+
+/* Uart */
+#define TX 16 /* uart 0 tx */
+#define RX 17 /* uart 0 rx */
+
+/* IO pins */
+#define RES 18 /* Reset */
+#define RW 19 /* Read/ Write enable */
+#define CS 20 /* Chip Select for 1 or 1 & 2 with SKPico */
+#define CS2 21 /* Chip Select for 2 or 3 & 4 with SKPico */
+#define PHI 22 /* Pico 1Mhz PWM out ~ External Clock In */
+
+/* Address pins ~ output only */
+#define A0 8
+#define A1 9
+#define A2 10
+#define A3 11
+#define A4 12
+#define A5 13 /* $D420+ or FM on SKPico */
+#define A8 14 /* $D500. $DE00, $DF00 or external CS on SKPico */
+
+/* Data pins ~ output/input */
+#define D0 0
+#define D1 1
+#define D2 2
+#define D3 3
+#define D4 4
+#define D5 5
+#define D6 6
+#define D7 7
+
+/* Other */
+#define BUILTIN_LED PICO_DEFAULT_LED_PIN /* 25 */
+#define WS2812_PIN 23
+
+/* Unused */
+#define NIL1 15
+#define NIL2 26
+#define NIL3 27
+#define NIL4 28
+
+#define bPIN(i) ( 1 << i )
+#define bADDR 8
+#define bDATA 0
+
+#define BUS_PINMASK 0x3FFFFF /* 0b00000000001111111111111111111111 21 GPIO pins */
+#define ADDRPINS_PINMASK 0x7F00 /* 0b0111111100000000 */
+#define DATAPINS_PINMASK 0xFF /* 0b0000000011111111 */
+#define INIT_MASK (bPIN(RW) | bPIN(CS) | bPIN(CS2) | bPIN(RES))
+#define CS_MASK (bPIN(CS) | bPIN(CS2))
+#define RW_MASK bPIN(RW)
+#define CSRW_MASK (bPIN(RW) | bPIN(CS) | bPIN(CS2))
+
+#define A8_MASK 0x100 /* 0b100000000 */
+#define A5_MASK 0x20 /* 0b00100000 */
+
+#define ADDR_MASK (0x1F | A5_MASK) /* 0b00111111 */
+#define DATA_MASK 0xFF /* 0b11111111 */
+
+/* The following 4 lines are a direct copy (var naming changed) from frenetic his SKPico code */
+#define bPHI bPIN( PHI )
+#define VIC_HALF_CYCLE( b ) ( !( (b) & bPHI ) ) /* low */
+#define CPU_HALF_CYCLE( b ) ( ( (b) & bPHI ) ) /* high */
+#define WAIT_FOR_VIC_HALF_CYCLE { do { b = *BUSState; } while ( !( VIC_HALF_CYCLE( b ) ) ); } /* while !0 == Falling edge*/
+#define WAIT_FOR_CPU_HALF_CYCLE { do { b = *BUSState; } while ( !( CPU_HALF_CYCLE( b ) ) ); } /* while !1 == Rising edge*/
+/* https://github.com/frntc/SIDKick-pico */
+
+/* Pins 0->20 (except TX and RX) to sio and output */
+void initPins(void);
+/* Clears and de-inits all pins */
+void resetPins(void);
+/* Sets the address and databus pins to LOW */
+void clearBus(void);
+/* Pauses any operation by cetting CS high */
+void pauseSID(void);
+/* Reset the SID and clears the bus */
+void resetSID(void);
+/* Enable the SID by setting RES to HIGH */
+/* Enable the SID by setting RW to LOW and CS & RES to HIGH */
+void enableSID(void);
+/* Disable the SID by setting RES to LOW */
+/* Disable the SID by setting RW & RES to LOW and CS to HIGH*/
+void disableSID(void);
+/* Read val at SID addr hex value */
+uint8_t readSID(uint16_t addr);
+/* Write val to sid at addr hex value */
+void writeSID(uint16_t addr, uint8_t val);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _SIDGPIO_H_ */
diff --git a/src/mcu.c b/src/mcu.c
new file mode 100644
index 0000000..4d64ff3
--- /dev/null
+++ b/src/mcu.c
@@ -0,0 +1,61 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * mcu.c
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * BusPirate5 by DangerousPrototypes:
+ * https://github.com/DangerousPrototypes/BusPirate5-firmware/blob/main/pirate/mcu.c
+ *
+ * Any licensing conditions from the above named source automatically apply to this code
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include
+#include "pico/stdlib.h"
+#include "pico/unique_id.h"
+#include "hardware/watchdog.h"
+#include "pico/bootrom.h"
+#include "mcu.h"
+
+uint64_t mcu_get_unique_id(void)
+{
+ static_assert(sizeof(pico_unique_board_id_t) == sizeof(uint64_t), "pico_unique_board_id_t is not 64 bits (but is cast to uint64_t)");
+ pico_unique_board_id_t id;
+ pico_get_unique_board_id(&id);
+ return *((uint64_t*)(id.id));
+};
+
+void mcu_reset(void)
+{
+ watchdog_enable(1, 1);
+ while(1);
+}
+
+void mcu_jump_to_bootloader(void)
+{
+ /* \param usb_activity_gpio_pin_mask 0 No pins are used as per a cold boot. Otherwise a single bit set indicating which
+ * GPIO pin should be set to output and raised whenever there is mass storage activity
+ * from the host.
+ * \param disable_interface_mask value to control exposed interfaces
+ * - 0 To enable both interfaces (as per a cold boot)
+ * - 1 To disable the USB Mass Storage Interface
+ * - 2 To disable the USB PICOBOOT Interface
+ */
+ reset_usb_boot(0x00,0x00);
+}
diff --git a/src/mcu.h b/src/mcu.h
new file mode 100644
index 0000000..6855830
--- /dev/null
+++ b/src/mcu.h
@@ -0,0 +1,47 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * mcu.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * BusPirate5 by DangerousPrototypes:
+ * https://github.com/DangerousPrototypes/BusPirate5-firmware/blob/main/pirate/mcu.h
+ *
+ * Any licensing conditions from the above named source apply to this code
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#ifndef _MCU_H_
+#define _MCU_H_
+#pragma once
+#include "pico/stdlib.h"
+
+/* https://forums.raspberrypi.com/viewtopic.php?p=1928868#p1928868 */
+/* Reset rp2040 register */
+#define AIRCR_Register (*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)))
+/* Reset rp2040 using the reset register */
+#define RESET_PICO() AIRCR_Register = 0x5FA0004;
+
+/* Retrieve the rp2040 mcu unique id */
+uint64_t mcu_get_unique_id(void);
+/* Reset the rp2040 mcu */
+void mcu_reset(void);
+/* Jump to rp2040 mcu bootloader */
+void mcu_jump_to_bootloader(void);
+
+#endif /* _MCU_H_ */
diff --git a/src/midi.c b/src/midi.c
new file mode 100644
index 0000000..ebf7658
--- /dev/null
+++ b/src/midi.c
@@ -0,0 +1,306 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * midi.c
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * TherapSID by Twisted Electrons: https://github.com/twistedelectrons/TherapSID
+ * TeensyROM by Sensorium Embedded: https://github.com/SensoriumEmbedded/TeensyROM
+ * SID Factory II by Chordian: https://github.com/Chordian/sidfactory2
+ *
+ * Any licensing conditions from either of the above named sources automatically
+ * apply to this code
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#include "midi.h"
+
+
+clock_rates clock_rate = CLOCK_DEFAULT;
+hertz_values hertz = HZ_50;
+
+void midi_init(void)
+{
+ /* Clear buffers once */
+ memset(midimachine.readbuffer, 0, sizeof midimachine.readbuffer);
+ // memset(midimachine.packetbuffer, 0, sizeof midimachine.packetbuffer);
+ memset(midimachine.streambuffer, 0, sizeof midimachine.streambuffer);
+ memset(midimachine.sid_states, 0, sizeof midimachine.sid_states);
+ /* Initial state and index */
+ midimachine.bus = FREE;
+ midimachine.state = IDLE;
+ midimachine.index = 0;
+ // writeSID((addr | 0x04), ((midimachine.sid_states[0][4] & 0xFE) | 0x1));
+}
+
+void process_midi(uint8_t* buffer, int size)
+{
+ uint8_t note_index = buffer[1];
+ uint8_t note_velocity = buffer[2];
+ uint16_t frequency = musical_scale_values[note_index];
+ double a, b, c, d, e, f, g, h;
+ int freq, reg01;
+ uint16_t calculated_freq;
+
+ a = buffer[1];
+ b = a / 12;
+ // uint16_t calculated_freq = round(pow((16.35 * 2), (note_index / 12)));
+ c = pow(2, b);
+ d = 16.35 * c;
+ e = round(d);
+ freq = e;
+ f = pow(2, 24);
+ g = f / clock_rate;
+ h = freq * g;
+ reg01 = round(h);
+ calculated_freq = round(h);
+
+ uint8_t Flo, Fhi, Flo1, Fhi1;
+ Flo = (frequency & 0xFF);
+ Fhi = ((frequency >> 8) >= 0xFF ? 0xFF : (frequency >> 8));
+ Flo1 = (calculated_freq & 0xFF);
+ Fhi1 = ((calculated_freq >> 8) >= 0xFF ? 0xFF : (calculated_freq >> 8));
+
+ // writeSID(0xD418, 0xE);
+ int in, out, val, volume, add;
+ uint8_t mask, modevol_state, voice_state, keep_state;
+ uint16_t pulsewidth, pw_state;
+ add = (buffer[0] & 0xF);
+ addr = (0xD400 | (0x20 * add));
+
+ switch (buffer[0]) {
+ case 0x90 ... 0x93: // Note On ~ Voice 1
+ midimachine.sid_states[0][0] = Flo;
+ midimachine.sid_states[0][1] = Fhi;
+ /* ADSR */
+ // writeSID((addr | 0x05), 0x0); /* Attack / Decay */
+ // writeSID((addr | 0x06), 0xF0); /* Sustain / Release */
+ writeSID((addr | 0x18), midimachine.sid_states[0][24]); /* Volume */
+ writeSID((addr | 0x05), midimachine.sid_states[0][5]); /* Attack / Decay */
+ writeSID((addr | 0x06), midimachine.sid_states[0][6]); /* Sustain / Release */
+ writeSID((addr | 0x02), midimachine.sid_states[0][2]); /* PW LO */
+ writeSID((addr | 0x03), midimachine.sid_states[0][3]); /* PW HI */
+ /* Control */
+ midimachine.sid_states[0][4] = ((midimachine.sid_states[0][4] & 0xFE) | 0x1);
+ writeSID((addr | 0x04), midimachine.sid_states[0][4]);
+ /* writeSID((addr | 0x04), ((midimachine.sid_states[0][4] & 0xFE) | 0x1)); */
+ /* Note Lo & Hi */
+ writeSID((addr | 0x0), midimachine.sid_states[0][0]);
+ writeSID((addr | 0x01), midimachine.sid_states[0][1]);
+ break;
+ case 0x80 ... 0x83: // Note Off
+ /* Note Lo & Hi */
+ // writeSID((addr | 0x0), 0);
+ // writeSID((addr | 0x01), 0);
+ // /* Control */
+ // writeSID((addr | 0x04), 0x10);
+ midimachine.sid_states[0][4] = (midimachine.sid_states[0][4] & 0xFE);
+ writeSID((addr | 0x04), midimachine.sid_states[0][4]);
+ break;
+ case 0xB0 ... 0xB3:
+ switch (buffer[1]) {
+ case 0x07: /* Set Volume */
+ in = buffer[2];
+ volume = map(in, 0, 127, 0, 15);
+ mask = 0xF0; /* Inverted */
+ modevol_state = midimachine.sid_states[0][24];
+ keep_state = (modevol_state & mask);
+ val = (keep_state << 4) | volume;
+ midimachine.sid_states[0][24] = val;
+ addr |= 0x18;
+ // writeSID(addr, val);
+ break;
+ case 0x49: /* Attack */
+ in = buffer[2];
+ out = map(in, 0, 127, 0, 15);
+ mask = 0xF; /* Inverted */
+ voice_state = midimachine.sid_states[0][5];
+ keep_state = (voice_state & mask);
+ val = (out << 4) | keep_state;
+ midimachine.sid_states[0][5] = val;
+ addr |= 0x05;
+ // writeSID(addr, val);
+ break;
+ case 0x4B: /* Decay */
+ in = buffer[2];
+ out = map(in, 0, 127, 0, 15);
+ mask = 0xF0; /* Inverted */
+ voice_state = midimachine.sid_states[0][5];
+ keep_state = (voice_state & mask);
+ val = (keep_state | out);
+ midimachine.sid_states[0][5] = val;
+ // addr |= 0x05;
+ // writeSID(addr, val);
+ break;
+ case 0x40: /* Sustain */
+ in = buffer[2];
+ out = map(in, 0, 127, 0, 15);
+ mask = 0xF; /* Inverted */
+ voice_state = midimachine.sid_states[0][6];
+ keep_state = (voice_state & mask);
+ val = (out << 4) | keep_state;
+ midimachine.sid_states[0][6] = val;
+ addr |= 0x06;
+ // writeSID(addr, val);
+ break;
+ case 0x48: /* Release */
+ in = buffer[2];
+ out = map(in, 0, 127, 0, 15);
+ mask = 0xF0; /* Inverted */
+ voice_state = midimachine.sid_states[0][6];
+ keep_state = (voice_state & mask);
+ val = (keep_state | out);
+ midimachine.sid_states[0][6] = val;
+ addr |= 0x06;
+ // writeSID(addr, val);
+ break;
+ case 0x4C: /* Pulse Width */
+ in = buffer[2];
+ out = map(in, 0, 127, 0, 4095);
+ // mask = 0xF0; /* Inverted */
+ pw_state = (midimachine.sid_states[0][3] << 8 | midimachine.sid_states[0][2]);
+ // keep_state = (voice_state & mask);
+ addr |= 0x06;
+ val = (out >> 8);
+ midimachine.sid_states[0][3] = val;
+ val = (out & 0xF);
+ midimachine.sid_states[0][2] = val;
+ // writeSID(addr, val);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0xC0 ... 0xC3:
+ switch (buffer[1]) {
+ case 0x00:
+ /* Noise */
+ midimachine.sid_states[0][4] = ((midimachine.sid_states[0][4] & 0xF) | 0x80);
+ break;
+ case 0x01:
+ /* Pulse */
+ midimachine.sid_states[0][4] = ((midimachine.sid_states[0][4] & 0xF) | 0x40);
+ break;
+ case 0x02:
+ /* Sawtooth */
+ midimachine.sid_states[0][4] = ((midimachine.sid_states[0][4] & 0xF) | 0x20);
+ break;
+ case 0x03:
+ /* Triangle */
+ midimachine.sid_states[0][4] = ((midimachine.sid_states[0][4] & 0xF) | 0x10);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void process_buffer(uint8_t buffer)
+{
+ if (buffer & 0x80) {
+ switch (buffer) {
+ /* System Exclusive */
+ case 0xF0: // System Exclusive Start
+ if (midimachine.bus != CLAIMED) {
+ midimachine.type = SYSEX;
+ midimachine.state = RECEIVING;
+ midimachine.bus = CLAIMED;
+ midimachine.index = 0;
+ midimachine.streambuffer[midimachine.index] = buffer;
+ midimachine.index++;
+ }
+ break;
+ case 0xF7: // System Exclusive End of SysEx (EOX)
+ midimachine.streambuffer[midimachine.index] = buffer;
+ process_sysex(midimachine.streambuffer, midimachine.index);
+ midimachine.index = 0;
+ midimachine.state = IDLE;
+ midimachine.bus = FREE;
+ midimachine.type = NONE;
+ break;
+ case 0xF1: // System Exclusive MIDI Time Code Qtr. Frame
+ case 0xF2: // System Exclusive Song Position Pointer
+ case 0xF3: // System Exclusive Song Select (Song #)
+ case 0xF4: // System Exclusive Undefined (Reserved)
+ case 0xF5: // System Exclusive Undefined (Reserved)
+ case 0xF6: // System Exclusive Tune request
+ case 0xF8: // System Exclusive Timing clock
+ case 0xF9: // System Exclusive Undefined (Reserved)
+ case 0xFA: // System Exclusive Start
+ case 0xFB: // System Exclusive Continue
+ case 0xFC: // System Exclusive Stop
+ case 0xFD: // System Exclusive Undefined (Reserved)
+ case 0xFE: // System Exclusive Active Sensing
+ case 0xFF: // System Exclusive System Reset
+ break;
+
+ /* Midi */
+ // case 0x0 ... 0x7F: // Data Byte
+ // break;
+ case 0x80 ... 0x8F: // Note Off Event
+ case 0x90 ... 0x9F: // Note On Event
+ case 0xA0 ... 0xAF: // Polyphonic Key Pressure (Aftertouch)
+ case 0xB0 ... 0xBF: // Control/Mode Change
+ case 0xC0 ... 0xCF: // Program Phange
+ case 0xD0 ... 0xDF: // Channel Pressure (After-touch)
+ case 0xE0 ... 0xEF: // Pitch Bend Change
+ if (midimachine.bus != CLAIMED && midimachine.type == NONE) {
+ midimachine.type = MIDI;
+ midimachine.state = RECEIVING;
+ midimachine.bus = CLAIMED;
+ midimachine.index = 0;
+ midimachine.streambuffer[midimachine.index] = buffer;
+ midimachine.index++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ if (midimachine.state == RECEIVING) {
+ if (midimachine.index < sizeof(midimachine.streambuffer) / sizeof(*midimachine.streambuffer)) {
+ /* Add midi data to the buffer ~ SysEx & Midi */
+ midimachine.streambuffer[midimachine.index++] = buffer;
+ /* Handle 3 byte midi buffer */
+ if (midimachine.type == MIDI) {
+ if (midimachine.streambuffer[0] >= 0x80 || midimachine.streambuffer[0] <= 0xEF) {
+ if (midimachine.index == 3) {
+ process_midi(midimachine.streambuffer, midimachine.index);
+ midimachine.index = 0;
+ midimachine.state = IDLE;
+ midimachine.bus = FREE;
+ midimachine.type = NONE;
+ }
+ }
+ }
+ } else {
+ // Buffer is full, just wait for this message to be finished
+ midimachine.state = WAITING_FOR_END;
+ }
+ } else if (midimachine.state == WAITING_FOR_END) {
+ // We are getting data even though we are just waiting for sysex end to come
+ // So just consume data and do nothing...
+ }
+ }
+}
diff --git a/src/midi.h b/src/midi.h
new file mode 100644
index 0000000..5113191
--- /dev/null
+++ b/src/midi.h
@@ -0,0 +1,87 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * midi.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * The contents of this file are based upon and heavily inspired by the sourcecode from
+ * TherapSID by Twisted Electrons: https://github.com/twistedelectrons/TherapSID
+ * TeensyROM by Sensorium Embedded: https://github.com/SensoriumEmbedded/TeensyROM
+ * SID Factory II by Chordian: https://github.com/Chordian/sidfactory2
+ *
+ * Any licensing conditions from either of the above named sources automatically
+ * apply to this code
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#ifndef _MIDI_H_
+#define _MIDI_H_
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include "usbsid.h"
+
+typedef enum {
+ CLAIMED,
+ FREE
+} bus_state;
+
+typedef enum {
+ IDLE,
+ RECEIVING,
+ WAITING_FOR_END
+} sysex_state;
+
+typedef enum {
+ NONE,
+ MIDI,
+ SYSEX,
+ ASID
+} midi_type;
+
+typedef struct {
+ bus_state bus;
+ sysex_state state;
+ midi_type type;
+ uint8_t readbuffer[1];
+ // uint8_t packetbuffer[4]; // 20240723 ~ disabled, unused
+ uint8_t streambuffer[64]; /* Normal speed max buffer for TinyUSB */
+ uint8_t sid_states[4][29]; /* Stores states of each SID */
+ uint8_t sidaddress;
+ int fmopl;
+ unsigned int index;
+} midi_machine;
+
+extern midi_machine midimachine;
+
+/* Processes the 1 byte incoming midi buffer
+ * Figures out if we're receiving midi or sysex */
+void process_buffer(uint8_t buffer);
+
+/* Initialize the midi handlers */
+void midi_init(void);
+
+/* Processes the received Midi stream */
+// void process_stream(uint8_t* buffer); // 20240723 ~ disabled, unused
+
+#endif /* _MIDI_H_ */
diff --git a/src/sid.h b/src/sid.h
new file mode 100644
index 0000000..06b7bd8
--- /dev/null
+++ b/src/sid.h
@@ -0,0 +1,213 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * sid.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+#pragma once
+#ifndef SID_H_
+#define SID_H_
+
+#include
+
+typedef enum {
+ CLOCK_DEFAULT = 1000000,
+ CLOCK_PAL = 985248,
+ CLOCK_NTSC = 1022727,
+ CLOCK_DREAN = 1023440
+} clock_rates;
+
+typedef enum {
+ HZ_DEFAULT = 20000, // 50Hz ~ 20000 == 20us
+ HZ_50 = 19950, // 50Hz ~ 20000 == 20us / 50.125Hz ~ 19.950124688279us exact
+ HZ_60 = 16715 // 60Hz ~ 16667 == 16.67us / 59.826Hz ~ 16.715140574332 exact
+} hertz_values;
+
+typedef enum {
+ DEFAULT = 0xFF,
+ L_NIBBLE = 0xF0,
+ R_NIBBLE = 0x0F,
+ VOICE_FREQLO = DEFAULT,
+ VOICE_FREQHI = DEFAULT,
+} register_masks;
+
+/*
+ * Voice registers
+ *
+ * 0: Freq low
+ * 1: Freq high
+ * 2: Pulsewidth low
+ * 3: Pulsewidth high
+ * 4: Control registers
+ * 5: Attack/Decay
+ * 6: Sustain/Release
+ */
+static const uint8_t sid_registers[] =
+{
+ /* Voice 1 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ /* Voice 2 */
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ /* Voice 3 */
+ 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14,
+ /* Filter low */
+ 0x15,
+ /* Filter high */
+ 0x16,
+ /* Resonance & filter channels */
+ 0x17,
+ /* Volume */
+ 0x18,
+ /* PotX/PotY */
+ 0x19, 0x1A,
+ /* Oscillator Voice 3 */
+ 0x1B,
+ /* Envelope Voice 3 */
+ 0x1C
+};
+
+/* HEX DEC Attack(Time/Cycle) Decay(Time/Cycle) */
+static const uint8_t attack_decay[] =
+{
+ 0x0, // 0 2 mS 6 mS
+ 0x1, // 1 8 mS 24 mS
+ 0x2, // 2 16 mS 48 mS
+ 0x3, // 3 24 mS 72 mS
+ 0x4, // 4 38 mS 114 mS
+ 0x5, // 5 56 mS 168 mS
+ 0x6, // 6 68 mS 204 mS
+ 0x7, // 7 80 mS 240 mS
+ 0x8, // 8 100 mS 300 mS
+ 0x9, // 9 250 mS 750 mS
+ 0xA, // 10 500 mS 1.5 S
+ 0xB, // 11 800 mS 2.4 S
+ 0xC, // 12 1 S 3 S
+ 0xD, // 13 3 S 9 S
+ 0xE, // 14 5 S 15 S
+ 0xF, // 15 8 S 24 S
+};
+
+/*
+ * Osc Fn (Hex), No, Musical Note, Freq (Hz), Osc Fn (Decimal)
+ */
+static const uint32_t musical_scale_values[] =
+{
+ 0x0112, // 0 C0 16.35 274
+ 0x0123, // 1 C0$ 17.32 291
+ 0x0134, // 2 D0 18.35 308
+ 0x0146, // 3 D0$ 19.44 326
+ 0x015A, // 4 E0 20.60 346
+ 0x016E, // 5 F0 21.83 366
+ 0x0184, // 6 F0$ 23.12 388
+ 0x018B, // 7 G0 24.50 411
+ 0x01B3, // 8 G0$ 25.96 435
+ 0x01CD, // 9 A0 27.50 461
+ 0x01E9, // 10 A0$ 29.14 489
+ 0x0206, // 11 B0 30.87 518
+ 0x0225, // 12 C1 32.70 549
+ 0x0245, // 13 C1$ 34.65 581
+ 0x0268, // 14 D1 36.71 616
+ 0x028C, // 15 D1$ 38.89 652
+ 0x02B3, // 16 E1 41.20 691
+ 0x02DC, // 17 F1 43.65 732
+ 0x0308, // 18 F1$ 46.25 776
+ 0x0336, // 19 G1 49.00 822
+ 0x0367, // 20 G1$ 51.91 871
+ 0x039B, // 21 A1 55.00 923
+ 0x03D2, // 22 A1$ 58.27 978
+ 0x040C, // 23 B1 61.74 1036
+ 0x0449, // 24 C2 65.41 1097
+ 0x048B, // 25 C2$ 69.30 1163
+ 0x04D0, // 26 D2 73.42 1232
+ 0x0519, // 27 D2$ 77.78 1305
+ 0x0567, // 28 E2 82.41 1383
+ 0x05B9, // 29 F2 87.31 1465
+ 0x0610, // 30 F2$ 92.50 1552
+ 0x066C, // 31 G2 98.00 1644
+ 0x06CE, // 32 G2$ 103.83 1742
+ 0x0735, // 33 A2 110.00 1845
+ 0x07A3, // 34 A2$ 116.54 1955
+ 0x0817, // 35 B2 123.47 2071
+ 0x0893, // 36 C3 130.81 2195
+ 0x0915, // 37 C3$ 138.59 2325
+ 0x099F, // 38 D3 146.83 2463
+ 0x0A32, // 39 D3$ 155.56 2610
+ 0x0ACD, // 40 E3 164.81 2765
+ 0x0B72, // 41 F3 174.61 2930
+ 0x0C20, // 42 F3$ 185.00 3104
+ 0x0CD8, // 43 G3 196.00 3288
+ 0x0D9C, // 44 G3$ 207.65 3484
+ 0x0E6B, // 45 A3 220.00 3691
+ 0x0F46, // 46 A3$ 233.08 3910
+ 0x102F, // 47 B3 246.94 4143
+ 0x1125, // 48 C4 261.63 4389
+ 0x122A, // 49 C4$ 277.18 4650
+ 0x133F, // 50 D4 293.66 4927
+ 0x1464, // 51 D4$ 311.13 5220
+ 0x159A, // 52 E4 329.63 5530
+ 0x16E3, // 53 F4 349.23 5859
+ 0x183F, // 54 F4$ 370.00 6207
+ 0x1981, // 55 G4 392.00 6577
+ 0x1B38, // 56 G4$ 415.30 6968
+ 0x1CD6, // 57 A4 440.00 7382
+ 0x1E80, // 58 A4$ 466.16 7821
+ 0x205E, // 59 B4 493.88 8286
+ 0x224B, // 60 C5 523.25 8779
+ 0x2455, // 61 C5$ 554.37 9301
+ 0x267E, // 62 D5 587.33 9854
+ 0x28C8, // 63 D5$ 622.25 10440
+ 0x2B34, // 64 E5 659.25 11060
+ 0x2DC6, // 65 F5 698.46 11718
+ 0x307F, // 66 F5$ 740.00 12415
+ 0x3361, // 67 G5 783.99 13153
+ 0x366F, // 68 G5$ 830.61 13935
+ 0x39AC, // 69 A5 880.00 14764
+ 0x3D1A, // 70 A5$ 932.33 15642
+ 0x40BC, // 71 B5 987.77 16572
+ 0x4495, // 72 C6 1046.50 17557
+ 0x48A9, // 73 C6$ 1108.73 18601
+ 0x4CFC, // 74 D6 1174.66 19709
+ 0x518F, // 75 D6$ 1244.51 20897
+ 0x5669, // 76 E6 1381.51 22121
+ 0x5B8C, // 77 F6 1396.91 23436
+ 0x60FE, // 78 F6$ 1479.98 24830
+ 0x6602, // 79 G6 1567.98 26306
+ 0x6CDF, // 80 G6$ 1661.22 27871
+ 0x7358, // 81 A6 1760.00 29528
+ 0x7A34, // 82 A6$ 1864.65 31234
+ 0x8178, // 83 B6 1975.53 33144
+ 0x892B, // 84 C7 2093.00 35115
+ 0x9153, // 85 C7$ 2217.46 37203
+ 0x99F7, // 86 D7 2349.32 39415
+ 0xA31F, // 87 D7$ 2489.01 41759
+ 0xACD2, // 88 E7 2637.02 44242
+ 0xB719, // 89 F7 2793.83 46873
+ 0xC1FC, // 90 F7$ 2959.95 49660
+ 0xCD85, // 91 G7 3135.96 52613
+ 0xD9BD, // 92 G7$ 3322.44 55741
+ 0xE6B0, // 93 A7 3520.00 59056
+ 0xF467, // 94 A7$ 3729.31 62567
+ 0x102F0, // 95 B7 3951.06 66288
+};
+
+// static const uint16_t notes[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "H"};
+
+#endif /* SID_H_ */
diff --git a/src/tusb_config.h b/src/tusb_config.h
new file mode 100644
index 0000000..ffb5e59
--- /dev/null
+++ b/src/tusb_config.h
@@ -0,0 +1,132 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * tusb_config.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+#define CFG_TUSB_MCU OPT_MCU_RP2040
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED OPT_MODE_FULL_SPEED
+#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_TUD_MAX_SPEED) /* High speed with TinyUSB is not available for rp2040*/
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 1
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+#define CFG_TUD_TASK_QUEUE_SZ 64 /* WHAT DOES THIS BUTTON DO!? */
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 0
+#define CFG_TUD_HID 0
+#define CFG_TUD_MIDI 1
+#define CFG_TUD_VENDOR 1
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// CDC Endpoint transfer buffer size, more is faster
+#define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// MIDI FIFO size of TX and RX
+#define CFG_TUD_MIDI_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_MIDI_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// Vendor FIFO size of TX and RX
+// If not configured vendor endpoints will not be buffered
+#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
new file mode 100644
index 0000000..8ee7ce9
--- /dev/null
+++ b/src/usb_descriptors.c
@@ -0,0 +1,269 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * usb_descriptors.c
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include
+
+#include "pico/stdlib.h"
+#include "pico/unique_id.h"
+#include "tusb.h"
+#include "mcu.h"
+
+#include "usbsid.h"
+
+#define USB_PID 0x4011 /* USBSID uses TinyUSB default ~ 1x CDC + 1x MIDI (Vendor not included) */
+#define USB_VID 0xCAFE /* USBSID uses TinyUSB default - Previous versions used 0x5553 */
+#define USB_BCD 0x0210
+
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD, // at least 2.1 or 3.x for BOS
+
+ // Use Interface Association Descriptor (IAD) for CDC
+ // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100, /* Device release number in binary-coded decimal */
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void)
+{
+ return (uint8_t const *) &desc_device;
+}
+
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+enum
+{
+ ITF_NUM_CDC = 0,
+ ITF_NUM_CDC_DATA, /* This gets the descriptor of CDC */
+ ITF_NUM_MIDI,
+ ITF_NUM_MIDI_STREAMING, /* This has to be here, even if not used! */
+ ITF_NUM_VENDOR,
+ ITF_NUM_TOTAL
+};
+
+#define EPNUM_CDC_NOTIF 0x81
+#define EPNUM_CDC_OUT 0x02
+#define EPNUM_CDC_IN 0x82
+#define EPNUM_MIDI_OUT 0x03
+#define EPNUM_MIDI_IN 0x83
+#define EPNUM_VENDOR_OUT 0x04
+#define EPNUM_VENDOR_IN 0x84
+
+#define USBD_CDC_CMD_MAX_SIZE 8
+#define USBD_CDC_IN_OUT_MAX_SIZE 64
+#define USBD_MIDI_IN_OUT_MAX_SIZE 64
+#define USBD_VENDOR_IN_OUT_MAX_SIZE 64
+
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MIDI_DESC_LEN + TUD_VENDOR_DESC_LEN)
+
+// full speed configuration
+uint8_t const desc_fs_configuration[] =
+{
+ // Config number, interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 500),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, USBD_CDC_CMD_MAX_SIZE, EPNUM_CDC_OUT, EPNUM_CDC_IN, USBD_CDC_IN_OUT_MAX_SIZE),
+
+ // Interface number, string index, EP Out & EP In address, EP size
+ TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 5, EPNUM_MIDI_OUT, EPNUM_MIDI_IN, USBD_MIDI_IN_OUT_MAX_SIZE),
+
+ // Interface number, string index, EP Out & IN address, EP size
+ TUD_VENDOR_DESCRIPTOR(ITF_NUM_VENDOR, 6, EPNUM_VENDOR_OUT, EPNUM_VENDOR_IN, USBD_VENDOR_IN_OUT_MAX_SIZE),
+
+};
+
+
+#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
+#define MS_OS_20_DESC_LEN 0xB2
+
+// BOS Descriptor is required for webUSB
+uint8_t const desc_bos[] =
+{
+ // total length, number of device caps
+ TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
+
+ // Vendor Code, iLandingPage
+ TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
+
+ // Microsoft OS 2.0 descriptor
+ TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
+};
+
+uint8_t const * tud_descriptor_bos_cb(void)
+{
+ return desc_bos;
+}
+
+uint8_t const desc_ms_os_20[] =
+{
+ // Set header: length, type, windows version, total length
+ U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
+
+ // Configuration subset header: length, type, configuration index, reserved, configuration total length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
+
+ // Function Subset header: length, type, first interface, reserved, subset length
+ U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_VENDOR, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
+
+ // MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
+ U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
+
+ // MS OS 2.0 Registry property descriptor: length, type
+ U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
+ U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
+ 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
+ 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
+ U16_TO_U8S_LE(0x0050), // wPropertyDataLength
+ //bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
+ '{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00,
+ '0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00,
+ '8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00,
+ '8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
+
+extern uint8_t const desc_ms_os_20[];
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
+{
+ (void) index; // for multiple configurations
+
+ return desc_fs_configuration;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+
+char const *string_desc_arr[] =
+{
+ (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
+ "USBSID", // 1: Manufacturer
+ "USBSID-Pico", // 2: Product
+ "USES-MCU-ID", // 3: Serial, uses chip ID
+ "USBSID-Pico Data", // 4: CDC Interface
+ "USBSID-Pico Midi", // 5: Midi Interface
+ "USBSID-Pico WebUSB", // 6: WebUSB Vendor Interface
+};
+
+// automatically update if an additional string is later added to the table
+#define STRING_DESC_ARR_ELEMENT_COUNT (sizeof(string_desc_arr)/sizeof(string_desc_arr[0]))
+
+// temporary buffer returned from tud_descriptor_string_cb()
+// relies on only a single outstanding call to this function
+// until the buffer is no longer used by caller.
+// "contents must exist long enough for transfer to complete"
+// Safe because each string descriptor is a separate transfer,
+// and only one is handled at a time.
+// Use of "length" is ambiguous (byte count? element count?),
+// so use more explicit "BYTE_COUNT" or "ELEMENT_COUNT" instead.
+#define MAXIMUM_DESCRIPTOR_STRING_ELEMENT_COUNT 32
+static uint16_t _desc_str[MAXIMUM_DESCRIPTOR_STRING_ELEMENT_COUNT];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
+{
+ (void) langid;
+
+ uint8_t chr_count;
+
+ if ( index == 0)
+ {
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ } else if ( index == 3 ) { // special-case for USB Serial number
+ /* BOARD ID */
+ // 1x uint16_t for length/type
+ // 16x uint16_t to encode 64-bit value as hex (16x nibbles)
+ static_assert(MAXIMUM_DESCRIPTOR_STRING_ELEMENT_COUNT >= 17);
+ uint64_t unique_id = mcu_get_unique_id();
+ for ( uint_fast8_t t = 0; t < 16; t++ ) {
+ uint8_t nibble = (unique_id >> (t * 4u)) & 0xF;
+ _desc_str[16-t] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
+ }
+ chr_count = 16;
+ } else if ( index >= STRING_DESC_ARR_ELEMENT_COUNT ) { // if not in table, return NULL
+ return NULL;
+ } else
+ {
+ // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+ const char* str = string_desc_arr[index];
+
+ // Cap at max char
+ chr_count = (uint8_t) strlen(str);
+ if ( chr_count > MAXIMUM_DESCRIPTOR_STRING_ELEMENT_COUNT-1 ) chr_count = MAXIMUM_DESCRIPTOR_STRING_ELEMENT_COUNT-1;
+
+ // Convert ASCII string into UTF-16
+ for(uint8_t i=0; i.
+ *
+ */
+
+#include "usbsid.h"
+
+/*--------------------------------------------------------------------
+ * GLOBAL VARS / FUNCTION DECLARING
+ *--------------------------------------------------------------------*/
+
+uint8_t memory[65536];
+uint16_t vue;
+midi_machine midimachine;
+extern void midi_init(void);
+
+
+/*--------------------------------------------------------------------
+ * MAIN LOOPS
+ *--------------------------------------------------------------------*/
+
+void core1_main(void)
+{
+ if (detect_clock() == 0) {
+ gpio_deinit(PHI);
+ square_wave();
+ } else {
+ /* Do nothing gpio is input pulled low */
+ }
+
+ #if defined(USE_RGB)
+ if (detect_smps() == 1) {
+ init_rgb();
+ rgb_mode = true;
+ } else {
+ /* Do nothing gpio is input pulled low */
+ rgb_mode = false;
+ }
+ #endif
+
+ init_memory();
+
+ int x = 0;
+ while (1)
+ {
+ usbdata == 1 ? led_vuemeter_task() : led_breathe_task();
+ if (vue == 0) x++;
+ if (x >= 10) {
+ x = 0;
+ usbdata = 0;
+ }
+ }
+}
+
+int main()
+{
+ // set_sys_clock_pll(120 * MHZ,1,1); /* 120MHz ? */
+ // set_sys_clock_pll( 1500000000, 6, 2 ); /* 125MHz */
+ set_sys_clock_pll( 1500000000, 6, 2 ); /* 300MHz */
+ memset(buffer, 0, sizeof buffer); /* clear USB read buffer */
+ memset(data, 0, sizeof data); /* clear USB write buffer */
+
+ /* Init board */
+ board_init();
+ /* Init USB */
+ tud_init(BOARD_TUD_RHPORT);
+
+ /* Init SID */
+ initPins();
+
+ /* Init logging */
+ init_logging(); /* Initialize logging ~ placed after board_init as this resets something making the first print not print */
+
+ // multicore_reset_core1(); /* Reset core 1 before use */
+ multicore_launch_core1(core1_main); /* Init core 1 */
+
+ sleep_us(100); /* wait for core1 init */
+ resetSID(); /* reset the SID */
+ sleep_us(100); /* Give the SID time to boot */
+
+ midi_init();
+
+ /* Loop IO tasks forever */
+ while (1) {
+ if (tud_task_event_ready())
+ {
+ tud_task(); // tinyusb device task
+ read_task(); // cdc read task
+ webserial_read_task(); // webusb read task
+ midi_task(); // midi read task
+ // sysex_task(); // sysex read task
+ /* midi_task(); */ // Disabled for now
+ }
+ }
+}
+
+
+/*--------------------------------------------------------------------
+ * SETUP FUNCTIONS
+ *--------------------------------------------------------------------*/
+
+void init_logging(void)
+{
+ #if defined(USBSID_UART)
+ uint baud_rate = 115200;
+ int tx_pin = TX;
+ int rx_pin = RX;
+ stdio_uart_init_full(uart0,
+ baud_rate,
+ tx_pin,
+ rx_pin);
+ sleep_ms(100); /* leave time for uart to settle */
+ stdio_flush();
+ #endif
+}
+
+void init_memory(void)
+{
+ for (unsigned int i = 0; i < 65536; i++) {
+ memory[i] = 0x00; // fill with NOPs
+ }
+}
+
+void square_wave(void)
+{
+ PIO pio = pio0;
+ uint offset = pio_add_program(pio, &clock_program);
+ uint sm = pio_claim_unused_sm(pio, true);
+ // clock_program_init(pio, sm, offset, PHI, 30.0f); // 2.08Mhz @ GPIO22
+ // clock_program_init(pio, sm, offset, PHI, 60.0f); // 1.04Mhz with clock at 125Mhz and 300Mhz @ GPIO22
+ clock_program_init(pio, sm, offset, PHI, 62.5f); // 1.00Mhz with clock at 125Mhz and 300Mhz @ GPIO22
+}
+
+int detect_clock(void)
+{
+ uint32_t b;
+ extern uint32_t *BUSState;
+ int c = 0, r = 0;
+ gpio_init(PHI);
+ gpio_set_pulls(PHI, false, true);
+ for (int i = 0; i < 20; i++)
+ {
+ extern uint32_t *BUSState;
+ b = *BUSState; /* read complete bus */
+ r |= c = (b & bPIN(PHI)) >> PHI;
+ }
+ return r; /* 1 if clock detected */
+}
+
+#if defined(USE_RGB)
+void init_rgb(void)
+{
+ // ws2812_sm = initProgramWS2812();
+ // pio_sm_put(pio1, ws2812_sm, 0xffffff);
+ // pio_sm_put( pio1, ws2812_sm, 0 );
+ r_ = g_ = b_ = 0;
+
+ PIO pio = pio1;
+ ws2812_sm = pio_claim_unused_sm(pio, true);
+ uint offset = pio_add_program(pio, &ws2812_program);
+ ws2812_program_init(pio, ws2812_sm, offset, WS2812_PIN, 800000, IS_RGBW);
+ put_pixel(urgb_u32(0,0,0));
+}
+
+int detect_smps(void)
+{ // TODO: FINISH!
+ rgb_mode = false;
+ return !(gpio_get(24));
+}
+#endif
+
+
+/*--------------------------------------------------------------------
+ * BUFFER TASKS
+ *--------------------------------------------------------------------*/
+
+void handle_buffer_task(unsigned char buff[4])
+{
+ // pause = ((buff[0] & 0x80) >> 7);
+ // reset = ((buff[0] & 0x40) >> 6);
+ // rw = ((buff[0] & 0x10) >> 4);
+ addr = ((buff[1] << 8) | buff[2]);
+ val = (buff[3] & 0xFF);
+ memory[addr] = val;
+
+
+ /* Set address for SID no# */
+ addr = sid_address(addr);
+
+
+ switch (buff[0])
+ {
+ // if (pause == 1)
+ // {
+ case PAUSE:
+ pauseSID();
+ break;
+ // }
+ // if (reset == 1)
+ // { /* ignore everything else if reset is true */
+ case RESET_SID:
+ resetSID();
+ break;
+ // }
+ // switch (rw)
+ // {
+ case RESET_USB:
+ RESET_PICO();
+ break;
+ case READ:
+ memset(data, 0, sizeof data); /* clear write buffer */
+ memset(rdata, 0, sizeof rdata); /* clear read buffer */
+ rdata[0] = readSID(addr); /* read the SID datapins */
+ for (int i = 0; i < sizeof(data); i++)
+ {
+ // data[i] = readSID(addr); /* assign the result to teach of the 4 bytes */
+ data[i] = rdata[0]; /* assign the result to teach of the 4 bytes */
+ }
+ /* write the result to the USB client */
+ if (dtype == cdc) {
+ write_task();
+ } else if (dtype == wusb) {
+ webserial_write_task();
+ }
+ break;
+ case WRITE:
+ writeSID(addr, val); /* write the address and value to the SID */
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+void handle_asidbuffer_task(uint8_t a, uint8_t d)
+{
+ dtype = asid;
+ addr = (0xD400 | a);
+ val = d;
+ memory[addr] = val;
+ addr = sid_address(addr);
+ writeSID(addr, val);
+ return;
+}
+
+
+/*--------------------------------------------------------------------
+ * IO TASKS
+ *--------------------------------------------------------------------*/
+
+void read_task(void)
+{
+ if (tud_cdc_connected()) {
+ while (tud_cdc_available()) { /* NOTICE: If disabled this breaks the Pico USB availability */
+ usbdata = 1;
+ read = tud_cdc_read(buffer, sizeof(buffer)); /* Read data from client */
+ tud_cdc_read_flush();
+ if (read == 4) { /* 4 as in exactly 4 bytes */
+ dtype = cdc;
+ handle_buffer_task(buffer);
+ }
+ memset(buffer, 0, sizeof buffer); /* clear read buffer */
+ }
+ }
+}
+
+void write_task(void)
+{
+ if (tud_cdc_connected()) {
+ if (tud_cdc_write_available()) { /* NOTICE: Still testing */ // <- this uses IF instead of WHILE on purpose to avoid a write back loop
+ write = tud_cdc_write(data, sizeof(data)); /* write data to client */
+ // write = tud_cdc_write(rxdata, sizeof(rxdata)); /* write data to client */
+ tud_cdc_write_flush();
+ memset(data, 0, sizeof data); /* clear write buffer */
+ }
+ }
+}
+
+void webserial_read_task(void)
+{
+ if ( web_serial_connected )
+ {
+ while ( tud_vendor_available() ) {
+ usbdata = 1;
+ read = tud_vendor_read(buffer, sizeof(buffer));
+ tud_vendor_read_flush();
+ if (read == 4) { /* 4 as in exactly 4 bytes */
+ // NOTE: This 4 byte minimum renders any webserial test useless
+ dtype = wusb;
+ handle_buffer_task(buffer);
+ }
+ memset(buffer, 0, sizeof buffer); /* clear read buffer */
+ }
+ }
+}
+
+void webserial_write_task(void)
+{
+ if (web_serial_connected) {
+ // if (tud_vendor_available()) {
+ if (tud_vendor_mounted()) {
+ write = tud_vendor_write(data, sizeof(data));
+ tud_vendor_flush();
+ memset(data, 0, sizeof data); /* clear write buffer */
+ }
+ }
+}
+
+void midi_task(void)
+{
+ while ( tud_midi_n_available(0, 0) ) {
+ usbdata = 1;
+ // tud_midi_packet_read(sysexMachine.packetbuffer); /* WORKS ~ PACKET IS 4 BYTES */
+ // tud_midi_stream_read(midimachine.readbuffer, 1); /* WORKS ~ Handles 2 Byte at a time */
+ // memset(midimachine.streambuffer, 0, sizeof midimachine.streambuffer);
+
+ /* Working stream config */
+ // int s = tud_midi_stream_read(midimachine.streambuffer, sizeof(midimachine.streambuffer)); /* WORKS ~ STREAM IS WHAT EVER THE MAX USB BUFFER SIZE IS! */
+ // process_stream(midimachine.streambuffer);
+
+
+ /* Working config */
+ int s = tud_midi_n_stream_read(0, 0, midimachine.readbuffer, 1); /* WORKS ~ Handles 1 Byte at a time */
+ process_buffer(midimachine.readbuffer[0]);
+ }
+}
+
+// void midi_task(void)
+// {
+// while ( tud_midi_n_available(1, 0) ) {
+// usbdata = 1;
+// /* Working packet config */
+// int p = tud_midi_n_packet_read(1, midimachine.packetbuffer); /* WORKS ~ PACKET IS 4 BYTES */
+
+// }
+// }
+
+
+/*--------------------------------------------------------------------
+ * LED TASKS
+ *--------------------------------------------------------------------*/
+
+void led_vuemeter_task(void)
+{
+ uint8_t osc1, osc2, osc3, osc4, osc5, osc6;
+ osc1 = (memory[0xD400] * 0.596); /* Frequency in Hz of SID1 @ $D400 Oscillator 1 */
+ osc2 = (memory[0xD407] * 0.596); /* Frequency in Hz of SID1 @ $D400 Oscillator 2 */
+ osc3 = (memory[0xD40E] * 0.596); /* Frequency in Hz of SID1 @ $D400 Oscillator 3 */
+ #if defined(USE_RGB)
+ osc4 = (memory[0xD420] * 0.596); /* Frequency in Hz of SID2 @ $D400 Oscillator 1 */
+ osc5 = (memory[0xD427] * 0.596); /* Frequency in Hz of SID2 @ $D400 Oscillator 2 */
+ osc6 = (memory[0xD42E] * 0.596); /* Frequency in Hz of SID2 @ $D400 Oscillator 3 */
+ #endif
+
+ vue = abs((osc1 + osc2 + osc3) / 3.0f);
+ vue = map(vue, 0, HZ_MAX, 0, VUE_MAX);
+
+ pwm_set_gpio_level(BUILTIN_LED, vue);
+
+ #if defined(USE_RGB)
+ if (rgb_mode) { // TODO: r_, g_ and b_ should be the modulo of the additions
+ r_ = 0, g_ = 0, b_ = 0, o1 = 0, o2 = 0, o3 = 0, o4 = 0, o5 = 0, o6 = 0;
+ o1 = map(osc1, 0, 255, 0, 43);
+ o2 = map(osc2, 0, 255, 0, 43);
+ o3 = map(osc3, 0, 255, 0, 43);
+ o4 = map(osc4, 0, 255, 0, 43);
+ o5 = map(osc5, 0, 255, 0, 43);
+ o6 = map(osc6, 0, 255, 0, 43);
+ r_ += color_LUT[o1][0][0] + color_LUT[o2][1][0] + color_LUT[o3][2][0] + color_LUT[o4][3][0] + color_LUT[o5][4][0] + color_LUT[o6][5][0] ;
+ g_ += color_LUT[o1][0][1] + color_LUT[o2][1][1] + color_LUT[o3][2][1] + color_LUT[o4][3][1] + color_LUT[o5][4][1] + color_LUT[o6][5][1] ;
+ b_ += color_LUT[o1][0][2] + color_LUT[o2][1][2] + color_LUT[o3][2][2] + color_LUT[o4][3][2] + color_LUT[o5][4][2] + color_LUT[o6][5][2] ;
+ put_pixel(urgb_u32(rgbb(r_), rgbb(g_), rgbb(b_)));
+ }
+ #endif
+}
+
+void led_breathe_task(void)
+{
+ static uint32_t start_ms = 0;
+
+ if ( board_millis() - start_ms < breathe_interval_ms) return; /* not enough time */
+ start_ms += breathe_interval_ms;
+
+ if (pwm_value >= VUE_MAX)
+ updown = 0;
+ if (pwm_value <= 0)
+ updown = 1;
+
+ if (updown == 1 && pwm_value < VUE_MAX)
+ pwm_value += BREATHE_STEP;
+
+ if (updown == 0 && pwm_value > 0)
+ pwm_value -= BREATHE_STEP;
+ pwm_set_gpio_level(BUILTIN_LED, pwm_value);
+ #if defined(USE_RGB)
+ if (rgb_mode) put_pixel(urgb_u32(0,0,0));
+ #endif
+}
+
+
+/*--------------------------------------------------------------------
+ * SUPPORTING FUNCTIOMS
+ *--------------------------------------------------------------------*/
+
+uint16_t sid_address(uint16_t addr)
+{ /* Set address for SID no# */
+ /* D500, DE00 or DF00 is the second sid in SIDTYPE1, 3 & 4 */
+ /* D500, DE00 or DF00 is the third sid in all other SIDTYPES */
+ if (((addr) & SIDUMASK) != SID1ADDR) { /* Not in $D400 range but in D5, DE or DF */
+ switch (SIDTYPES) {
+ case SIDTYPE1:
+ case SIDTYPE2:
+ addr = (SID2ADDR | (addr & SID1MASK));
+ break;
+ case SIDTYPE3:
+ addr = (SID3ADDR | (addr & SID1MASK));
+ break;
+ case SIDTYPE4:
+ addr = (SID3ADDR | (addr & SID2MASK));
+ break;
+ case SIDTYPE5: /* UNTESTED */
+ addr = (SID1ADDR | (addr & SIDUMASK));
+ }
+ } else { /* $D400 */
+ switch (SIDTYPES) {
+ case SIDTYPE1:
+ case SIDTYPE3:
+ case SIDTYPE4:
+ addr; /* $D400 -> $D479 */
+ break;
+ case SIDTYPE2:
+ addr = ((addr & SIDLMASK) >= 0x20) ? (SID2ADDR | (addr & SID1MASK)) : addr ; /* */
+ break;
+ case SIDTYPE5:
+ addr =
+ ((addr & SIDLMASK) >= 0x20) && ((addr & SIDLMASK) <= 0x39) /* $D420~$D439 -> $D440~$D459 */
+ ? (addr + 0x20)
+ : ((addr & SIDLMASK) >= 0x40) && ((addr & SIDLMASK) <= 0x59) /* $D440~$D459 -> $D420~$D439 */
+ ? (addr - 0x20) : addr;
+ break;
+ }
+ }
+ return addr;
+}
+
+long map(long x, long in_min, long in_max, long out_min, long out_max)
+{
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+}
+
+#if defined(USE_RGB)
+void put_pixel(uint32_t pixel_grb) {
+ pio_sm_put_blocking(pio1, ws2812_sm, pixel_grb << 8u);
+}
+
+uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b) {
+ return ((uint32_t) (r) << 8) | ((uint32_t) (g) << 16) | (uint32_t) (b);
+}
+
+uint8_t rgbb(double inp)
+{
+ int r = abs((inp / 255) * BRIGHTNESS);
+ return (uint8_t)r;
+};
+#endif
+
+
+/*--------------------------------------------------------------------
+ * DEVICE CALLBACKS
+ *--------------------------------------------------------------------*/
+
+void tud_mount_cb(void)
+{
+ // NOTE: Disabled for now, causes dual volume write with a CLICK sound on PWM
+ // enableSID(); /* NOTICE: Testing if this is causing the random lockups */
+}
+
+void tud_umount_cb(void)
+{
+ // NOTE: Disabled for now, causes dual volume write with a CLICK sound on PWM
+ // disableSID(); /* NOTICE: Testing if this is causing the random lockups */
+}
+
+void tud_suspend_cb(bool remote_wakeup_en)
+{
+ (void) remote_wakeup_en;
+ tud_mounted() ? enableSID() : disableSID(); /* NOTICE: Testing if this is causing the random lockups */
+}
+
+void tud_resume_cb(void)
+{
+ tud_mounted() ? enableSID() : disableSID(); /* NOTICE: Testing if this is causing the random lockups */
+}
+
+
+/*--------------------------------------------------------------------
+ * CDC CALLBACKS
+ *--------------------------------------------------------------------*/
+
+void tud_cdc_rx_cb(uint8_t itf)
+{
+ (void)itf;
+}
+
+void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char)
+{
+ (void)itf;
+ (void)wanted_char;
+}
+
+void tud_cdc_tx_complete_cb(uint8_t itf)
+{
+ (void)itf;
+}
+
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
+{
+ (void) itf;
+ (void) rts;
+
+ if ( dtr ) {
+ /* Terminal connected */
+ usbdata = 1;
+ }
+ else
+ {
+ /* Terminal disconnected */
+ breathe_interval_ms = BREATHE_ON;
+ usbdata = 0; /* ISSUE: Led breather is not invoked again */
+ }
+}
+
+void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
+{
+ (void)itf;
+ (void)p_line_coding;
+}
+
+void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms)
+{
+ (void)itf;
+ (void)duration_ms;
+}
+
+
+/*--------------------------------------------------------------------
+ * WEBUSB VENDOR CLASS CALLBACKS
+ *--------------------------------------------------------------------*/
+
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+ // nothing to with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
+ switch (request->bmRequestType_bit.type) {
+ case TUSB_REQ_TYPE_VENDOR:
+ switch (request->bRequest) {
+ case VENDOR_REQUEST_WEBUSB:
+ // match vendor request in BOS descriptor
+ // Get landing page url
+ // return tud_control_xfer(rhport, request, (void*)(uintptr_t) &desc_url, desc_url.bLength); /* Do we want this? */
+ return tud_control_status(rhport, request);
+ case VENDOR_REQUEST_MICROSOFT:
+ if ( request->wIndex == 7 ) {
+ // Get Microsoft OS 2.0 compatible descriptor
+ uint16_t total_len;
+ memcpy(&total_len, desc_ms_os_20+8, 2);
+
+ return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, total_len);
+ // return true;
+ } else {
+ return false;
+ }
+ default: break;
+ }
+ break;
+ case TUSB_REQ_TYPE_CLASS:
+ if (request->bRequest == 0x22) {
+ // Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to connect and disconnect.
+ web_serial_connected = (request->wValue != 0);
+
+ // Always lit LED if connected
+ if (web_serial_connected) {
+ // board_led_write(true);
+ // blink_interval_ms = BLINK_ALWAYS_ON;
+
+ /* tud_vendor_write_str("\r\nWebUSB interface connected\r\n"); */
+ // tud_vendor_write_flush();
+ usbdata = 1;
+ } else {
+ // blink_interval_ms = BLINK_MOUNTED;
+ }
+ // response with status OK
+ return tud_control_status(rhport, request);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // stall unknown request
+ return false;
+}
diff --git a/src/usbsid.h b/src/usbsid.h
new file mode 100644
index 0000000..5517da5
--- /dev/null
+++ b/src/usbsid.h
@@ -0,0 +1,461 @@
+/*
+ * USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+ * MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+ * phone or ASID supporting player
+ *
+ * usbsid.h
+ * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+ * File author: LouD
+ *
+ * Copyright (c) 2024 LouD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#ifndef _USBSID_H_
+#define _USBSID_H_
+#pragma once
+
+
+#include
+#include
+#include
+#include
+
+#include "pico/stdlib.h"
+#include "pico/types.h" /* absolute_time_t */
+// #define __STDC_FORMAT_MACROS
+// #include
+#include "pico/multicore.h" /* Multicore */
+#include "hardware/pio.h" /* Programmable I/O (PIO) API */
+#include "hardware/clocks.h"
+// #include "hardware/irq.h" /* Hardware interrupt handling */
+// #include "hardware/adc.h" /* Analog to Digital Converter (ADC) API */
+
+#include "bsp/board.h" /* Tiny USB Boards Abstraction Layer */
+#include "tusb.h" /* Tiny USB stack */
+#include "tusb_config.h" /* Tiny USB configuration */
+
+#include "clock.pio.h" /* Square wave generator */
+#if defined(USE_RGB)
+#include "ws2812.pio.h" /* RGB led handler */
+#endif
+
+/* Debugging enabling overrides */
+/* #define USBSID_UART */
+/* #define MEM_DEBUG */
+/* #define USBSID_DEBUG */
+/* #define ADDR_DEBUG */
+/* #define USBIO_DEBUG */
+/* #define ASID_DEBUG */
+/* #define USBSIDGPIO_DEBUG */
+
+#include "mcu.h" /* rp2040 mcu */
+#include "gpio.h" /* GPIO configuration */
+#include "midi.h" /* Midi */
+#include "asid.h" /* ASID */
+#include "sid.h" /* SID */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+/* SIDTYPES ~ Defaults to 0
+ *
+ * 0: Single SID config
+ * SID socket 1 filled with either of
+ * - MOS6581/MOS6582/MOS8580
+ * - SIDKickPico
+ * - SwinSID
+ * - any other single SID hardware emulator
+ * - NOTE: Will use the second SID socket simultaniously
+ * 1: Single SID config (dual optional with 1x SIDKickPico)
+ * SID socket 1 filled with either of
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * - MOS6581/MOS6582/MOS8580
+ * - SwinSID
+ * - any other single SID hardware emulator
+ * 2: Dual SID config
+ * SID sockets 1 & 2 filled with either of
+ * - MOS6581/MOS6582/MOS8580
+ * - SIDKickPico
+ * - SwinSID
+ * - any other single SID hardware emulator
+ * 3: Triple SID config with 1x SIDKickPico and other
+ * SID socket 1 filled with a
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * SID socket 2 filled with either of
+ * - MOS6581/MOS6582/MOS8580
+ * - SIDKickPico
+ * - SwinSID
+ * - any other single SID hardware emulator
+ * 4: Quad SID config with 2x SIDKickPico
+ * SID socket 1 filled with a
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * SID socket 2 filled with a
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * 5: Quad SID mixed config with 2x SIDKickPico
+ * SID socket 1 filled with a
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * - Acts as SID 1 & 3
+ * SID socket 2 filled with a
+ * - SIDKickPico (A5 connected to A5 for dual SID)
+ * - Acts as SID 2 & 4
+ *
+ */
+#define SIDTYPE0 0
+#define SIDTYPE1 1
+#define SIDTYPE2 2
+#define SIDTYPE3 3
+#define SIDTYPE4 4
+#define SIDTYPE5 5
+
+/* Default config for SID type */
+#ifndef SIDTYPES
+#define SIDTYPES SIDTYPE0
+#endif
+
+/* SID type masks for GPIO */
+#define SIDUMASK 0xFF00
+#define SIDLMASK 0xFF
+#if SIDTYPES == SIDTYPE0
+#define NUMSIDS 1
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0x0
+#define SID2MASK 0x0
+#define SID3ADDR 0x0
+#define SID3MASK 0x0
+#define SID4ADDR 0x0
+#define SID4MASK 0x0
+#elif SIDTYPES == SIDTYPE1
+#define NUMSIDS 2
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0xD420 /* SID 2 - socket 1 ~ OPTIONAL */
+#define SID2MASK 0x3F
+#define SID3ADDR 0x0
+#define SID3MASK 0x0
+#define SID4ADDR 0x0
+#define SID4MASK 0x0
+#elif SIDTYPES == SIDTYPE2
+#define NUMSIDS 2
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0xD440 /* SID 2 - socket 2 */
+#define SID2MASK 0x5F
+#define SID3ADDR 0x0
+#define SID3MASK 0x0
+#define SID4ADDR 0x0
+#define SID4MASK 0x0
+#elif SIDTYPES == SIDTYPE3
+#define NUMSIDS 3
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0xD420 /* SID 2 - socket 1 */
+#define SID2MASK 0x3F
+#define SID3ADDR 0xD440 /* SID 3 - socket 2 */
+#define SID3MASK 0x5F
+#define SID4ADDR 0x0
+#define SID4MASK 0x0
+#elif SIDTYPES == SIDTYPE4 /* SKPico only */
+#define NUMSIDS 4
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0xD420 /* SID 2 - socket 1 */
+#define SID2MASK 0x3F
+#define SID3ADDR 0xD440 /* SID 3 - socket 2 */
+#define SID3MASK 0x5F
+#define SID4ADDR 0xD460 /* SID 4 - socket 2 */
+#define SID4MASK 0x7F
+#elif SIDTYPES == SIDTYPE5 /* SKPico only */
+#define NUMSIDS 4
+#define SID1ADDR 0xD400 /* SID 1 - socket 1 */
+#define SID1MASK 0x1F
+#define SID2ADDR 0xD440 /* SID 2 - socket 2 */
+#define SID2MASK 0x5F
+#define SID3ADDR 0xD420 /* SID 3 - socket 1 */
+#define SID3MASK 0x3F
+#define SID4ADDR 0xD460 /* SID 4 - socket 2 */
+#define SID4MASK 0x7F
+#endif
+
+
+/* Global vars */
+
+enum
+{
+ WRITE = 0,
+ READ,
+ PAUSE,
+ RESET_SID,
+ RESET_USB
+};
+
+enum /* LED breathe levels */
+{
+ ALWAYS_OFF = 99999,
+ ALWAYS_ON = 0,
+ BREATHE_ON = 1,
+ BREATHE_STEP = 100,
+ VUE_MAX = 65534,
+ HZ_MAX = 40
+};
+
+extern uint8_t memory[65536];
+extern uint16_t vue;
+static uint32_t breathe_interval_ms = BREATHE_ON;
+static uint32_t read = 0, write = 0;
+static intptr_t usbdata = 0, pwm_value = 0, updown = 1;
+static uintptr_t pause, reset, rw, addr, val;
+static char dtype = 'E', cdc = 'C', asid = 'A', midi = 'M', wusb = 'W';
+
+// static uint64_t read_uS, write_uS;
+
+#if defined(USE_RGB) /* RGB LED */
+
+#define IS_RGBW false
+#define BRIGHTNESS 127 /* Half of max brightness */
+
+static bool rgb_mode;
+static uint ws2812_sm;
+static uint8_t r_ = 0, g_ = 0, b_ = 0;
+static unsigned char o1 = 0, o2 = 0, o3 = 0, o4 = 0, o5 = 0, o6 = 0;
+static const __not_in_flash( "mydata" ) unsigned char color_LUT[ 43 ][ 6 ][ 3 ] =
+{
+ /* Red Green Blue Yellow Cyan Purple*/
+ {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
+ {{6, 0, 0}, {0, 6, 0}, {0, 0, 6}, {6, 6, 0}, {0, 6, 6}, {6, 0, 6}},
+ {{12, 0, 0}, {0, 12, 0}, {0, 0, 12}, {12, 12, 0}, {0, 12, 12}, {12, 0, 12}},
+ {{18, 0, 0}, {0, 18, 0}, {0, 0, 18}, {18, 18, 0}, {0, 18, 18}, {18, 0, 18}},
+ {{24, 0, 0}, {0, 24, 0}, {0, 0, 24}, {24, 24, 0}, {0, 24, 24}, {24, 0, 24}},
+ {{30, 0, 0}, {0, 30, 0}, {0, 0, 30}, {30, 30, 0}, {0, 30, 30}, {30, 0, 30}},
+ {{36, 0, 0}, {0, 36, 0}, {0, 0, 36}, {36, 36, 0}, {0, 36, 36}, {36, 0, 36}},
+ {{42, 0, 0}, {0, 42, 0}, {0, 0, 42}, {42, 42, 0}, {0, 42, 42}, {42, 0, 42}},
+ {{48, 0, 0}, {0, 48, 0}, {0, 0, 48}, {48, 48, 0}, {0, 48, 48}, {48, 0, 48}},
+ {{54, 0, 0}, {0, 54, 0}, {0, 0, 54}, {54, 54, 0}, {0, 54, 54}, {54, 0, 54}},
+ {{60, 0, 0}, {0, 60, 0}, {0, 0, 60}, {60, 60, 0}, {0, 60, 60}, {60, 0, 60}},
+ {{66, 0, 0}, {0, 66, 0}, {0, 0, 66}, {66, 66, 0}, {0, 66, 66}, {66, 0, 66}},
+ {{72, 0, 0}, {0, 72, 0}, {0, 0, 72}, {72, 72, 0}, {0, 72, 72}, {72, 0, 72}},
+ {{78, 0, 0}, {0, 78, 0}, {0, 0, 78}, {78, 78, 0}, {0, 78, 78}, {78, 0, 78}},
+ {{84, 0, 0}, {0, 84, 0}, {0, 0, 84}, {84, 84, 0}, {0, 84, 84}, {84, 0, 84}},
+ {{90, 0, 0}, {0, 90, 0}, {0, 0, 90}, {90, 90, 0}, {0, 90, 90}, {90, 0, 90}},
+ {{96, 0, 0}, {0, 96, 0}, {0, 0, 96}, {96, 96, 0}, {0, 96, 96}, {96, 0, 96}},
+ {{102, 0, 0}, {0, 102, 0}, {0, 0, 102}, {102, 102, 0}, {0, 102, 102}, {102, 0, 102}},
+ {{108, 0, 0}, {0, 108, 0}, {0, 0, 108}, {108, 108, 0}, {0, 108, 108}, {108, 0, 108}},
+ {{114, 0, 0}, {0, 114, 0}, {0, 0, 114}, {114, 114, 0}, {0, 114, 114}, {114, 0, 114}},
+ {{120, 0, 0}, {0, 120, 0}, {0, 0, 120}, {120, 120, 0}, {0, 120, 120}, {120, 0, 120}},
+ {{126, 0, 0}, {0, 126, 0}, {0, 0, 126}, {126, 126, 0}, {0, 126, 126}, {126, 0, 126}},
+ {{132, 0, 0}, {0, 132, 0}, {0, 0, 132}, {132, 132, 0}, {0, 132, 132}, {132, 0, 132}},
+ {{138, 0, 0}, {0, 138, 0}, {0, 0, 138}, {138, 138, 0}, {0, 138, 138}, {138, 0, 138}},
+ {{144, 0, 0}, {0, 144, 0}, {0, 0, 144}, {144, 144, 0}, {0, 144, 144}, {144, 0, 144}},
+ {{150, 0, 0}, {0, 150, 0}, {0, 0, 150}, {150, 150, 0}, {0, 150, 150}, {150, 0, 150}},
+ {{156, 0, 0}, {0, 156, 0}, {0, 0, 156}, {156, 156, 0}, {0, 156, 156}, {156, 0, 156}},
+ {{162, 0, 0}, {0, 162, 0}, {0, 0, 162}, {162, 162, 0}, {0, 162, 162}, {162, 0, 162}},
+ {{168, 0, 0}, {0, 168, 0}, {0, 0, 168}, {168, 168, 0}, {0, 168, 168}, {168, 0, 168}},
+ {{174, 0, 0}, {0, 174, 0}, {0, 0, 174}, {174, 174, 0}, {0, 174, 174}, {174, 0, 174}},
+ {{180, 0, 0}, {0, 180, 0}, {0, 0, 180}, {180, 180, 0}, {0, 180, 180}, {180, 0, 180}},
+ {{186, 0, 0}, {0, 186, 0}, {0, 0, 186}, {186, 186, 0}, {0, 186, 186}, {186, 0, 186}},
+ {{192, 0, 0}, {0, 192, 0}, {0, 0, 192}, {192, 192, 0}, {0, 192, 192}, {192, 0, 192}},
+ {{198, 0, 0}, {0, 198, 0}, {0, 0, 198}, {198, 198, 0}, {0, 198, 198}, {198, 0, 198}},
+ {{204, 0, 0}, {0, 204, 0}, {0, 0, 204}, {204, 204, 0}, {0, 204, 204}, {204, 0, 204}},
+ {{210, 0, 0}, {0, 210, 0}, {0, 0, 210}, {210, 210, 0}, {0, 210, 210}, {210, 0, 210}},
+ {{216, 0, 0}, {0, 216, 0}, {0, 0, 216}, {216, 216, 0}, {0, 216, 216}, {216, 0, 216}},
+ {{222, 0, 0}, {0, 222, 0}, {0, 0, 222}, {222, 222, 0}, {0, 222, 222}, {222, 0, 222}},
+ {{228, 0, 0}, {0, 228, 0}, {0, 0, 228}, {228, 228, 0}, {0, 228, 228}, {228, 0, 228}},
+ {{234, 0, 0}, {0, 234, 0}, {0, 0, 234}, {234, 234, 0}, {0, 234, 234}, {234, 0, 234}},
+ {{240, 0, 0}, {0, 240, 0}, {0, 0, 240}, {240, 240, 0}, {0, 240, 240}, {240, 0, 240}},
+ {{246, 0, 0}, {0, 246, 0}, {0, 0, 246}, {246, 246, 0}, {0, 246, 246}, {246, 0, 246}},
+ {{252, 0, 0}, {0, 252, 0}, {0, 0, 252}, {252, 252, 0}, {0, 252, 252}, {252, 0, 252}}
+};
+#endif
+
+
+/* WebUSB Vars */
+
+enum
+{
+ VENDOR_REQUEST_WEBUSB = 1,
+ VENDOR_REQUEST_MICROSOFT = 2
+};
+
+extern uint8_t const desc_ms_os_20[];
+
+#define URL "github.com/LouDnl/USBSID-Pico"
+
+// extern const tusb_desc_webusb_url_t desc_url;
+
+static const tusb_desc_webusb_url_t desc_url =
+{
+ .bLength = 3 + sizeof(URL) - 1,
+ .bDescriptorType = 3, // WEBUSB URL type
+ .bScheme = 1, // 0: http, 1: https
+ .url = URL
+};
+
+static bool web_serial_connected = false;
+
+
+/* Buffers */
+
+/* Incoming USB data buffer
+ *
+ * 4 bytes
+ * Byte 0 ~ command byte
+ * Byte 1 ~ address range byte
+ * Byte 2 ~ address destination byte
+ * Byte 3 ~ data byte
+ *
+ * 4 byte build up MSB -> LSB:
+`* Byte 0 : see enum above
+ * Byte 1 : address range e.g. $D4
+ * Byte 2 : address e.g. $1C
+ * Byte 3 : value to write e.g. $FF
+ *
+ * Example MSB -> LSB:
+ * 0x00D4181A
+ * Byte 0 0x10 ~ 0b00010000 no reset, write mode
+ * Byte 1 0xD4 ~ address range $D400 ~ $DFFF
+ * Byte 2 0x18 ~ address
+ * Byte 3 0x1A ~ value to write
+*/
+static unsigned char buffer[4];
+
+/* Outgoing USB data buffer
+ *
+ * 4 bytes:
+ * byte 0 : value to return
+ * byte 1~3 : copies of value to return
+ */
+static unsigned char data[4];
+
+/* SID read data buffer
+ *
+ * Exactly 1 byte with data pin values:
+ * byte 0 : value read from the SID
+ */
+static unsigned char rdata[1];
+
+
+/* Setup */
+
+/* Initialize debug logging if enabled
+ * WARNING: any debug logging other then SID memory
+ * _WILL_ slow down SID play
+ */
+void init_logging(void);
+/* Create fake 16bit memory filled with nops */
+void init_memory(void);
+/* Init 1MHz square wave output */
+void square_wave(void);
+/* Detect clock signal */
+int detect_clock(void);
+#if defined(USE_RGB)
+/* Init the RGB LED pio */
+void init_rgb(void);
+/* Experimental function for detecting the SMPS
+ * type on GPIO23/GPIO24 */
+int detect_smps(void);
+#endif
+
+
+/* Buffer tasks */
+
+/* Handle the received data */
+void handle_buffer_task(unsigned char buff[4]);
+/* Handle the received Sysex ASID data */
+void handle_asidbuffer_task(uint8_t a, uint8_t d);
+
+
+/* IO Tasks */
+
+/* Read from host to device */
+void read_task(void);
+/* Write from device to host */
+void write_task(void);
+/* Read from host to device */
+void webserial_read_task(void);
+/* Write from device to host */
+void webserial_write_task(void);
+/* Handle incoming midi data */
+void midi_task(void);
+
+
+/* LED tasks */
+
+/* It goes up and it goes down */
+void led_vuemeter_task(void);
+/* Mouth breather! */
+void led_breathe_task(void);
+
+
+/* Supporting functions */
+
+/* Address conversion */
+uint16_t sid_address(uint16_t addr);
+/* Map n in range x to range y
+ * Source: https://stackoverflow.com/a/61000581 */
+long map(long x, long in_min, long in_max, long out_min, long out_max);
+#if defined(USE_RGB)
+/* Write a single RGB pixel */
+void put_pixel(uint32_t pixel_grb);
+/* Generate uint32_t with RGB colors */
+uint32_t urgb_u32(uint8_t r, uint8_t g, uint8_t b);
+/* Generate RGB brightness */
+uint8_t rgbb(double inp);
+#endif
+
+
+/* Device callbacks */
+
+/* Invoked when device is mounted */
+void tud_mount_cb(void);
+/* Invoked when device is unmounted */
+void tud_umount_cb(void);
+/* Invoked when usb bus is resumed */
+void tud_suspend_cb(bool remote_wakeup_en);
+/* Invoked when usb bus is suspended
+ * remote_wakeup_en : if host allow us to perform remote wakeup
+ * Within 7ms, device must draw an average of current less than 2.5 mA from bus
+ */
+void tud_resume_cb(void);
+
+
+/* CDC callbacks */
+
+/* Invoked when received new data */
+void tud_cdc_rx_cb(uint8_t itf);
+/* Invoked when received `wanted_char` */
+void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char);
+/* Invoked when last rx transfer finished */
+void tud_cdc_tx_complete_cb(uint8_t itf);
+/* Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE */
+void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts);
+/* Invoked when line coding is change via SET_LINE_CODING */
+void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding);
+/* Invoked when received send break */
+void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
+
+
+/* WebUSB Vendor callbacks */
+
+/* Invoked when received control request with VENDOR TYPE */
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _USBSID_H_ */
diff --git a/src/ws2812.pio b/src/ws2812.pio
new file mode 100644
index 0000000..747da5a
--- /dev/null
+++ b/src/ws2812.pio
@@ -0,0 +1,71 @@
+;
+; USBSID-Pico is a RPi Pico (RP2040) based board for interfacing one or two
+; MOS SID chips and/or hardware SID emulators over (WEB)USB with your computer,
+; phone or ASID supporting player
+;
+; ws2812.pio
+; This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico)
+; File author: LouD
+;
+; Source: https://github.com/raspberrypi/pico-examples/blob/master/pio/ws2812/ws2812.pio
+;
+; Any licensing conditions from the above named source automatically apply to this code
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, version 2.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see .
+;
+; Description:
+; Repeatedly get one word of data from the TX FIFO, stalling when the FIFO is
+; empty. Write the least significant bit to the OUT pin group.
+;
+.program ws2812
+.side_set 1
+
+.define public T1 3
+.define public T2 3
+.define public T3 4
+
+.lang_opt python sideset_init = pico.PIO.OUT_HIGH
+.lang_opt python out_init = pico.PIO.OUT_HIGH
+.lang_opt python out_shiftdir = 1
+
+.wrap_target
+bitloop:
+ out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls
+ jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse
+do_one:
+ jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse
+do_zero:
+ nop side 0 [T2 - 1] ; Or drive low, for a short pulse
+.wrap
+
+% c-sdk {
+#include "hardware/clocks.h"
+
+static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) {
+
+ pio_gpio_init(pio, pin);
+ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
+
+ pio_sm_config c = ws2812_program_get_default_config(offset);
+ sm_config_set_sideset_pins(&c, pin);
+ sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24);
+ sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
+
+ int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3;
+ float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
+ sm_config_set_clkdiv(&c, div);
+
+ pio_sm_init(pio, sm, offset, &c);
+ pio_sm_set_enabled(pio, sm, true);
+}
+%}