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