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