libsidplayfp 2.16.1
src/USBSID.h
1/*
2 * USBSID-Pico is a RPi Pico (RP2040/RP2350) based board for interfacing one
3 * or two MOS SID chips and/or hardware SID emulators over (WEB)USB with your
4 * computer, phone or ASID supporting player.
5 *
6 * USBSID.h
7 * This file is part of USBSID-Pico (https://github.com/LouDnl/USBSID-Pico-driver)
8 * File author: LouD
9 *
10 * Copyright (c) 2024-2025 LouD
11 *
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, version 2.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#ifndef _USBSID_H_
27#define _USBSID_H_
28
29#if defined(__linux__) || defined(__linux) || defined(linux) || defined(__unix__) || defined(__APPLE__)
30 #define __US_LINUX_COMPILE
31#elif defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) || defined(__MINGW64__)
32 #define __US_WINDOWS_COMPILE
33#endif
34
40#if defined(__US_WINDOWS_COMPILE)
41 /* Detect ARM64 across various compiler defines */
42 #if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) || defined(_ARM_)
43 /* libusb on Windows typically expects WINAPI (stdcall) on x86,
44 but Clang on ARM64 must treat this as empty. */
45 #undef WINAPI
46 #define WINAPI
47 #undef LIBUSB_CALL
48 #define LIBUSB_CALL
49 #else
50 #ifndef WINAPI
51 #define WINAPI __stdcall
52 #endif
53 #ifndef LIBUSB_CALL
54 #define LIBUSB_CALL WINAPI
55 #endif
56 #endif
57#endif
58
59/* Fallback for other platforms */
60#ifndef LIBUSB_CALL
61 #define LIBUSB_CALL
62#endif
63
64#pragma GCC diagnostic push
65#pragma GCC diagnostic ignored "-Wunused-variable"
66
67#ifdef __cplusplus
68 #include <cstdint>
69 #include <cstdio>
70 #include <cstdlib>
71 #include <cstring>
72 #include <chrono>
73 #include <thread>
74 #include <atomic>
75#else
76 #include <stdbool.h>
77 #include <stdint.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <pthread.h>
82 #include <stdatomic.h>
83#endif
84
90#if defined(__clang__) && defined(__MINGW32__)
91 #if defined(__x86_64__) || defined(__aarch64__)
92 #include <pthread.h>
93 #endif
94#endif
95
96
97/* Optional driver start and driver exit commands
98 *
99 * Some players do weird things on start, choose
100 * any of these to suit your own needs
101 *
102 */
103
104// #define US_RESET_ON_ENTRY /* Send Reset SID command on LIBUSB Entry */
105// #define US_CLEARBUS_ON_ENTRY /* Send Clear Bus command on LIBUSB Entry */
106// #define US_UNMUTE_ON_ENTRY /* Send UnMute SID command on LIBUSB Exit */
107
108// #define US_MUTE_ON_EXIT /* Send Mute SID command on LIBUSB Exit */
109// #define US_RESET_ON_EXIT /* Send Reset SID command on LIBUSB Exit */
110
111
112/* Uncomment for debug logging */
113// #define USBSID_DEBUG
114#ifdef USBSID_DEBUG
115 #define USBDBG(...) fprintf(__VA_ARGS__)
116#else
117 #define USBDBG(...) ((void)0)
118#endif
119#define USBERR(...) fprintf(__VA_ARGS__)
120
121using namespace std;
122
123/* Pre-define libusb structs */
124struct libusb_context;
125struct libusb_transfer;
126
127namespace USBSID_NS
128{
129 /* pre-declaration for static functions */
130 class USBSID_Class;
131
132 /* LIBUSB/USBSID related */
133 enum {
134 VENDOR_ID = 0xCAFE,
135 PRODUCT_ID = 0x4011,
136 ACM_CTRL_DTR = 0x01,
137 ACM_CTRL_RTS = 0x02,
138 EP_OUT_ADDR = 0x02,
139 EP_IN_ADDR = 0x82,
140 LEN_IN_BUFFER = 1,
141 LEN_OUT_BUFFER = 64,
142 #ifdef DEBUG_USBSID_MEMORY
143 LEN_TMP_BUFFER = 4
144 #endif
145 };
146
147 enum {
148 /* BYTE 0 - top 2 bits */
149 WRITE = 0, /* 0b0 ~ 0x00 */
150 READ = 1, /* 0b1 ~ 0x40 */
151 CYCLED_WRITE = 2, /* 0b10 ~ 0x80 */
152 COMMAND = 3, /* 0b11 ~ 0xC0 */
153 /* BYTE 0 - lower 6 bits for byte count */
154 /* BYTE 0 - lower 6 bits for Commands */
155 PAUSE = 10, /* 0b1010 ~ 0x0A */
156 UNPAUSE = 11, /* 0b1011 ~ 0x0B */
157 MUTE = 12, /* 0b1100 ~ 0x0C */
158 UNMUTE = 13, /* 0b1101 ~ 0x0D */
159 RESET_SID = 14, /* 0b1110 ~ 0x0E */
160 DISABLE_SID = 15, /* 0b1111 ~ 0x0F */
161 ENABLE_SID = 16, /* 0b10000 ~ 0x10 */
162 CLEAR_BUS = 17, /* 0b10001 ~ 0x11 */
163 CONFIG = 18, /* 0b10010 ~ 0x12 */
164 RESET_MCU = 19, /* 0b10011 ~ 0x13 */
165 BOOTLOADER = 20, /* 0b10100 ~ 0x14 */
166 };
167
168 /* Thread related */
169 static int run_thread;
170
171 /* Fake C64 Memory */
172 #ifdef DEBUG_USBSID_MEMORY
173 static uint8_t sid_memory[0x20];
174 static uint8_t sid_memory_changed[0x20];
175 static uint16_t sid_memory_cycles[0x20];
176 #endif
177
178 /* LIBUSB related */
179 static struct libusb_device_handle *devh = NULL;
180 static struct libusb_transfer *transfer_out = NULL; /* OUT-going transfers (OUT from host PC to USB-device) */
181 static struct libusb_transfer *transfer_in = NULL; /* IN-coming transfers (IN to host PC from USB-device) */
182 static libusb_context *ctx = NULL;
183 static bool in_buffer_dma = false;
184 static bool out_buffer_dma = false;
185
186 static bool threaded = false;
187 static bool withcycles = false;
188 static int rc, read_completed, write_completed;
189
190 /* USB buffer related */
191 static uint8_t * __restrict__ in_buffer; /* incoming libusb will reside in this buffer */
192 static uint8_t * __restrict__ out_buffer; /* outgoing libusb will reside in this buffer */
193 static uint8_t * __restrict__ thread_buffer; /* data to be transfered to the out_buffer will reside in this buffer */
194 static uint8_t * __restrict__ write_buffer; /* non async data will be written from this buffer */
195 #ifdef DEBUG_USBSID_MEMORY
196 static uint8_t * __restrict__ temp_buffer; /* temp buffer for debug printing */
197 #endif
198 static uint8_t * __restrict__ result; /* variable where read data is copied into */
199 static int len_out_buffer; /* changable variable for out buffer size */
200 static int buffer_pos = 1; /* current position of the out buffer */
201 static int flush_buffer = 0; /* flush buffer yes or no */
202
203 /* Ringbuffer related */
204 typedef struct {
205 int ring_read;
206 int ring_write;
207 int is_allocated;
208 uint8_t * __restrict__ ringbuffer;
209 } ring_buffer_t;
210 static ring_buffer_t us_ringbuffer;
211 const int min_diff_size = 16;
212 const int min_ring_size = 256;
213 const int default_diff_size = 64;
214 const int default_ring_size = 8192;
215
216 /* Clock cycles per second
217 * Clock speed: 0.985 MHz (PAL) or 1.023 MHz (NTSC)
218 *
219 * For some reason both 1022727 and 1022730 are
220 * mentioned as NTSC clock cycles per second
221 * Going for the rates specified by Vice it should
222 * be 1022730, except in the link about raster time
223 * on c64 wiki it's 1022727.
224 * I chose to implement both, let's see how this
225 * works out
226 *
227 * https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/c64/c64.h
228 */
229
230 /* Clock cycles per second */
231 enum clock_speeds
232 {
233 DEFAULT = 1000000, /* 1 MHz = 1 us */
234 PAL = 985248, /* 0.985 MHz = 1.014973 us */
235 NTSC = 1022727, /* 1.023 MHz = 0.977778 us */
236 DREAN = 1023440, /* 1.023 MHz = 0.977097 us */
237 NTSC2 = 1022730, /* 1.023 MHz = 0.977778 us */
238 };
239 /* Refreshrates (cycles) in microseconds */
240 enum refresh_rates
241 {
242 HZ_DEFAULT = 20000, /* 50Hz ~ 20000 == 20 us */
243 HZ_EU = 19950, /* 50Hz ~ 20000 == 20 us / 50.125Hz ~ 19.950124688279 exact */
244 HZ_US = 16715, /* 60Hz ~ 16667 == 16.67 us / 59.826Hz ~ 16.715140574332 exact */
245 };
246 /* Rasterrates (cycles) in microseconds
247 * Source: https://www.c64-wiki.com/wiki/raster_time
248 *
249 * PAL: 1 horizontal raster line takes 63 cycles
250 * or 504 pixels including side borders
251 * whole screen consists of 312 horizontal lines
252 * for a frame including upper and lower borders
253 * 63 * 312 CPU cycles is 19656 for a complete
254 * frame update @ 985248 Hertz
255 * 985248 / 19656 = approx 50.12 Hz frame rate
256 *
257 * NTSC: 1 horizontal raster line takes 65 cycles
258 * whole screen consists of 263 rasters per frame
259 * 65 * 263 CPU cycles is 17096 for a complete
260 * frame update @ 985248 Hertz
261 * 1022727 / 17096 = approx 59.83 Hz frame rate
262 *
263 */
264 enum raster_rates
265 {
266 R_DEFAULT = 20000, /* 20us ~ fallback */
267 R_EU = 19656, /* PAL: 63 cycles * 312 lines = 19656 cycles per frame update @ 985248 Hz = 50.12 Hz frame rate */
268 R_US = 17096, /* NTSC: 65 cycles * 263 lines = 17096 cycles per frame update @ 1022727 Hz = 59.83 Hz Hz frame rate */
269 };
270 static const enum clock_speeds clockSpeed[] = { DEFAULT, PAL, NTSC, DREAN, NTSC2 };
271 static const enum refresh_rates refreshRate[] = { HZ_DEFAULT, HZ_EU, HZ_US, HZ_US, HZ_US };
272 static const enum raster_rates rasterRate[] = { R_DEFAULT, R_EU, R_US, R_US, R_US };
273 static long cycles_per_sec = DEFAULT; /* default @ 1000000 */
274 static long cycles_per_frame = HZ_DEFAULT; /* default @ 20000 */
275 static long cycles_per_raster = R_DEFAULT; /* default @ 20000 */
276 static int clk_retrieved = 0;
277 static long us_clkrate = 0;
278 static int numsids = 0;
279 static int fmoplsid = -1;
280 static int pcbversion = -1;
281 static int socketconfig = -1;
282
283 /* Object related */
284 static int us_Found = 0;
285 static int instance = -1;
286
287 /* Timing related */
288 typedef std::nano ratio_t; /* 1000000000 */
289 typedef std::chrono::steady_clock::time_point timestamp_t; /* Point in time */
290 typedef std::chrono::nanoseconds duration_t; /* Duration in nanoseconds */
291
292 #ifdef __cplusplus
293 static std::atomic_int us_thread(0);
294 #else
295 static _Atomic int us_thread = 0;
296 #endif
297 static pthread_mutex_t us_mutex;
298 class USBSID_Class {
299 private:
300
301 /* Driver related */
302 static bool us_Initialised;
303 static bool us_Available;
304 static bool us_PortIsOpen;
305 int us_InstanceID;
306
307 /* Timing related */
308 static double us_CPUcycleDuration; /* CPU cycle duration in nanoseconds */
309 static double us_InvCPUcycleDurationNanoSeconds; /* Inverted CPU cycle duration in nanoseconds */
310 static timestamp_t m_StartTime; /* That moment when... */
311 static timestamp_t m_LastTime; /* I know what you did last summer! */
312
313 /* Ringbuffer related */
314 static int diff_size;
315 static int ring_size;
316
317 /* LIBUSB */
318 int LIBUSB_Setup(bool start_threaded, bool with_cycles);
319 int LIBUSB_Exit(void);
320 int LIBUSB_Available(uint16_t vendor_id, uint16_t product_id);
321 void LIBUSB_StopTransfers(void);
322 int LIBUSB_OpenDevice(void);
323 void LIBUSB_CloseDevice(void);
324 int LIBUSB_DetachKernelDriver(void);
325 int LIBUSB_ConfigureDevice(void);
326 void LIBUSB_InitOutBuffer(void);
327 void LIBUSB_FreeOutBuffer(void);
328 void LIBUSB_InitInBuffer(void);
329 void LIBUSB_FreeInBuffer(void);
330 static void LIBUSB_CALL usb_out(struct libusb_transfer *transfer);
331 static void LIBUSB_CALL usb_in(struct libusb_transfer *transfer);
332
333 /* Line encoding ~ baud rate is ignored by TinyUSB */
334 unsigned char encoding[7] = { 0x40, 0x54, 0x89, 0x00, 0x00, 0x00, 0x08 }; // 9000000 ~ 0x895440
335
336 /* Threading */
337 void* USBSID_Thread(void);
338 int USBSID_InitThread(void);
339 void USBSID_StopThread(void);
340 int USBSID_IsRunning(void);
341 pthread_t us_ptid;
342
343 /* Ringbuffer */
344 void USBSID_ResetRingBuffer(void);
345 void USBSID_InitRingBuffer(int buffer_size, int differ_size);
346 void USBSID_InitRingBuffer(void);
347 void USBSID_DeInitRingBuffer(void);
348 bool USBSID_IsHigher(void);
349 int USBSID_RingDiff(void);
350 void USBSID_RingPut(uint8_t item);
351 uint8_t USBSID_RingGet(void);
352 void USBSID_FlushBuffer(void);
353
354 /* Ringbuffer reads & writes*/
355 void USBSID_RingPopCycled(void); /* Threaded writer with cycles */
356 void USBSID_RingPop(void); /* Threaded writer */
357
358 public:
359
360 USBSID_Class(); /* Constructor */
361 ~USBSID_Class(); /* Deconstructor */
362
363 /* USBSID */
364 int USBSID_Init(bool start_threaded, bool with_cycles);
365 int USBSID_Close(void);
366 int USBSID_GetInstanceID(void){ return us_InstanceID; };
367 bool USBSID_isInitialised(void){ return us_Initialised; };
368 bool USBSID_isAvailable(void){ return us_Available; };
369 bool USBSID_isOpen(void){ return us_PortIsOpen; };
370
371 /* USBSID & SID control */
372 void USBSID_Pause(void); /* Pause playing by releasing chipselect pins */
373 void USBSID_Reset(void); /* Reset all SID chips */
374 void USBSID_ResetAllRegisters(void); /* Reset register for all SID chips */
375 void USBSID_Mute(void); /* Mute all SID chips */
376 void USBSID_UnMute(void); /* UnMute all SID chips */
377 void USBSID_DisableSID(void); /* Release reset pin and unmute SID */
378 void USBSID_EnableSID(void); /* Assert reset pin and release chipselect pins */
379 void USBSID_ClearBus(void); /* Clear the SID bus from any data */
380 void USBSID_SetClockRate(long clockrate_cycles, /* Set CPU clockrate in Hertz */
381 bool suspend_sids); /* Assert SID RES signal while changing clockrate (Advised!)*/
382 long USBSID_GetClockRate(void); /* Get CPU clockrate in Hertz */
383 long USBSID_GetRefreshRate(void); /* Get cycles per refresh rate */
384 long USBSID_GetRasterRate(void); /* Get cycles per raster rate */
385 uint8_t* USBSID_GetSocketConfig(uint8_t socket_config[]); /* Get socket config for parsing */
386 int USBSID_GetSocketNumSIDS(int socket, uint8_t socket_config[]); /* Get the socket number of sids configured */
387 int USBSID_GetSocketChipType(int socket, uint8_t socket_config[]); /* Get the socket chip type configured */
388 int USBSID_GetSocketSIDType1(int socket, uint8_t socket_config[]); /* Get the socket SID 1 type configured */
389 int USBSID_GetSocketSIDType2(int socket, uint8_t socket_config[]); /* Get the socket SID 2 type configured (only works for clone chip types ofcourse) */
390 int USBSID_GetNumSIDs(void); /* Get the total number of sids configured */
391 int USBSID_GetFMOplSID(void); /* Get the sid number (if configured) to address FMOpl */
392 int USBSID_GetPCBVersion(void); /* Get the PCB version */
393 void USBSID_SetStereo(int state); /* Set device to mono or stereo ~ v1.3 PCB only */
394 void USBSID_ToggleStereo(void); /* Toggle between mono and stereo ~ v1.3 PCB only */
395
396 /* Synchronous direct */
397 void USBSID_SingleWrite(unsigned char *buff, int len); /* Single write buffer of size_t ~ example: config writing */
398 unsigned char USBSID_SingleRead(uint8_t reg); /* Single read register, return result */
399 unsigned char USBSID_SingleReadConfig(unsigned char *buff, int len); /* Single to buffer of specified length ~ example: config reading */
400
401 /* Asynchronous direct */
402 void USBSID_Write(unsigned char *buff, size_t len); /* Write buffer of size_t len */
403 void USBSID_Write(uint8_t reg, uint8_t val); /* Write register and value */
404 void USBSID_Write(unsigned char *buff, size_t len, uint16_t cycles); /* Wait n cycles, write buffer of size_t len */
405 void USBSID_Write(uint8_t reg, uint8_t val, uint16_t cycles); /* Wait n cycles, write register and value */
406 void USBSID_WriteCycled(uint8_t reg, uint8_t val, uint16_t cycles); /* Write register and value, USBSID uses cycles for delay */
407 unsigned char USBSID_Read(uint8_t reg); /* Write register, return result */
408 unsigned char USBSID_Read(unsigned char *writebuff); /* Write buffer, return result */
409 unsigned char USBSID_Read(unsigned char *writebuff, uint16_t cycles); /* Wait for n cycles and write buffer, return result */
410
411 /* Asynchronous thread */
412 void USBSID_WriteRing(uint8_t reg, uint8_t val); /* Write register and value to ringbuffer, USBSID adds 10 delay cycles to each write */
413 void USBSID_WriteRingCycled(uint8_t reg, uint8_t val, uint16_t cycles); /* Write register, value, and cycles to ringbuffer */
414
415 /* Threading */
416 void USBSID_EnableThread(void); /* Enable the thread on the fly */
417 void USBSID_DisableThread(void); /* Disable the running thread and switch to non threaded and cycled on the fly */
418
419 /* Ringbuffer */
420 void USBSID_SetFlush(void); /* Set flush buffer flag to 1 */
421 void USBSID_Flush(void); /* Set flush buffer flag to 1 and flushes the buffer */
422 void USBSID_SetBufferSize(int size); /* Set the buffer size for storing writes */
423 void USBSID_SetDiffSize(int size); /* Set the minimum size difference between head & tail */
424 void USBSID_RestartRingBuffer(void); /* Restart the ringbuffer*/
425
426 /* Thread utils */
427 void USBSID_RestartThread(bool with_cycles);
428 static void *_USBSID_Thread(void *context)
429 { /* Required for supplying private function to pthread_create */
430 return ((USBSID_Class *)context)->USBSID_Thread();
431 }
432
433 /* Timing and cycles */
434 uint_fast64_t USBSID_WaitForCycle(uint_fast16_t cycles); /* Sleep for n cycles */
435 uint_fast64_t USBSID_WaitForCycle_(uint_fast16_t cycles); /* Sleep for n cycles ~ deprecated */
436 void USBSID_SyncTime(void); /* Sync time for cycle delay function */
437
438 /* Utils */
439 /* TODO: Deprecate this function, emulator/player should handle this */
440 uint8_t USBSID_Address(uint16_t addr); /* Calculates correct SID address to write to if player does not */
441 };
442
443} /* USBSIDDriver */
444
445
446#ifdef USBSID_OPTOFF
447#pragma GCC diagnostic pop
448#pragma GCC pop_options
449#endif
450
451#endif /* _USBSID_H_ */