39 #if defined(HAVE_PTHREAD_H)
42 #error "No thread model available"
44 #endif // EXSID_TRHREADED
46 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
47 #define XS_ERRORBUF 256
48 #define xserror(xs, format, ...) snprintf(xs->xSerrstr, XS_ERRORBUF, "(%s) ERROR " format, __func__, ## __VA_ARGS__)
88 .read_offset_cycles = -2,
104 .read_offset_cycles = 0,
123 unsigned long accioops;
124 unsigned long accdelay;
125 unsigned long acccycle;
131 #ifdef EXSID_THREADED
133 pthread_mutex_t flip_mtx;
134 pthread_cond_t backbuf_full_cnd, backbuf_avail_cnd;
135 unsigned char * restrict frontbuf;
138 pthread_t thread_output;
139 #endif // EXSID_THREADED
152 static int _xSusleep(
int usecs)
157 xsdbg(
"sleep: %d\n", usecs);
159 tv.tv_sec = usecs / 1000000;
160 tv.tv_nsec = (usecs % 1000000) * 1000;
163 ret = nanosleep(&tv, &tv);
179 const struct _exsid *
const xs = exsid;
194 static inline void xSwrite(
struct _exsid *
const xs,
const unsigned char * buff,
int size)
199 xsdbg(
"Error ftdi_write_data(%d): %s\n",
203 xsdbg(
"ftdi_write_data only wrote %d (of %d) bytes\n",
219 static void xSread(
struct _exsid *
const xs,
unsigned char * buff,
int size)
226 }
while (ret != size);
230 xsdbg(
"Error ftdi_read_data(%d): %s\n",
234 xsdbg(
"ftdi_read_data only read %d (of %d) bytes\n",
241 #ifdef EXSID_THREADED
253 static void * _exSID_thread_output(
void * arg)
255 struct _exsid *
const xs = arg;
256 unsigned char * bufptr;
257 int frontbuf_idx, delay;
260 pthread_setname_np(pthread_self(),
"output");
263 xsdbg(
"thread started\n");
265 pthread_mutex_lock(&xs->flip_mtx);
268 while (!xs->backbuf_ready)
269 pthread_cond_wait(&xs->backbuf_full_cnd, &xs->flip_mtx);
272 delay = xs->postdelay;
273 bufptr = xs->frontbuf;
278 xs->backbuf_ready = 0;
281 pthread_cond_signal(&xs->backbuf_avail_cnd);
283 pthread_mutex_unlock(&xs->flip_mtx);
286 xSwrite(xs, xs->frontbuf, frontbuf_idx);
290 xsdbg(
"thread exiting!\n");
298 #endif // EXSID_THREADED
308 static void xSoutb(
struct _exsid *
const xs, uint8_t byte,
int flush)
315 #ifdef EXSID_THREADED
317 pthread_mutex_lock(&xs->flip_mtx);
319 xs->postdelay = flush;
322 xs->backbuf_ready = 1;
323 pthread_cond_signal(&xs->backbuf_full_cnd);
327 pthread_cond_wait(&xs->backbuf_avail_cnd, &xs->flip_mtx);
329 pthread_mutex_unlock(&xs->flip_mtx);
345 xs = calloc(1,
sizeof(*xs));
356 struct _exsid * xs = exsid;
364 #ifdef EXSID_THREADED
386 struct _exsid *
const xs = exsid;
387 unsigned char buf[2];
393 fprintf(stderr,
"ERROR: exSID_init: invalid handle\n");
398 xserror(xs,
"Failed to open dynamic loader");
405 for (i = 0; i <
ARRAY_SIZE(xSsupported); i++) {
409 xserror(xs,
"ftdi_new failed");
414 xsdbg(
"Trying %s...\n", xSsupported[i].
desc);
415 xs->
cst = &xSsupported[i].xsc;
430 xserror(xs,
"No device could be opened");
436 xserror(xs,
"Failed to setup device");
441 xsdbg(
"Device setup\n");
448 xs->
hwvers = buf[0] << 8 | buf[1];
449 xsdbg(
"HV: %c, FV: %hhu\n", buf[0], buf[1]);
457 #ifdef EXSID_THREADED
464 ret = pthread_mutex_init(&xs->flip_mtx, NULL);
465 ret |= pthread_cond_init(&xs->backbuf_avail_cnd, NULL);
466 ret |= pthread_cond_init(&xs->backbuf_full_cnd, NULL);
467 ret |= pthread_create(&xs->thread_output, NULL, _exSID_thread_output, xs);
469 xserror(xs,
"Thread setup failed");
473 xsdbg(
"Thread setup\n");
477 xsdbg(
"Rock'n'roll!\n");
491 struct _exsid *
const xs = exsid;
500 #ifdef EXSID_THREADED
502 pthread_join(xs->thread_output, NULL);
503 pthread_cond_destroy(&xs->backbuf_full_cnd);
504 pthread_cond_destroy(&xs->backbuf_avail_cnd);
505 pthread_mutex_destroy(&xs->flip_mtx);
517 xserror(xs,
"Unable to close ftdi device: %d (%s)",
525 xsdbg(
"mean jitter: %.2f cycle(s) over %lu I/O ops\n",
526 ((
float)xs->accdrift/xs->accioops), xs->accioops);
527 xsdbg(
"bandwidth used for I/O ops: %lu%% (approx)\n",
528 100-(xs->accdelay*100/xs->acccycle));
529 xs->accdrift = xs->accioops = xs->accdelay = xs->acccycle = 0;
551 struct _exsid *
const xs = exsid;
578 struct _exsid *
const xs = exsid;
583 xsdbg(
"clk: %d\n", clock);
619 struct _exsid *
const xs = exsid;
624 xsdbg(
"auop: %d\n", operation);
665 struct _exsid *
const xs = exsid;
672 xsdbg(
"cs: %d\n", chip);
699 struct _exsid *
const xs = exsid;
717 xsdbg(
"HW model: %d\n", model);
733 struct _exsid *
const xs = exsid;
747 static inline void xSdelay(
struct _exsid *
const xs, uint_fast32_t cycles)
750 xs->accdelay += cycles;
758 xs->accdelay -= cycles;
771 struct _exsid *
const xs = exsid;
779 xs->acccycle += cycles;
798 static inline void _exSID_write(
struct _exsid *
const xs, uint_least8_t addr, uint8_t data,
int flush)
800 xSoutb(xs, (
unsigned char)addr, 0);
801 xSoutb(xs, (
unsigned char)data, flush);
816 int exSID_clkdwrite(
void *
const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t data)
818 struct _exsid *
const xs = exsid;
826 xsdbg(
"Invalid write: %.2" PRIxLEAST8
"\n", addr);
841 xsdbg(
"Impossible drift adjustment! %" PRIdFAST32
" cycles\n", xs->
clkdrift);
856 addr = (
unsigned char)(addr | (adj << 5));
858 xs->accdrift += (xs->
clkdrift - adj);
864 xs->acccycle += cycles;
869 _exSID_write(xs, addr, data, 0);
909 int exSID_clkdread(
void *
const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t * data)
911 struct _exsid *
const xs = exsid;
912 #ifndef EXSID_THREADED
919 if (
unlikely((addr < 0x19) || (addr > 0x1C))) {
920 xsdbg(
"Invalid read: %.2" PRIxLEAST8
"\n", addr);
936 xsdbg(
"Impossible drift adjustment! %" PRIdFAST32
" cycles", xs->
clkdrift);
939 xsdbg(
"Late read request! %" PRIdFAST32
" cycles\n", xs->
clkdrift);
946 addr = (
unsigned char)(addr | (adj << 5));
948 xs->accdrift += (xs->
clkdrift - adj);
954 xs->acccycle += cycles;
965 #else // !EXSID_THREADED
xSfw_write_data_p xSfw_write_data
Data write callback.
int exSID_clkdwrite(void *const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t data)
Timed write routine, attempts cycle-accurate writes.
#define XS_AD_IOCTRS
SID reset.
#define XS_AD_IOCTS1
select chip 1
int_fast32_t clkdrift_t
cycles is uint_fast32_t.
int xSfw_dlopen()
Attempt to dlopen a known working library to access FTDI chip.
#define XS_AD_IOCTHV
Hardware version query.
int exSID_clockselect(void *const exsid, int clock)
exSID+ clock selection routine.
clkdrift_t clkdrift
negative values mean we're lagging, positive mean we're ahead.
#define XSP_AD_IOCTC1
Select 1MHz clock.
char xSerrstr[256+1]
XS_ERRORBUF-byte max string for last error message.
This private structure holds hardware-dependent constants.
uint16_t hwvers
hardware version
FTDI access wrapper header file.
int exSID_chipselect(void *const exsid, int chip)
SID chipselect routine.
#define XS_CYCCHR
SID cycles between two consecutive chars.
#define XS_AD_IOCTSB
select both chips.
#define XSP_AD_IOCTAU
Audio Unmute.
int exSID_hwmodel(void *const exsid)
Device hardware model.
#define XSP_CYCIO
minimum cycles between two consecutive I/Os (addr + data)
int exSID_exit(void *const exsid)
Device exit routine.
clkdrift_t read_post_cycles
number of SID clocks spent in read op after data is actually read
#define XSP_AD_IOCTA1
Audio Mix: 8580 L / 6581 R.
#define XSP_AD_IOCTA3
Audio Mix: 6581 L / 6581 R.
#define XS_AD_IOCTD1
shortest delay (XS_MINDEL SID cycles)
int exSID_clkdread(void *const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t *data)
BLOCKING Timed read routine, attempts cycle-accurate reads.
int exSID_reset(void *const exsid)
SID reset routine.
#define XSP_CYCCS
cycles lost in chipselect()
clkdrift_t mindel_cycles
lowest number of SID clocks that can be accounted for in delay
void * ftdi
FTDI device handle.
xSfw_read_data_p xSfw_read_data
Data read callback.
#define XS_CYCIO
minimum cycles between two consecutive I/Os (addr + data)
int exSID_audio_op(void *const exsid, int operation)
exSID+ audio operations routine.
#define XS_AD_IOCTFV
Firmware version query.
clkdrift_t max_adj
maximum number of SID clocks that can be encoded in final delay for read()/write() ...
size_t buff_size
output buffer size
#define XSP_AD_IOCTAM
Audio Mute.
#define XSP_AD_IOCTCN
Select NTSC clock.
#define XSP_MINDEL
Smallest possible delay (with IOCTD1).
clkdrift_t write_cycles
number of SID clocks spent in write ops
#define XSP_USBPID
Default FTDI PID.
void exSID_free(void *exsid)
Deallocate an exSID handle.
const struct xSconsts_s *restrict cst
Pointer to constants used by all the hardware access routines.
unsigned int model
exSID device model in use
clkdrift_t read_pre_cycles
number of SID clocks spent in read op before data is actually read
#define XSP_MAXADJ
maximum encodable value for post write clock adjustment: must fit on 3 bits
const char * exSID_error_str(void *const exsid)
Returns a string describing the last recorded error.
xSfw_new_p xSfw_new
Handle allocation callback.
int backbuf_idx
current index for the back buffer
#define XS_MINDEL
Smallest possible delay (with IOCTD1).
xSfw_usb_open_desc_p xSfw_usb_open_desc
Device open callback.
#define XSP_AD_IOCTA2
Audio Mix: 8580 L / 8580 R.
#define xsdbg(format,...)
void * exSID_new(void)
Allocate an exSID handle.
clkdrift_t read_offset_cycles
read offset adjustment to align with writes (see function documentation)
int exSID_delay(void *const exsid, uint_fast32_t cycles)
Cycle accurate delay routine.
#define XSP_USBVID
Default FTDI VID.
#define xserror(xs, format,...)
#define XSP_AD_IOCTCP
Select PAL clock.
xSfw_get_error_string_p xSfw_get_error_string
Human readable error string callback.
int xSfw_usb_setup(void *ftdi, int baudrate, int latency)
Setup FTDI chip to match exSID firmware.
const char * desc
USB device description string.
int exSID_init(void *const exsid)
Device init routine.
#define XS_USBPID
Default FTDI PID.
libexsid private definitions header file.
void xSfw_dlclose()
Release dlopen'd library.
uint16_t exSID_hwversion(void *const exsid)
Hardware and firmware version of the device.
libexsid interface header file.
unsigned char *restrict backbuf
back buffer
#define XS_AD_IOCTS0
select chip 0
#define XSC_USBLAT
FTDI latency: 1-255ms in 1ms increments.
const struct xSconsts_s xsc
constants specific to the device
clkdrift_t csioctl_cycles
number of SID clocks spent in chip select ioctl
xSfw_free_p xSfw_free
Handle deallocation callback.
#define XS_MAXADJ
maximum encodable value for post write clock adjustment: must fit on 3 bits
#define XS_BUFFSZ
Must be multiple of user data transfer size or USB won't be happy. This floors XSC_BUFFMS.
#define XSP_AD_IOCTA0
Audio Mix: 6581 L / 8580 R.
#define XS_USBVID
Default FTDI VID.
xSfw_usb_close_p xSfw_usb_close
Device close callback.
#define XSP_BUFFSZ
Must be multiple of user data transfer size or USB won't be happy. This floors XSC_BUFFMS.