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