diff --git a/CMakeLists.txt b/CMakeLists.txt index b934d02..f204b38 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,10 @@ cmake_minimum_required(VERSION 3.17) ### Project set(PROJECT_NAME usbsidpico) -set(PROJECT_VERSION "0.2.1-BETA.20241109") # Must be the same as in config.h -set(MAGIC_SMOKE "20241109") # DATEOFRELEASE +set(PROJECT_MANUFACTURER "LouD") +set(PROJECT_PRODUCT "USBSID-Pico") +set(MAGIC_SMOKE "20241117") # DATEOFRELEASE +set(PROJECT_VERSION "0.2.2-BETA.${MAGIC_SMOKE}") # Must be the same as in config.h ### Optimization and debugging SET(DEBUG_SYMBOLS 0) @@ -37,19 +39,32 @@ SET(OPTIMIZATIONS 3) ### Enable debug logging # NOTICE: ENABLING THESE DEBUGGING DEFINITIONS WILL HAVE SIGNIFICANT IMPACT AND WILL DELAY PLAYING! -set(USBSID_DEBUGGING 1) # Enable UART ~ mandatory enable for all other logging types -set(MEMORY_LOGGING 0) # Enable memory map of SID 1 voices printing -set(DEFAULT_DEBUGGING 1) # Enable debugging in usbsid.c -set(USBIO_DEBUGGING 0) # Enable debugging in usbsid.c -set(CONFIG_DEBUGGING 1) # Enable debugging in config.c -set(GPIOBUS_DEBUGGING 0) # Enable debugging in gpio.c -set(MIDI_DEBUGGING 0) # Enable debugging in midi.c -set(MIDIVOICE_DEBUGGING 0) # Enable debugging in midi.c +if(NOT DEFINED $ENV{DISABLE_DEBUGGING}) # MATCHES + set(USBSID_DEBUGGING 1) # Enable UART ~ mandatory enable for all other logging types + set(MEMORY_LOGGING 0) # Enable memory map of SID 1 voices printing + set(DEFAULT_DEBUGGING 1) # Enable debugging in usbsid.c + set(USBIO_DEBUGGING 0) # Enable debugging in usbsid.c + set(CONFIG_DEBUGGING 1) # Enable debugging in config.c + set(GPIOBUS_DEBUGGING 0) # Enable debugging in gpio.c + set(MIDI_DEBUGGING 0) # Enable debugging in midi.c +endif() #### You no touchy after this line ### Target board +message("ENV Pico board: $ENV{PICO_BOARD}") +message("ENV Pico platform: $ENV{PICO_PLATFORM}") +if(NOT DEFINED $ENV{PICO_BOARD}) + message("PICO_BOARD not defined, defaulting to 'pico'") + set(PICO_BOARD pico) + # set(PICO_BOARD pico2) +endif() +if(NOT DEFINED $ENV{PICO_PLATFORM}) + message("PICO_PLATFORM not defined, defaulting to 'rp2040'") + set(PICO_PLATFORM rp2040) + # set(PICO_PLATFORM rp2350) +endif() # set(PICO_PLATFORM rp2350-arm-s) # set(PICO_BOARD pico2) @@ -65,6 +80,12 @@ if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/src/config.h) if(${VERSION} MATCHES ${PROJECT_VERSION}) set(REDUCTION_SUCCESS True) message("VALIDATED config version ${VERSION} matches project version \"${PROJECT_VERSION}\"") + set(USBSID_PRODUCT ${PROJECT_PRODUCT}) + set(USBSID_MANUFACTURER "${PROJECT_MANUFACTURER} (v${PROJECT_VERSION})") + add_compile_definitions(USBSID_PRODUCT="${USBSID_PRODUCT}") + add_compile_definitions(USBSID_MANUFACTURER="${USBSID_MANUFACTURER}") + message("Set USB manufacturer: ${USBSID_MANUFACTURER}") + message("Set USB product: ${USBSID_PRODUCT}") break() else() message("ERROR config version ${VERSION} does not match project version \"${PROJECT_VERSION}\"") @@ -84,7 +105,9 @@ set(CMAKE_CXX_STANDARD 17) ### Compilers to use for C and C++ # set(CMAKE_C_COMPILER arm-none-eabi-gcc) # set(CMAKE_CXX_COMPILER arm-none-eabi-g++) -set(PICO_COMPILER pico_arm_gcc) +if(${PICO_PLATFORM} MATCHES rp2040) + set(PICO_COMPILER pico_arm_gcc) # required for mem_ops on rp2040 +endif() ### Target environment path set(CMAKE_FIND_ROOT_PATH $ENV{PICO_ENV_PATH}) @@ -165,6 +188,7 @@ set(SOURCEFILES ${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/sid.c ${CMAKE_CURRENT_LIST_DIR}/src/mcu.c ${CMAKE_CURRENT_LIST_DIR}/src/usb_descriptors.c ) @@ -206,9 +230,9 @@ set(TARGET_INCLUDE_DIRS PRIVATE ) ### Pico sdk path -include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake) +# include($ENV{PICO_SDK_PATH}/pico_sdk_init.cmake) include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) -include($ENV{PICO_EXTRAS_PATH}/external/pico_extras_import.cmake) +# include($ENV{PICO_EXTRAS_PATH}/external/pico_extras_import.cmake) ### Project type project(${PROJECT_NAME} C CXX ASM) @@ -269,6 +293,16 @@ foreach(FILENAME PICOTYPE IN ZIP_LISTS FILENAMES PICOTYPES) # source files to compile target_sources(${BUILD} PUBLIC ${SOURCEFILES}) target_compile_options(${BUILD} ${COMPILE_OPTS}) + target_link_options(${BUILD} PRIVATE -Xlinker --print-memory-usage) + if(${PICO_SDK_VERSION_MAJOR} LESS 2) + pico_set_linker_script(${BUILD} ${PICO_SDK_PATH}/src/memmap_custom.ld) + else() + if(${PICO_PLATFORM} MATCHES rp2350) + pico_set_linker_script(${BUILD} ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/rp2350/memmap_default.ld) + else() + pico_set_linker_script(${BUILD} ${PICO_SDK_PATH}/src/rp2_common/pico_crt0/rp2040/memmap_default.ld) + endif() + endif() # tell the linker what libraries to link target_link_libraries(${BUILD} ${TARGET_LL}) # target sid types diff --git a/src/config.c b/src/config.c index 24a6880..aae4253 100644 --- a/src/config.c +++ b/src/config.c @@ -27,6 +27,7 @@ #include "globals.h" #include "config.h" +#include "usbsid.h" #include "midi.h" #include "sid.h" #include "logging.h" @@ -35,11 +36,17 @@ /* USBSID externals */ extern void init_sidclock(void); extern void deinit_sidclock(void); -extern void cdc_write(size_t n); -extern void webserial_write(size_t n); +extern void cdc_write(uint8_t * itf, uint32_t n); +extern void webserial_write(uint8_t * itf, uint32_t n); extern char dtype; +extern uint8_t *cdc_itf; +extern uint8_t *wusb_itf; extern uint8_t *write_buffer_p; +/* SID externals */ +extern uint8_t detect_sid_version(uint8_t start_addr); +extern void sid_test(int sidno, char test, char wf); + /* GPIO externals */ extern void restart_bus(void); @@ -55,7 +62,8 @@ void apply_config(void); static uint8_t config_array[FLASH_PAGE_SIZE]; /* 256 MIN ~ FLASH_PAGE_SIZE & 4096 MAX ~ FLASH_SECTOR_SIZE */ int sock_one, sock_two, sids_one, sids_two, numsids, act_as_one; uint8_t one, two, three, four; -const char* project_version = PROJECT_VERSION; /* Needed? */ +const char* project_version = PROJECT_VERSION; +static uint8_t p_version_array[MAX_BUFFER_SIZE]; // static const Config usbsid_default_config = { #define USBSID_DEFAULT_CONFIG_INIT { \ @@ -66,13 +74,13 @@ const char* project_version = PROJECT_VERSION; /* Needed? */ .socketOne = { \ .enabled = true, \ .dualsid = false, \ - .sidtype = 0x0, /* unused */ \ + .sidtype = 0x0, /* empty */ \ }, \ .socketTwo = { \ .enabled = true, \ .dualsid = false, \ .act_as_one = false, \ - .sidtype = 0x0, /* unused */ \ + .sidtype = 0x0, /* empty */ \ }, \ .LED = { \ .enabled = true, \ @@ -103,18 +111,53 @@ static const Config usbsid_default_config = USBSID_DEFAULT_CONFIG_INIT; void print_cfg(const uint8_t *buf, size_t len) { CFG("[PRINT CFG START]\n"); for (size_t i = 0; i < len; ++i) { + if (i == 0) + CFG("[R%d] ", i); CFG("%02x", buf[i]); - if (i % 16 == 15) + if (i == (len - 1)) { CFG("\n"); - else + } else if (i % 16 == 15) { + CFG("\n[R%d] ", i); + } else { CFG(" "); + } } CFG("[PRINT CFG END]\n"); } +void detect_sid_types(void) +{ + const char *sidtypes[4] = {"NONE", "CLONE", "MOS8580", "MOS6581"}; + int sid1 = 0, sid2 = 0; + if (usbsid_config.socketOne.enabled) { + sid1 = detect_sid_version(0x00); + usbsid_config.socketOne.sidtype = sid1; + } else { + usbsid_config.socketOne.sidtype = 0; + } + if (usbsid_config.socketTwo.enabled) { + if (usbsid_config.socketOne.dualsid) { + sid2 = detect_sid_version(0x40); + usbsid_config.socketTwo.sidtype = sid2; + } else { + sid2 = detect_sid_version(0x20); + usbsid_config.socketTwo.sidtype = sid2; + } + } else { + usbsid_config.socketTwo.sidtype = 0; + } + + CFG("[SOCKET ONE] %s\n", sidtypes[usbsid_config.socketOne.sidtype]); + CFG("[SOCKET TWO] %s\n", sidtypes[usbsid_config.socketTwo.sidtype]); + CFG("[READ SID1] [%02x %s]\n", sid1, sidtypes[sid1]); + CFG("[READ SID2] [%02x %s]\n", sid2, sidtypes[sid2]); +} + void read_config(Config* config) { - config_array[0] = 0x7F; /* Verification byte */ + + config_array[0] = READ_CONFIG; /* Initiator byte */ + config_array[1] = 0x7F; /* Verification byte */ config_array[6] = (int)config->external_clock; config_array[7] = (config->clock_rate >> 16) & BYTE; config_array[8] = (config->clock_rate >> 8) & BYTE; @@ -136,6 +179,14 @@ void read_config(Config* config) config_array[52] = (int)config->WebUSB.enabled; config_array[53] = (int)config->Asid.enabled; config_array[54] = (int)config->Midi.enabled; + config_array[63] = 0xFF; // Terminator byte +} + +void read_firmware_version() +{ + p_version_array[0] = USBSID_VERSION; /* Initiator byte */ + p_version_array[1] = strlen(project_version); /* Length of version string */ + __builtin_memcpy(p_version_array+2, project_version, strlen(project_version)); } void default_config(Config* config) @@ -193,21 +244,27 @@ void handle_config_request(uint8_t * buffer) case READ_CONFIG: CFG("[READ_CONFIG]\n"); /* TODO: loads current config and sends it to the requester ~ when config is finished */ - printf("[XIP_BASE]%u [FLASH_PAGE_SIZE]%u [FLASH_SECTOR_SIZE]%u [FLASH_TARGET_OFFSET]%u\r\n", XIP_BASE, FLASH_PAGE_SIZE, FLASH_SECTOR_SIZE, FLASH_TARGET_OFFSET); - printf("[>]\r\n"); - // Not working yet + /* ISSUE: Although 4 writes are performed, only the first 2 are received */ + CFG("[XIP_BASE]%u [FLASH_PAGE_SIZE]%u [FLASH_SECTOR_SIZE]%u [FLASH_TARGET_OFFSET]%u\r\n", XIP_BASE, FLASH_PAGE_SIZE, FLASH_SECTOR_SIZE, FLASH_TARGET_OFFSET); + CFG("[>]\r\n"); read_config(&usbsid_config); print_cfg(config_array, count_of(config_array)); - printf("[<]\r\n"); - int writes = count_of(config_array) / 64; - for (int i = 0; i <= writes; i++) { + CFG("[<]\r\n"); + int writes = count_of(config_array) / 64; /* BUG: It should send 4 packets of 64 bytes, but sends only 2 and a zero packet */ + CFG("[>] SEND %d WRITES OF 64 BYTES TO %c [<]\n", writes, dtype); + memset(write_buffer_p, 0, 64); + for (int i = 0; i < writes; i++) { __builtin_memcpy(write_buffer_p, config_array + (i * 64), 64); + CFG("[>] SEND WRITE %d OF %d [<]\n", i, writes); + CFG("[>]"); + for (int i = 0; i < 64; i++) CFG(" %x", write_buffer_p[i]); + CFG(" [<]\n"); switch (dtype) { case 'C': - cdc_write(64); + cdc_write(cdc_itf, 64); break; case 'W': - webserial_write(64); + webserial_write(wusb_itf, 64); break; } } @@ -295,6 +352,8 @@ void handle_config_request(uint8_t * buffer) usbsid_config.socketTwo.enabled = false; usbsid_config.socketOne.dualsid = false; usbsid_config.socketTwo.dualsid = false; + usbsid_config.socketOne.sidtype = 0; + usbsid_config.socketTwo.sidtype = 0; usbsid_config.socketTwo.act_as_one = true; save_config(&usbsid_config); load_config(&usbsid_config); @@ -306,6 +365,8 @@ void handle_config_request(uint8_t * buffer) usbsid_config.socketTwo.enabled = true; usbsid_config.socketOne.dualsid = false; usbsid_config.socketTwo.dualsid = false; + usbsid_config.socketOne.sidtype = 1; + usbsid_config.socketTwo.sidtype = 1; usbsid_config.socketTwo.act_as_one = false; save_config(&usbsid_config); load_config(&usbsid_config); @@ -317,8 +378,8 @@ void handle_config_request(uint8_t * buffer) usbsid_config.socketTwo.enabled = true; usbsid_config.socketOne.dualsid = true; usbsid_config.socketTwo.dualsid = true; - usbsid_config.socketOne.sidtype = 3; - usbsid_config.socketTwo.sidtype = 3; + usbsid_config.socketOne.sidtype = 1; + usbsid_config.socketTwo.sidtype = 1; usbsid_config.socketTwo.act_as_one = false; save_config(&usbsid_config); load_config(&usbsid_config); @@ -330,8 +391,8 @@ void handle_config_request(uint8_t * buffer) usbsid_config.socketTwo.enabled = true; usbsid_config.socketOne.dualsid = true; usbsid_config.socketTwo.dualsid = false; - usbsid_config.socketOne.sidtype = 3; - usbsid_config.socketTwo.sidtype = 2; + usbsid_config.socketOne.sidtype = 1; + usbsid_config.socketTwo.sidtype = 1; usbsid_config.socketTwo.act_as_one = false; save_config(&usbsid_config); mcu_reset(); @@ -341,6 +402,7 @@ void handle_config_request(uint8_t * buffer) for (int i = 0; i < 4; i++) { CFG("[SID %d]", (i + 1)); for (int j = 0; j < 32; j++) { + /* ISSUE: this only loads 1 channel state and should actually save all channel states */ midimachine.channel_states[curr_midi_channel][i][j] = usbsid_config.Midi.sid_states[i][j]; midimachine.channelkey_states[curr_midi_channel][i][i] = 0; /* Make sure extras is always initialized @ zero */ CFG(" %02x", midimachine.channel_states[curr_midi_channel][i][j]); @@ -354,6 +416,7 @@ void handle_config_request(uint8_t * buffer) for (int i = 0; i < 4; i++) { CFG("[SID %d]", (i + 1)); for (int j = 0; j < 32; j++) { + /* ISSUE: this only loads 1 channel state and should actually save all channel states */ usbsid_config.Midi.sid_states[i][j] = midimachine.channel_states[curr_midi_channel][i][j]; CFG(" %02x", usbsid_config.Midi.sid_states[i][j]); } @@ -366,6 +429,7 @@ void handle_config_request(uint8_t * buffer) for (int i = 0; i < 4; i++) { CFG("[SID %d]", (i + 1)); for (int j = 0; j < 32; j++) { + // BUG: this only resets 1 channel state usbsid_config.Midi.sid_states[i][j] = midimachine.channel_states[curr_midi_channel][i][j] = 0; CFG(" %02x", usbsid_config.Midi.sid_states[i][j]); midi_bus_operation((0x20 * i) | j, midimachine.channel_states[curr_midi_channel][i][j]); @@ -378,13 +442,67 @@ void handle_config_request(uint8_t * buffer) case SET_CLOCK: /* Change SID clock frequency */ CFG("[SET_CLOCK]\n"); if (!usbsid_config.external_clock) { - CFG("[CLOCK FROM]%d [CLOCK TO]%d\n", usbsid_config.clock_rate, clockrates[(int)buffer[1]]); - usbsid_config.clock_rate = clockrates[(int)buffer[1]]; - deinit_sidclock(); - init_sidclock(); - /* restart_bus(); */ /* TODO: Enable after bus_clock change */ + if (clockrates[(int)buffer[1]] != usbsid_config.clock_rate) { + CFG("[CLOCK FROM]%d [CLOCK TO]%d\n", usbsid_config.clock_rate, clockrates[(int)buffer[1]]); + usbsid_config.clock_rate = clockrates[(int)buffer[1]]; + deinit_sidclock(); + init_sidclock(); + /* restart_bus(); */ /* TODO: Enable after bus_clock change */ + } else { + CFG("[CLOCK FROM]%d AND [CLOCK TO]%d ARE THE SAME, SKIPPING SET_CLOCK\n", usbsid_config.clock_rate, clockrates[(int)buffer[1]]); + } + } break; + case DETECT_SIDS: + CFG("[DETECT_SIDS]\n"); + detect_sid_types(); + break; + case TEST_ALLSIDS: + CFG("[TEST_ALLSIDS]\n"); + for (int s = 0; s < numsids; s++) { + CFG("[START TEST SID %d]\n", s); + sid_test(s, '1', 'A'); + }; + break; + case TEST_SID1: + case TEST_SID2: + case TEST_SID3: + case TEST_SID4: + int s = (buffer[0] == TEST_SID1 ? 0 + : buffer[0] == TEST_SID2 ? 1 + : buffer[0] == TEST_SID3 ? 2 + : buffer[0] == TEST_SID4 ? 3 + : 0); /* Fallback to SID 0 */ + int t = (buffer[1] == 1 ? '1' /* All tests */ + : buffer[1] == 2 ? '2' /* All waveforms test */ + : buffer[1] == 3 ? '3' /* Filter tests */ + : buffer[1] == 4 ? '4' /* Envelope tests */ + : buffer[1] == 5 ? '5' /* Modulation tests */ + : '1'); /* Fallback to all tests */ + int wf = (buffer[2] == 0 ? 'A' /* All */ + : buffer[2] == 1 ? 'T' /* Triangle */ + : buffer[2] == 2 ? 'S' /* Sawtooth */ + : buffer[2] == 3 ? 'P' /* Pulse */ + : buffer[2] == 4 ? 'N' /* Noise */ + : 'P'); /* Fallback to pulse waveform */ + CFG("[TEST_SID%d] TEST: %c WF: %c\n", (s + 1), t, wf); + sid_test(s, t, wf); + break; + case USBSID_VERSION: + CFG("[READ_FIRMWARE_VERSION]\n"); + read_firmware_version(); + memset(write_buffer_p, 0, MAX_BUFFER_SIZE); + __builtin_memcpy(write_buffer_p, p_version_array, MAX_BUFFER_SIZE); + switch (dtype) { + case 'C': + cdc_write(cdc_itf, MAX_BUFFER_SIZE); + break; + case 'W': + webserial_write(wusb_itf, MAX_BUFFER_SIZE); + break; + } + break; case TEST_FN: /* TODO: Remove before v1 release */ printf("[TEST_FN]\n"); printf("[FLASH_TARGET_OFFSET]0x%x\n", FLASH_TARGET_OFFSET); diff --git a/src/config.h b/src/config.h index 4edd956..17ebf18 100644 --- a/src/config.h +++ b/src/config.h @@ -51,11 +51,13 @@ /* Config constants */ #define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) #define CONFIG_SIZE (FLASH_SECTOR_SIZE / 4) /* 1024 Bytes */ -#define PROJECT_VERSION "0.2.1-BETA.20241109" /* Compile time variable settings */ #ifndef MAGIC_SMOKE #define MAGIC_SMOKE 19700101 /* DATEOFRELEASE */ #endif +#ifndef PROJECT_VERSION +#define PROJECT_VERSION "0.2.2-BETA.20241117" /* Must be the same as in CMakeLists.txt */ +#endif #ifdef USE_RGB #define RGB_ENABLED true #else @@ -72,13 +74,13 @@ typedef struct Config { { bool enabled : 1; /* enable / disable this socket */ bool dualsid : 1; /* enable / disable dual SID support for this socket */ - uint8_t sidtype; /* 0 = empty, 1 = 6581, 2 = 8085, 3 = SKPico or other clone */ + uint8_t sidtype; /* 0 = empty, 1 = SKPico or other clone, 2 = MOS6581, 3 = MOS8085 */ } socketOne; /* 1 */ struct { bool enabled : 1; /* enable / disable this socket */ bool dualsid : 1; /* enable / disable dual SID support for this socket */ bool act_as_one : 1; /* act as socket 1 */ - uint8_t sidtype; /* 0 = empty, 1 = 6581, 2 = 8085, 3 = SKPico or other clone */ + uint8_t sidtype; /* 0 = empty, 1 = SKPico or other clone, 2 = MOS6581, 3 = MOS8085 */ } socketTwo; /* 2 */ struct { bool enabled : 1; @@ -125,11 +127,19 @@ enum TRIPLE_SID = 0x43, SET_CLOCK = 0x50, + DETECT_SIDS = 0x51, + TEST_ALLSIDS = 0x52, + TEST_SID1 = 0x53, + TEST_SID2 = 0x54, + TEST_SID3 = 0x55, + TEST_SID4 = 0x56, LOAD_MIDI_STATE = 0x60, SAVE_MIDI_STATE = 0x61, RESET_MIDI_STATE = 0x63, + USBSID_VERSION = 0x80, + TEST_FN = 0x99, /* TODO: Remove before v1 release */ }; diff --git a/src/gpio.c b/src/gpio.c index 2761e27..9acf5bd 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -27,6 +27,7 @@ #include "config.h" #include "gpio.h" #include "logging.h" +#include "sid.h" /* Init external vars */ @@ -270,3 +271,13 @@ void reset_sid(void) clear_bus(); enable_sid(); } + +void clear_sid_registers(void) +{ + for (int sid = 0; sid < numsids; sid++) { + for (int reg = 0; reg < count_of(sid_registers); reg++) { + bus_operation((0x10 | WRITE), sid_registers[reg], 0x0); + } + } + clear_bus(); +} diff --git a/src/mcu.c b/src/mcu.c index b5d7327..a9e31d5 100644 --- a/src/mcu.c +++ b/src/mcu.c @@ -47,7 +47,7 @@ void mcu_reset(void) { sleep_ms(100); // sleep some ms to let commands prior to reset settle or finish /* watchdog_enable(1, 1); */ - watchdog_reboot( 0, 0, 0 ); + watchdog_reboot(0, 0, 0); while(1); } diff --git a/src/midi.c b/src/midi.c index a8efe71..5ddbd52 100644 --- a/src/midi.c +++ b/src/midi.c @@ -566,6 +566,7 @@ void process_midi(uint8_t *buffer, int size) switch (bank) { /* QUESTION: Should this be/stay bank independent!? */ case 0: /* Bank 0 ~ Polyfonic stacking */ case 1: /* Bank 1 ~ Single voices */ + case 9: /* Bank 9 ~ Cynthcart */ switch (buffer[1]) { /* MSB or LSB for Control/Mode Change */ /* Bank ~ Patch select */ case CC_BMSB: /* Bank Select MSB */ @@ -814,7 +815,7 @@ void process_midi(uint8_t *buffer, int size) break; } break; - case 9: /* Bank 9 ~ Cynthcart */ + // case 9: /* Bank 9 ~ Cynthcart */ default: break; } diff --git a/src/sid.c b/src/sid.c new file mode 100644 index 0000000..4a70365 --- /dev/null +++ b/src/sid.c @@ -0,0 +1,263 @@ +/* + * 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.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 "globals.h" +#include "config.h" +#include "usbsid.h" +#include "sid.h" +#include "logging.h" + + +/* GPIO externals */ +extern uint8_t __not_in_flash_func(bus_operation)(uint8_t command, uint8_t address, uint8_t data); +extern void clear_sid_registers(void); + +/* Declare variables */ +uint8_t waveforms[4] = { 16, 32, 64, 128 }; + +uint8_t detect_sid_version(uint8_t start_addr) /* Not working on real SIDS!? */ +{ + bus_operation(0x10, (start_addr + 0x12), 0xFF); /* Set testbit in voice 3 control register to disable oscillator */ + bus_operation(0x10, (start_addr + 0x0E), 0xFF); /* Set frequency in voice 3 to $ffff */ + bus_operation(0x10, (start_addr + 0x0F), 0xFF); /* Set frequency in voice 3 to $ffff */ + bus_operation(0x10, (start_addr + 0x12), 0x20); /* Set Sawtooth wave and gatebit OFF to start oscillator again */ + uint8_t sidtype = bus_operation(0x11, (start_addr + 0x1B), 0x00); /* Accu now has different value depending on sid model (6581=3/8580=2) */ + return sidtype; /* that is: Carry flag is set for 6581, and clear for 8580. */ +} + +void test_operation(uint8_t reg, uint8_t val) +{ + bus_operation(0x10, reg, val); + sleep_us(5); +} + +void wave_form_test(uint8_t voices[3], int v, int on, int off) +{ + test_operation((voices[v] + sid_registers[CONTR]), on); /* CONTR */ + for (int f = 1; f <= 255; f++) { + test_operation((voices[v] + sid_registers[NOTEHI]), f); /* NOTEHI */ + sleep_ms(8); + } + test_operation((voices[v] + sid_registers[CONTR]), off); /* CONTR */ +} + +void pulse_sweep_test(uint8_t voices[3], int v, int on, int off) +{ + test_operation((voices[v] + sid_registers[CONTR]), on); /* CONTR */ + for (int x = 0; x <= 5; x++) { + for (int y = 0; y <= 15; y++) { + test_operation((voices[v] + sid_registers[PWMHI]), y); /* PWMHI */ + sleep_ms(16); + } + for (int y = 15; y >= 0; y--) { + test_operation((voices[v] + sid_registers[PWMHI]), y); /* PWMHI */ + sleep_ms(16); + } + } + test_operation((voices[v] + sid_registers[CONTR]), off); /* CONTR */ +} + +void test_all_waveforms(uint8_t addr, uint8_t voices[3]) +{ + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + for (int v = 0; v < 3; v++) { + DBG("TEST VOICE %d\n", (v + 1)); + DBG("WAVEFORM TESTS\n"); + test_operation((voices[v] + sid_registers[ATTDEC]), 33); /* ATTDEC 2*16+1 */ + test_operation((voices[v] + sid_registers[SUSREL]), 242); /* SUSREL 15*16+2 */ + test_operation((voices[v] + sid_registers[PWMHI]), 8); /* PWMHI */ + + DBG("TRIANGLE TESTING "); + wave_form_test(voices, v, 17, 16); + DBG("COMPLETE\n"); + DBG("SAWTOOTH TESTING "); + wave_form_test(voices, v, 33, 32); + DBG("COMPLETE\n"); + DBG("PULSE TESTING "); + wave_form_test(voices, v, 65, 64); + DBG("COMPLETE\n"); + DBG("NOISE TESTING "); + wave_form_test(voices, v, 129, 128); + DBG("COMPLETE\n"); + + DBG("PULSE WIDTH SWEEP TESTING "); + test_operation((voices[v] + sid_registers[NOTEHI]), 40); /* NOTEHI */ + pulse_sweep_test(voices, v, 65, 64); + DBG("COMPLETE\n"); + } +} + +void filter_tests(uint8_t addr, uint8_t voices[3], int wf) +{ + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + DBG("FILTER TESTS %s\n", (wf == 0 ? "TRIANGLE" : wf == 2 ? "SAWTOOTH" : wf == 3 ? "PULSE" : "NOISE")); + for (int fq = 15; fq <= 45; fq += 15) { + DBG("HIGH FILTER FREQUENCY = %d\n", fq); + test_operation(sid_registers[RESFLT], 87); /* RESFLT 5*16+1+2+4 */ + for (int flt = 1; flt <= 3; flt++) { + DBG("%s PASS\n", (flt == 1 ? "LOW" : flt == 2 ? "BAND" : "HIGH")); + test_operation(sid_registers[MODVOL], (flt == 1 ? 31 : flt == 2 ? 47 : 79)); /* MODVOL */ + for (int v = 0; v < 3; v++) { + DBG("VOICE %d TESTING ", (v + 1)); + test_operation((voices[v] + sid_registers[NOTEHI]), fq); /* NOTEHI */ + test_operation((voices[v] + sid_registers[ATTDEC]), 0); /* ATTDEC */ + test_operation((voices[v] + sid_registers[SUSREL]), 240); /* SUSREL */ + test_operation((voices[v] + sid_registers[PWMHI]), 8); /* PWMHI */ + test_operation((voices[v] + sid_registers[CONTR]), waveforms[wf] + 1); /* CONTR */ + for (int f = 0; f <= 255; f++) { + test_operation((voices[v] + sid_registers[FC_HI]), f); /* FC_HI */ + sleep_ms(8); + } + test_operation((voices[v] + sid_registers[CONTR]), waveforms[wf]); /* CONTR */ + DBG("COMPLETE\n"); + } + } + } +} + +void envelope_tests(uint8_t addr, uint8_t voices[3], int wf) +{ + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + DBG("A D S R TESTS %s\n", (wf == 0 ? "TRIANGLE" : wf == 1 ? "SAWTOOTH" : wf == 2 ? "PULSE" : "NOISE")); + for (int v = 0; v < 3; v++) { + DBG("VOICE %d A D S R TESTING ", (v + 1)); + test_operation((voices[v] + sid_registers[ATTDEC]), 170); /* ATTDEC 10*16+10 */ + test_operation((voices[v] + sid_registers[SUSREL]), 58); /* SUSREL 3*16+10 */ + test_operation((voices[v] + sid_registers[NOTEHI]), 40); /* NOTEHI */ + test_operation((voices[v] + sid_registers[PWMHI]), 8); /* PWMHI */ + test_operation((voices[v] + sid_registers[CONTR]), (waveforms[wf] + 1)); /* CONTR */ + sleep_ms(3000); + test_operation((voices[v] + sid_registers[CONTR]), waveforms[wf]); /* CONTR */ + DBG("RELEASE "); + sleep_ms(1000); + DBG("COMPLETE\n"); + sleep_ms(600); + + DBG("VOICE %d S R TESTING ", (v + 1)); + test_operation((voices[v] + sid_registers[ATTDEC]), 0); /* ATTDEC */ + test_operation((voices[v] + sid_registers[SUSREL]), 250); /* SUSREL 15*16+10 */ + test_operation((voices[v] + sid_registers[CONTR]), (waveforms[wf] + 1)); /* CONTR */ + sleep_ms(600); + test_operation((voices[v] + sid_registers[CONTR]), waveforms[wf]); /* CONTR */ + DBG("RELEASE "); + sleep_ms(600); + DBG("COMPLETE\n"); + sleep_ms(600); + + DBG("VOICE %d A D TESTING ", (v + 1)); + test_operation((voices[v] + sid_registers[ATTDEC]), 170); /* ATTDEC 10*16+10 */ + test_operation((voices[v] + sid_registers[SUSREL]), 0); /* SUSREL */ + test_operation((voices[v] + sid_registers[CONTR]), (waveforms[wf] + 1)); /* CONTR */ + sleep_ms(2000); + test_operation((voices[v] + sid_registers[CONTR]), waveforms[wf]); /* CONTR */ + DBG("RELEASE "); + sleep_ms(600); + DBG("COMPLETE\n"); + sleep_ms(600); + } +} + +void modulation_tests(uint8_t addr, uint8_t voices[3], int wf) +{ + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + DBG("RING MODULATION TESTS %s\n", (wf == 0 ? "TRIANGLE" : wf == 1 ? "SAWTOOTH" : wf == 2 ? "PULSE" : "NOISE")); + for (int v = 0; v < 3; v++) { + int v2 = (v == 1 ? 0 : v == 2 ? 1 : 2); + DBG("VOICE %d WITH VOICE %d ", (v + 1), (v2 + 1)); + test_operation((voices[v] + sid_registers[PWMHI]), 8); /* PWMHI */ + test_operation((voices[v] + sid_registers[ATTDEC]), 0); /* ATTDEC */ + test_operation((voices[v] + sid_registers[SUSREL]), 250); /* SUSREL 15*16+10 */ + test_operation((voices[v2] + sid_registers[NOTEHI]), 10); /* NOTEHI */ + test_operation((voices[v] + sid_registers[CONTR]), (waveforms[wf] + 3)); /* CONTR */ + for (int f = 0; f <= 255; f++) { + test_operation((voices[v] + sid_registers[NOTEHI]), f); /* NOTEHI */ + sleep_ms(24); + } + test_operation((voices[v] + sid_registers[CONTR]), 0); /* CONTR */ + DBG("COMPLETE\n"); + } +} + +void sid_test(int sidno, char test, char wf) +{ + clear_sid_registers(); + uint8_t addr = (sidno * 0x20); + uint8_t voices[3] = { (sid_registers[0] + addr), (sid_registers[7] + addr), (sid_registers[14] + addr) }; + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + + switch (test) { + case '1': /* RUN ALL TESTS */ + test_all_waveforms(addr, voices); + clear_sid_registers(); + filter_tests(addr, voices, 0); + filter_tests(addr, voices, 1); + filter_tests(addr, voices, 2); + filter_tests(addr, voices, 3); + clear_sid_registers(); + envelope_tests(addr, voices, 0); + envelope_tests(addr, voices, 1); + envelope_tests(addr, voices, 2); + envelope_tests(addr, voices, 3); + clear_sid_registers(); + modulation_tests(addr, voices, 0); + modulation_tests(addr, voices, 1); + modulation_tests(addr, voices, 2); + modulation_tests(addr, voices, 3); + clear_sid_registers(); + case '2': /* ALL WAVEFORMS */ + test_all_waveforms(addr, voices); + break; + case '3': /* FILTER */ + if (wf == 'A') { + for (int w = 0; w < 4; w++) { + filter_tests(addr, voices, w); + } + } else { + filter_tests(addr, voices, (wf == 'T' ? 0 : wf == 'S' ? 1 : wf == 'P' ? 2 : 3)); + } + break; + case '4': /* ENVELOPE */ + if (wf == 'A') { + for (int w = 0; w < 4; w++) { + envelope_tests(addr, voices, w); + } + } else { + envelope_tests(addr, voices, (wf == 'T' ? 0 : wf == 'S' ? 1 : wf == 'P' ? 2 : 3)); + } + break; + case '5': /* MODULATION */ + test_operation((sid_registers[MODVOL] + addr), 0x0F); /* Volume to full */ + if (wf == 'A') { + for (int w = 0; w < 4; w++) { + modulation_tests(addr, voices, w); + } + } else { + modulation_tests(addr, voices, (wf == 'T' ? 0 : wf == 'S' ? 1 : wf == 'P' ? 2 : 3)); + } + break; + default: + break; + } +} diff --git a/src/sid.h b/src/sid.h index fb88ab9..dfdfd38 100644 --- a/src/sid.h +++ b/src/sid.h @@ -101,7 +101,7 @@ typedef enum { * 5: Attack/Decay * 6: Sustain/Release */ -static const uint8_t sid_registers[] = +static const uint8_t sid_registers[29] = { /* Voice 1 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index a451d91..24f9c7e 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -194,12 +194,19 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) return desc_fs_configuration; } +#ifndef USBSID_MANUFACTURER +#define USBSID_MANUFACTURER "ERROR MISSING MANUFACTURER!" +#endif +#ifndef USBSID_PRODUCT +#define USBSID_PRODUCT "ERROR MISSING PRODUCT!" +#endif + /* String Descriptors */ -char const *string_desc_arr[] = +const char *string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) - "USBSID", // 1: Manufacturer - "USBSID-Pico", // 2: Product + USBSID_MANUFACTURER, // 1: Manufacturer + USBSID_PRODUCT, // 2: Product "USES-MCU-ID", // 3: Serial, uses chip ID "USBSID-Pico Data", // 4: CDC Interface "USBSID-Pico Midi", // 5: Midi Interface diff --git a/src/usbsid.c b/src/usbsid.c index e077111..9ecbf65 100644 --- a/src/usbsid.c +++ b/src/usbsid.c @@ -311,15 +311,19 @@ void __not_in_flash_func(handle_buffer_task)(uint8_t * itf, uint32_t * n) } break; case PAUSE: + DBG("[PAUSE_SID]\n"); pause_sid(); break; case RESET_SID: + DBG("[RESET_SID]\n"); reset_sid(); break; case RESET_MCU: + DBG("[RESET_MCU]\n"); mcu_reset(); break; case BOOTLOADER: + DBG("[BOOTLOADER]\n"); mcu_jump_to_bootloader(); break; default: