libexsid  2.1
exSID.c
Go to the documentation of this file.
1 //
2 // exSID.c
3 // A simple I/O library for exSID/exSID+ USB
4 //
5 // (C) 2015-2018,2021 Thibaut VARENE
6 // License: GPLv2 - http://www.gnu.org/licenses/gpl-2.0.html
7 
28 #include "exSID.h"
29 #include "exSID_defs.h"
30 #include "exSID_ftdiwrap.h"
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <inttypes.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <time.h>
37 
38 #ifdef EXSID_THREADED
39  #if defined(HAVE_PTHREAD_H)
40  #include <pthread.h>
41  #else
42  #error "No thread model available"
43  #endif
44 #endif // EXSID_TRHREADED
45 
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__)
49 
54 typedef int_fast32_t clkdrift_t;
55 
59 struct xSconsts_s {
60  unsigned int model;
68  size_t buff_size;
69 };
70 
72 static const struct {
73  const char * desc;
74  const int pid;
75  const int vid;
76  const struct xSconsts_s xsc;
77 } xSsupported[] = {
78  {
79  /* exSID USB */
80  .desc = XS_USBDSC,
81  .pid = XS_USBPID,
82  .vid = XS_USBVID,
83  .xsc = (struct xSconsts_s){
84  .model = XS_MODEL_STD,
85  .write_cycles = XS_CYCIO,
86  .read_pre_cycles = XS_CYCCHR,
87  .read_post_cycles = XS_CYCCHR,
88  .read_offset_cycles = -2, // see exSID_clkdread() documentation
89  .csioctl_cycles = XS_CYCCHR,
90  .mindel_cycles = XS_MINDEL,
91  .max_adj = XS_MAXADJ,
92  .buff_size = XS_BUFFSZ,
93  },
94  }, {
95  /* exSID+ USB */
96  .desc = XSP_USBDSC,
97  .pid = XSP_USBPID,
98  .vid = XSP_USBVID,
99  .xsc = (struct xSconsts_s){
100  .model = XS_MODEL_PLUS,
101  .write_cycles = XSP_CYCIO,
102  .read_pre_cycles = XSP_PRE_RD,
103  .read_post_cycles = XSP_POSTRD,
104  .read_offset_cycles = 0,
105  .csioctl_cycles = XSP_CYCCS,
106  .mindel_cycles = XSP_MINDEL,
107  .max_adj = XSP_MAXADJ,
108  .buff_size = XSP_BUFFSZ,
109  },
110  }
111 };
112 
114 struct _exsid {
117 
118  const struct xSconsts_s * restrict cst;
119  void * ftdi;
120 
121 #ifdef DEBUG
122  long accdrift;
123  unsigned long accioops;
124  unsigned long accdelay;
125  unsigned long acccycle; // ensures no overflow with exSID+ up to ~1h of continuous playback
126 #endif
127 
129  unsigned char * restrict backbuf;
130 
131 #ifdef EXSID_THREADED
132  // Variables for flip buffering
133  pthread_mutex_t flip_mtx;
134  pthread_cond_t backbuf_full_cnd, backbuf_avail_cnd;
135  unsigned char * restrict frontbuf;
136  _Bool backbuf_ready;
137  int postdelay;
138  pthread_t thread_output;
139 #endif // EXSID_THREADED
140 
141  uint16_t hwvers;
143 };
144 
152 static int _xSusleep(int usecs)
153 {
154  struct timespec tv;
155  int ret;
156 
157  xsdbg("sleep: %d\n", usecs);
158 
159  tv.tv_sec = usecs / 1000000;
160  tv.tv_nsec = (usecs % 1000000) * 1000;
161 
162  while (1) {
163  ret = nanosleep(&tv, &tv);
164  if (ret) {
165  if (EINTR == errno)
166  continue;
167  }
168  return ret;
169  }
170 }
171 
177 const char * exSID_error_str(void * const exsid)
178 {
179  const struct _exsid * const xs = exsid;
180  if (!exsid)
181  return NULL;
182  return (xs->xSerrstr);
183 }
184 
185 
194 static inline void xSwrite(struct _exsid * const xs, const unsigned char * buff, int size)
195 {
196  int ret = xSfw_write_data(xs->ftdi, buff, size);
197 #ifdef DEBUG
198  if (unlikely(ret < 0)) {
199  xsdbg("Error ftdi_write_data(%d): %s\n",
200  ret, xSfw_get_error_string(xs->ftdi));
201  }
202  if (unlikely(ret != size)) {
203  xsdbg("ftdi_write_data only wrote %d (of %d) bytes\n",
204  ret, size);
205  }
206 #endif
207 }
208 
219 static void xSread(struct _exsid * const xs, unsigned char * buff, int size)
220 {
221  int ret = 0;
222 
223  // libftdi implements a non-blocking read() call that returns 0 if no data was available
224  do {
225  ret += xSfw_read_data(xs->ftdi, buff+ret, size-ret);
226  } while (ret != size);
227 
228 #ifdef DEBUG
229  if (unlikely(ret < 0)) {
230  xsdbg("Error ftdi_read_data(%d): %s\n",
231  ret, xSfw_get_error_string(xs->ftdi));
232  }
233  if (unlikely(ret != size)) {
234  xsdbg("ftdi_read_data only read %d (of %d) bytes\n",
235  ret, size);
236  }
237 #endif
238 }
239 
240 
241 #ifdef EXSID_THREADED
242 
253 static void * _exSID_thread_output(void * arg)
254 {
255  struct _exsid * const xs = arg;
256  unsigned char * bufptr;
257  int frontbuf_idx, delay;
258 
259 #ifdef _GNU_SOURCE
260  pthread_setname_np(pthread_self(), "output");
261 #endif
262 
263  xsdbg("thread started\n");
264  while (1) {
265  pthread_mutex_lock(&xs->flip_mtx);
266 
267  // wait for backbuf full
268  while (!xs->backbuf_ready)
269  pthread_cond_wait(&xs->backbuf_full_cnd, &xs->flip_mtx);
270 
271  // Keep the critical section fast and short:
272  delay = xs->postdelay; // record postdelay
273  bufptr = xs->frontbuf;
274  frontbuf_idx = xs->backbuf_idx;
275  xs->frontbuf = xs->backbuf; // flip back to front
276  xs->backbuf = bufptr;
277  xs->backbuf_idx = 0;
278  xs->backbuf_ready = 0;
279 
280  // signal we're done and free mutex
281  pthread_cond_signal(&xs->backbuf_avail_cnd);
282 
283  pthread_mutex_unlock(&xs->flip_mtx);
284 
285  // this blocks outside of mutex held
286  xSwrite(xs, xs->frontbuf, frontbuf_idx);
287 
288  if (unlikely(delay)) {
289  if (unlikely(delay < 0)) { // exit condition
290  xsdbg("thread exiting!\n");
291  pthread_exit(NULL);
292  }
293  else if (XS_MODEL_STD == xs->cst->model)
294  _xSusleep(delay);
295  }
296  }
297 }
298 #endif // EXSID_THREADED
299 
308 static void xSoutb(struct _exsid * const xs, uint8_t byte, int flush)
309 {
310  xs->backbuf[xs->backbuf_idx++] = (unsigned char)byte;
311 
312  if (likely((xs->backbuf_idx < xs->cst->buff_size) && !flush))
313  return;
314 
315 #ifdef EXSID_THREADED
316  // buffer dance
317  pthread_mutex_lock(&xs->flip_mtx);
318 
319  xs->postdelay = flush;
320 
321  // signal backbuff full
322  xs->backbuf_ready = 1;
323  pthread_cond_signal(&xs->backbuf_full_cnd);
324 
325  // wait for buffer flipped
326  while (xs->backbuf_idx)
327  pthread_cond_wait(&xs->backbuf_avail_cnd, &xs->flip_mtx);
328 
329  pthread_mutex_unlock(&xs->flip_mtx);
330 #else // unthreaded
331  xSwrite(xs, xs->backbuf, xs->backbuf_idx);
332  xs->backbuf_idx = 0;
333  if (unlikely((flush > 1) && (XS_MODEL_STD == xs->cst->model)))
334  _xSusleep(flush);
335 #endif
336 }
337 
342 void * exSID_new(void)
343 {
344  struct _exsid * xs;
345  xs = calloc(1, sizeof(*xs));
346  return xs;
347 }
348 
354 void exSID_free(void * exsid)
355 {
356  struct _exsid * xs = exsid;
357 
358  if (!xs)
359  return;
360 
361  if (xSfw_free && xs->ftdi)
362  xSfw_free(xs->ftdi);
363 
364 #ifdef EXSID_THREADED
365  if (xs->frontbuf)
366  free(xs->frontbuf);
367 #endif
368 
369  if (xs->backbuf)
370  free(xs->backbuf);
371 
372  free(xs);
373 }
374 
384 int exSID_init(void * const exsid)
385 {
386  struct _exsid * const xs = exsid;
387  unsigned char buf[2];
388  int i, ret;
389 
390  xsdbg("libexsid v" EXSID_VERSION "\n");
391 
392  if (!exsid) {
393  fprintf(stderr, "ERROR: exSID_init: invalid handle\n");
394  return -1;
395  }
396 
397  if (xSfw_dlopen()) {
398  xserror(xs, "Failed to open dynamic loader");
399  return -1;
400  }
401 
402  /* Attempt to open all supported devices until first success.
403  * Cleanup ftdi after each try to avoid passing garbage around as we don't know what
404  * the FTDI open routines do with the pointer. */
405  for (i = 0; i < ARRAY_SIZE(xSsupported); i++) {
406  if (xSfw_new) {
407  xs->ftdi = xSfw_new();
408  if (!xs->ftdi) {
409  xserror(xs, "ftdi_new failed");
410  return -1;
411  }
412  }
413 
414  xsdbg("Trying %s...\n", xSsupported[i].desc);
415  xs->cst = &xSsupported[i].xsc; // setting unconditionnally avoids segfaults if user code calls exit() after failure.
416  ret = xSfw_usb_open_desc(&xs->ftdi, xSsupported[i].vid, xSsupported[i].pid, xSsupported[i].desc, NULL);
417  if (ret >= 0) {
418  xsdbg("Opened!\n");
419  break;
420  }
421  else {
422  xsdbg("Failed: %d (%s)\n", ret, xSfw_get_error_string(xs->ftdi));
423  if (xSfw_free)
424  xSfw_free(xs->ftdi);
425  xs->ftdi = NULL;
426  }
427  }
428 
429  if (i >= ARRAY_SIZE(xSsupported)) {
430  xserror(xs, "No device could be opened");
431  return -1;
432  }
433 
435  if (ret < 0) {
436  xserror(xs, "Failed to setup device");
437  return -1;
438  }
439 
440  // success - device is setup
441  xsdbg("Device setup\n");
442 
443  // Wait for device ready by trying to read hw version and wait for the answer
444  buf[0] = XS_AD_IOCTHV;
445  buf[1] = XS_AD_IOCTFV; // ok as we have a 2-byte RX buffer on PIC
446  xSwrite(xs, buf, 2);
447  xSread(xs, buf, 2);
448  xs->hwvers = buf[0] << 8 | buf[1]; // ensure proper order regardless of endianness
449  xsdbg("HV: %c, FV: %hhu\n", buf[0], buf[1]);
450 
451  xs->backbuf = malloc(xs->cst->buff_size);
452  if (!xs->backbuf) {
453  xserror(xs, "Out of memory!");
454  return -1;
455  }
456 
457 #ifdef EXSID_THREADED
458  xs->frontbuf = malloc(xs->cst->buff_size);
459  if (!xs->frontbuf) {
460  xserror(xs, "Out of memory!");
461  return -1;
462  }
463 
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);
468  if (ret) {
469  xserror(xs, "Thread setup failed");
470  return -1;
471  }
472 
473  xsdbg("Thread setup\n");
474 #endif
475 
476  xsdbg("buffer size: %zu bytes\n", xs->cst->buff_size);
477  xsdbg("Rock'n'roll!\n");
478 
479  return 0;
480 }
481 
489 int exSID_exit(void * const exsid)
490 {
491  struct _exsid * const xs = exsid;
492  int ret = 0;
493 
494  if (!xs)
495  return -1;
496 
497  if (xs->ftdi) {
498  exSID_reset(xs);
499 
500 #ifdef EXSID_THREADED
501  xSoutb(xs, XS_AD_IOCTD1, -1); // signal end of thread
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);
506  if (xs->frontbuf)
507  free(xs->frontbuf);
508  xs->frontbuf = NULL;
509 #endif
510 
511  if (xs->backbuf)
512  free(xs->backbuf);
513  xs->backbuf = NULL;
514 
515  ret = xSfw_usb_close(xs->ftdi);
516  if (ret < 0)
517  xserror(xs, "Unable to close ftdi device: %d (%s)",
518  ret, xSfw_get_error_string(xs->ftdi));
519 
520  if (xSfw_free)
521  xSfw_free(xs->ftdi);
522  xs->ftdi = NULL;
523 
524 #ifdef DEBUG
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;
530 #endif
531  }
532 
533  xs->clkdrift = 0;
534  xSfw_dlclose();
535 
536  return ret;
537 }
538 
539 
549 int exSID_reset(void * const exsid)
550 {
551  struct _exsid * const xs = exsid;
552 
553  if (!xs)
554  return -1;
555 
556  xsdbg("reset\n");
557 
558  xSoutb(xs, XS_AD_IOCTRS, 100); // this will stall
559 
560  xs->clkdrift = 0;
561 
562  return 0;
563 }
564 
565 
576 int exSID_clockselect(void * const exsid, int clock)
577 {
578  struct _exsid * const xs = exsid;
579 
580  if (!xs)
581  return -1;
582 
583  xsdbg("clk: %d\n", clock);
584 
585  if (XS_MODEL_PLUS != xs->cst->model)
586  return -1;
587 
588  switch (clock) {
589  case XS_CL_PAL:
590  xSoutb(xs, XSP_AD_IOCTCP, 100);
591  break;
592  case XS_CL_NTSC:
593  xSoutb(xs, XSP_AD_IOCTCN, 100);
594  break;
595  case XS_CL_1MHZ:
596  xSoutb(xs, XSP_AD_IOCTC1, 100);
597  break;
598  default:
599  return -1;
600  }
601 
602  xs->clkdrift = 0; // reset drift
603 
604  return 0;
605 }
606 
617 int exSID_audio_op(void * const exsid, int operation)
618 {
619  struct _exsid * const xs = exsid;
620 
621  if (!xs)
622  return -1;
623 
624  xsdbg("auop: %d\n", operation);
625 
626  if (XS_MODEL_PLUS != xs->cst->model)
627  return -1;
628 
629  switch (operation) {
630  case XS_AU_6581_8580:
631  xSoutb(xs, XSP_AD_IOCTA0, 0);
632  break;
633  case XS_AU_8580_6581:
634  xSoutb(xs, XSP_AD_IOCTA1, 0);
635  break;
636  case XS_AU_8580_8580:
637  xSoutb(xs, XSP_AD_IOCTA2, 0);
638  break;
639  case XS_AU_6581_6581:
640  xSoutb(xs, XSP_AD_IOCTA3, 0);
641  break;
642  case XS_AU_MUTE:
643  xSoutb(xs, XSP_AD_IOCTAM, 0);
644  break;
645  case XS_AU_UNMUTE:
646  xSoutb(xs, XSP_AD_IOCTAU, 0);
647  break;
648  default:
649  return -1;
650  }
651 
652  return 0;
653 }
654 
663 int exSID_chipselect(void * const exsid, int chip)
664 {
665  struct _exsid * const xs = exsid;
666 
667  if (!xs)
668  return -1;
669 
670  xs->clkdrift -= xs->cst->csioctl_cycles;
671 
672  xsdbg("cs: %d\n", chip);
673 
674  switch (chip) {
675  case XS_CS_CHIP0:
676  xSoutb(xs, XS_AD_IOCTS0, 0);
677  break;
678  case XS_CS_CHIP1:
679  xSoutb(xs, XS_AD_IOCTS1, 0);
680  break;
681  case XS_CS_BOTH:
682  xSoutb(xs, XS_AD_IOCTSB, 0);
683  break;
684  default:
685  return -1;
686  }
687 
688  return 0;
689 }
690 
697 int exSID_hwmodel(void * const exsid)
698 {
699  struct _exsid * const xs = exsid;
700  int model;
701 
702  if (!xs)
703  return -1;
704 
705  switch (xs->cst->model) {
706  case XS_MODEL_STD:
707  model = XS_MD_STD;
708  break;
709  case XS_MODEL_PLUS:
710  model = XS_MD_PLUS;
711  break;
712  default:
713  model = -1;
714  break;
715  }
716 
717  xsdbg("HW model: %d\n", model);
718 
719  return model;
720 }
721 
731 uint16_t exSID_hwversion(void * const exsid)
732 {
733  struct _exsid * const xs = exsid;
734 
735  if (!xs)
736  return 0xFFFF;
737 
738  return xs->hwvers;
739 }
740 
747 static inline void xSdelay(struct _exsid * const xs, uint_fast32_t cycles)
748 {
749 #ifdef DEBUG
750  xs->accdelay += cycles;
751 #endif
752  while (likely(cycles >= xs->cst->mindel_cycles)) {
753  xSoutb(xs, XS_AD_IOCTD1, 0);
754  cycles -= xs->cst->mindel_cycles;
755  xs->clkdrift -= xs->cst->mindel_cycles;
756  }
757 #ifdef DEBUG
758  xs->accdelay -= cycles;
759 #endif
760 }
761 
769 int exSID_delay(void * const exsid, uint_fast32_t cycles)
770 {
771  struct _exsid * const xs = exsid;
772  uint_fast32_t delay;
773 
774  if (unlikely(!xs))
775  return -1;
776 
777  xs->clkdrift += cycles;
778 #ifdef DEBUG
779  xs->acccycle += cycles;
780 #endif
781 
782  if (unlikely(xs->clkdrift <= xs->cst->write_cycles)) // never delay for less than a full write would need
783  return 1; // too short
784 
785  delay = xs->clkdrift - xs->cst->write_cycles;
786  xSdelay(xs, delay);
787 
788  return 0;
789 }
790 
798 static inline void _exSID_write(struct _exsid * const xs, uint_least8_t addr, uint8_t data, int flush)
799 {
800  xSoutb(xs, (unsigned char)addr, 0);
801  xSoutb(xs, (unsigned char)data, flush);
802 }
803 
816 int exSID_clkdwrite(void * const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t data)
817 {
818  struct _exsid * const xs = exsid;
819  int adj;
820 
821  if (unlikely(!xs))
822  return -1;
823 
824 #ifdef DEBUG
825  if (unlikely(addr > 0x18)) {
826  xsdbg("Invalid write: %.2" PRIxLEAST8 "\n", addr);
827  exSID_delay(xs, cycles);
828  return 1;
829  }
830 #endif
831 
832  // actual write will cost write_cycles. Delay for cycles - write_cycles then account for the write
833  xs->clkdrift += cycles;
834  if (xs->clkdrift > xs->cst->write_cycles)
835  xSdelay(xs, xs->clkdrift - xs->cst->write_cycles);
836 
837  xs->clkdrift -= xs->cst->write_cycles; // write is going to consume write_cycles clock ticks
838 
839 #ifdef DEBUG
840  if (xs->clkdrift >= xs->cst->mindel_cycles)
841  xsdbg("Impossible drift adjustment! %" PRIdFAST32 " cycles\n", xs->clkdrift);
842  else if (xs->clkdrift < 0)
843  xs->accdrift += xs->clkdrift;
844 #endif
845 
846  /* if we are still going to be early, delay actual write by up to max_adj ticks
847  At this point it is guaranted that clkdrift will be < mindel_cycles. */
848  if (likely(xs->clkdrift >= 0)) {
849  adj = xs->clkdrift % (xs->cst->max_adj+1);
850  /* if max_adj is >= clkdrift, modulo will give the same results
851  as the correct test:
852  adj = (clkdrift < max_adj ? clkdrift : max_adj)
853  but without an extra conditional branch. If is is < max_adj, then it
854  seems to provide better results by evening jitter accross writes. So
855  it's the preferred solution for all cases. */
856  addr = (unsigned char)(addr | (adj << 5)); // final delay encoded in top 3 bits of address
857 #ifdef DEBUG
858  xs->accdrift += (xs->clkdrift - adj);
859 #endif
860  //xsdbg("drft: %d, adj: %d, addr: %.2hhx, data: %.2hhx\n", clkdrift, adj, (char)(addr | (adj << 5)), data);
861  }
862 
863 #ifdef DEBUG
864  xs->acccycle += cycles;
865  xs->accioops++;
866 #endif
867 
868  //xsdbg("delay: %d, clkdrift: %d\n", cycles, xs->clkdrift);
869  _exSID_write(xs, addr, data, 0);
870 
871  return 0;
872 }
873 
909 int exSID_clkdread(void * const exsid, uint_fast32_t cycles, uint_least8_t addr, uint8_t * data)
910 {
911  struct _exsid * const xs = exsid;
912 #ifndef EXSID_THREADED
913  int adj;
914 
915  if (unlikely(!xs || !data))
916  return -1;
917 
918 #ifdef DEBUG
919  if (unlikely((addr < 0x19) || (addr > 0x1C))) {
920  xsdbg("Invalid read: %.2" PRIxLEAST8 "\n", addr);
921  exSID_delay(xs, cycles);
922  return 1;
923  }
924 #endif
925 
926  // actual read will happen after read_pre_cycles. Delay for cycles - read_pre_cycles then account for the read
927  xs->clkdrift += xs->cst->read_offset_cycles; // 2-cycle offset adjustement, see function documentation.
928  xs->clkdrift += cycles;
929  if (xs->clkdrift > xs->cst->read_pre_cycles)
930  xSdelay(xs, xs->clkdrift - xs->cst->read_pre_cycles);
931 
932  xs->clkdrift -= xs->cst->read_pre_cycles; // read request is going to consume read_pre_cycles clock ticks
933 
934 #ifdef DEBUG
935  if (xs->clkdrift > xs->cst->mindel_cycles)
936  xsdbg("Impossible drift adjustment! %" PRIdFAST32 " cycles", xs->clkdrift);
937  else if (xs->clkdrift < 0) {
938  xs->accdrift += xs->clkdrift;
939  xsdbg("Late read request! %" PRIdFAST32 " cycles\n", xs->clkdrift);
940  }
941 #endif
942 
943  // if we are still going to be early, delay actual read by up to max_adj ticks
944  if (likely(xs->clkdrift >= 0)) {
945  adj = xs->clkdrift % (xs->cst->max_adj+1); // see clkdwrite()
946  addr = (unsigned char)(addr | (adj << 5)); // final delay encoded in top 3 bits of address
947 #ifdef DEBUG
948  xs->accdrift += (xs->clkdrift - adj);
949 #endif
950  //xsdbg("drft: %d, adj: %d, addr: %.2hhx, data: %.2hhx\n", clkdrift, adj, (char)(addr | (adj << 5)), data);
951  }
952 
953 #ifdef DEBUG
954  xs->acccycle += cycles;
955  xs->accioops++;
956 #endif
957 
958  // after read has completed, at least another read_post_cycles will have been spent
959  xs->clkdrift -= xs->cst->read_post_cycles;
960 
961  //xsdbg("delay: %d, clkdrift: %d\n", cycles, clkdrift);
962  xSoutb(xs, addr, 1);
963  xSread(xs, data, 1); // blocking
964  return 0;
965 #else // !EXSID_THREADED
966  exSID_delay(xs, cycles);
967  return -1;
968 #endif
969 }
#define XS_MODEL_STD
Definition: exSID_defs.h:96
exSID private handle
Definition: exSID.c:114
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.
Definition: exSID.c:816
#define XS_AD_IOCTRS
SID reset.
Definition: exSID_defs.h:86
exSID USB
Definition: exSID.h:58
#define XS_AD_IOCTS1
select chip 1
Definition: exSID_defs.h:81
int_fast32_t clkdrift_t
cycles is uint_fast32_t.
Definition: exSID.c:54
#define unlikely(x)
Definition: exSID_defs.h:110
int xSfw_dlopen()
Attempt to dlopen a known working library to access FTDI chip.
#define XS_AD_IOCTHV
Hardware version query.
Definition: exSID_defs.h:85
int exSID_clockselect(void *const exsid, int clock)
exSID+ clock selection routine.
Definition: exSID.c:576
clkdrift_t clkdrift
negative values mean we're lagging, positive mean we're ahead.
Definition: exSID.c:116
#define XSP_AD_IOCTC1
Select 1MHz clock.
Definition: exSID_defs.h:67
char xSerrstr[256+1]
XS_ERRORBUF-byte max string for last error message.
Definition: exSID.c:142
select NTSC clock
Definition: exSID.h:52
This private structure holds hardware-dependent constants.
Definition: exSID.c:59
uint16_t hwvers
hardware version
Definition: exSID.c:141
FTDI access wrapper header file.
int exSID_chipselect(void *const exsid, int chip)
SID chipselect routine.
Definition: exSID.c:663
#define XS_CYCCHR
SID cycles between two consecutive chars.
Definition: exSID_defs.h:42
#define XS_AD_IOCTSB
select both chips.
Definition: exSID_defs.h:82
#define XSP_AD_IOCTAU
Audio Unmute.
Definition: exSID_defs.h:75
6581
Definition: exSID.h:34
int exSID_hwmodel(void *const exsid)
Device hardware model.
Definition: exSID.c:697
#define likely(x)
Definition: exSID_defs.h:109
mix: 8580 L / 6581 R
Definition: exSID.h:42
const int vid
USB VID.
Definition: exSID.c:75
#define XSP_CYCIO
minimum cycles between two consecutive I/Os (addr + data)
Definition: exSID_defs.h:56
int exSID_exit(void *const exsid)
Device exit routine.
Definition: exSID.c:489
clkdrift_t read_post_cycles
number of SID clocks spent in read op after data is actually read
Definition: exSID.c:63
#define XSP_AD_IOCTA1
Audio Mix: 8580 L / 6581 R.
Definition: exSID_defs.h:70
#define XSP_AD_IOCTA3
Audio Mix: 6581 L / 6581 R.
Definition: exSID_defs.h:73
#define XS_AD_IOCTD1
shortest delay (XS_MINDEL SID cycles)
Definition: exSID_defs.h:77
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.
Definition: exSID.c:909
int exSID_reset(void *const exsid)
SID reset routine.
Definition: exSID.c:549
#define XSP_CYCCS
cycles lost in chipselect()
Definition: exSID_defs.h:61
clkdrift_t mindel_cycles
lowest number of SID clocks that can be accounted for in delay
Definition: exSID.c:66
void * ftdi
FTDI device handle.
Definition: exSID.c:119
xSfw_read_data_p xSfw_read_data
Data read callback.
#define XS_CYCIO
minimum cycles between two consecutive I/Os (addr + data)
Definition: exSID_defs.h:48
int exSID_audio_op(void *const exsid, int operation)
exSID+ audio operations routine.
Definition: exSID.c:617
#define XS_AD_IOCTFV
Firmware version query.
Definition: exSID_defs.h:84
clkdrift_t max_adj
maximum number of SID clocks that can be encoded in final delay for read()/write() ...
Definition: exSID.c:67
size_t buff_size
output buffer size
Definition: exSID.c:68
#define XSP_AD_IOCTAM
Audio Mute.
Definition: exSID_defs.h:74
#define XSP_AD_IOCTCN
Select NTSC clock.
Definition: exSID_defs.h:66
#define XSP_MINDEL
Smallest possible delay (with IOCTD1).
Definition: exSID_defs.h:55
clkdrift_t write_cycles
number of SID clocks spent in write ops
Definition: exSID.c:61
#define XSP_USBPID
Default FTDI PID.
Definition: exSID_defs.h:93
void exSID_free(void *exsid)
Deallocate an exSID handle.
Definition: exSID.c:354
const int pid
USB PID.
Definition: exSID.c:74
#define XSP_USBDSC
Definition: exSID_defs.h:94
const struct xSconsts_s *restrict cst
Pointer to constants used by all the hardware access routines.
Definition: exSID.c:118
#define XS_BDRATE
2Mpbs
Definition: exSID_defs.h:39
#define EXSID_VERSION
Definition: exSID.h:26
unsigned int model
exSID device model in use
Definition: exSID.c:60
clkdrift_t read_pre_cycles
number of SID clocks spent in read op before data is actually read
Definition: exSID.c:62
#define XSP_MAXADJ
maximum encodable value for post write clock adjustment: must fit on 3 bits
Definition: exSID_defs.h:59
const char * exSID_error_str(void *const exsid)
Returns a string describing the last recorded error.
Definition: exSID.c:177
xSfw_new_p xSfw_new
Handle allocation callback.
mix: 8580 L and R
Definition: exSID.h:43
int backbuf_idx
current index for the back buffer
Definition: exSID.c:128
#define XS_MINDEL
Smallest possible delay (with IOCTD1).
Definition: exSID_defs.h:47
select PAL clock
Definition: exSID.h:51
#define XSP_POSTRD
Definition: exSID_defs.h:58
#define XS_ERRORBUF
Definition: exSID.c:47
xSfw_usb_open_desc_p xSfw_usb_open_desc
Device open callback.
#define XSP_AD_IOCTA2
Audio Mix: 8580 L / 8580 R.
Definition: exSID_defs.h:71
mute output
Definition: exSID.h:45
#define xsdbg(format,...)
Definition: exSID_defs.h:102
void * exSID_new(void)
Allocate an exSID handle.
Definition: exSID.c:342
clkdrift_t read_offset_cycles
read offset adjustment to align with writes (see function documentation)
Definition: exSID.c:64
int exSID_delay(void *const exsid, uint_fast32_t cycles)
Cycle accurate delay routine.
Definition: exSID.c:769
#define XSP_USBVID
Default FTDI VID.
Definition: exSID_defs.h:92
#define ARRAY_SIZE(x)
Definition: exSID.c:46
select 1MHz clock
Definition: exSID.h:53
#define xserror(xs, format,...)
Definition: exSID.c:48
#define XSP_AD_IOCTCP
Select PAL clock.
Definition: exSID_defs.h:65
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.
mix: 6581 L / 8580 R
Definition: exSID.h:41
const char * desc
USB device description string.
Definition: exSID.c:73
int exSID_init(void *const exsid)
Device init routine.
Definition: exSID.c:384
exSID+ USB
Definition: exSID.h:59
#define XS_MODEL_PLUS
Definition: exSID_defs.h:97
#define XS_USBPID
Default FTDI PID.
Definition: exSID_defs.h:89
libexsid private definitions header file.
#define XSP_PRE_RD
Definition: exSID_defs.h:57
void xSfw_dlclose()
Release dlopen'd library.
uint16_t exSID_hwversion(void *const exsid)
Hardware and firmware version of the device.
Definition: exSID.c:731
libexsid interface header file.
unsigned char *restrict backbuf
back buffer
Definition: exSID.c:129
#define XS_AD_IOCTS0
select chip 0
Definition: exSID_defs.h:80
#define XSC_USBLAT
FTDI latency: 1-255ms in 1ms increments.
Definition: exSID_defs.h:35
Both chips.
Definition: exSID.h:36
8580
Definition: exSID.h:35
const struct xSconsts_s xsc
constants specific to the device
Definition: exSID.c:76
clkdrift_t csioctl_cycles
number of SID clocks spent in chip select ioctl
Definition: exSID.c:65
xSfw_free_p xSfw_free
Handle deallocation callback.
#define XS_MAXADJ
maximum encodable value for post write clock adjustment: must fit on 3 bits
Definition: exSID_defs.h:49
unmute output
Definition: exSID.h:46
#define XS_BUFFSZ
Must be multiple of user data transfer size or USB won't be happy. This floors XSC_BUFFMS.
Definition: exSID_defs.h:44
#define XSP_AD_IOCTA0
Audio Mix: 6581 L / 8580 R.
Definition: exSID_defs.h:69
#define XS_USBVID
Default FTDI VID.
Definition: exSID_defs.h:88
#define XS_USBDSC
Definition: exSID_defs.h:90
xSfw_usb_close_p xSfw_usb_close
Device close callback.
mix: 6581 L and R
Definition: exSID.h:44
#define XSP_BUFFSZ
Must be multiple of user data transfer size or USB won't be happy. This floors XSC_BUFFMS.
Definition: exSID_defs.h:54