OpenCBM
cbmctrl.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version
5  * 2 of the License, or (at your option) any later version.
6  *
7  * Copyright 1999-2004 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
8  * Modifications for cbm4win and general rework Copyright 2001-2007 Spiro Trikaliotis
9  * Additions Copyright 2006,2011 Wolfgang Moser (http://d81.de)
10  * Additions Copyright 2011 Thomas Winkler
11  */
12 
13 #include "opencbm.h"
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <getopt.h>
19 #include <assert.h>
20 
21 #include "arch.h"
22 #include "libmisc.h"
23 
24 typedef
25 enum {
26  PA_UNSPEC = 0,
27  PA_PETSCII,
28  PA_RAW
29 } PETSCII_RAW;
30 
32 typedef struct {
33  int argc;
34  char **argv;
35  int ownargv;
36  int error;
37  int help;
38  int version;
39  char *adapter;
40  PETSCII_RAW petsciiraw;
41 } OPTIONS;
42 
43 typedef int (*mainfunc)(CBM_FILE fd, OPTIONS * const options);
44 
45 
46 static const unsigned char prog_tdchange[] = {
47 #include "tdchange.inc"
48 };
49 
50 
51 /*
52  * Output version information
53  */
54 static int do_version(CBM_FILE fd, OPTIONS * const options)
55 {
56  printf("cbmctrl version " OPENCBM_VERSION ", built on " __DATE__ " at " __TIME__ "\n");
57 
58  return 0;
59 }
60 
61 static int check_if_parameters_ok(OPTIONS * const options)
62 {
63  if (options->argc != 0)
64  {
65  fprintf(stderr, "Extra parameter, aborting...\n");
66  return 1;
67  }
68  return 0;
69 }
70 
71 static int get_argument_char(OPTIONS * const options, unsigned char *where)
72 {
73  if (options->argc > 0)
74  {
75  *where = arch_atoc(options->argv[0]);
76  options->argv++;
77  options->argc--;
78  return 0;
79  }
80  else
81  {
82  fprintf(stderr, "Not enough parameters, aborting!\n");
83  return 1;
84  }
85 }
86 
87 static int
88 get_argument_string(OPTIONS * const options, char *where[], unsigned int *len)
89 {
90  if (options->argc > 0)
91  {
92  if (where)
93  *where = options->argv[0];
94 
95  if (len)
96  *len = strlen(*where);
97 
98  options->argv++;
99  options->argc--;
100  return 0;
101  }
102  else
103  {
104  fprintf(stderr, "Not enough parameters, aborting!\n");
105  return 1;
106  }
107 }
108 
109 static int get_argument_file_for_write(OPTIONS * const options, FILE **f)
110 {
111  char *filename = NULL;
112 
113  assert(f);
114 
115  if (options->argc > 0)
116  {
117  get_argument_string(options, &filename, NULL);
118 
119  if (filename && strcmp(filename, "-") == 0)
120  filename = NULL;
121  }
122 
123  if (check_if_parameters_ok(options))
124  return 1;
125 
126  if(filename != NULL)
127  {
128  /* a filename (other than simply "-") was given, open that file */
129 
130  *f = fopen(filename, "wb");
131  }
132  else
133  {
134  /* no filename was given, open stdout in binary mode */
135 
136  *f = arch_fdopen(arch_fileno(stdout), "wb");
137 
138  /* set binary mode for output stream */
139 
140  if (*f != NULL)
141  arch_setbinmode(arch_fileno(stdout));
142  }
143 
144  if(*f == NULL)
145  {
146  arch_error(0, arch_get_errno(), "could not open output file: %s",
147  (filename != 0) ? filename : "stdout");
148 
149  return 1;
150  }
151 
152  return 0;
153 }
154 
155 static int get_argument_file_for_read(OPTIONS * const options, FILE **f, char **fn)
156 {
157  char *filename = NULL;
158 
159  assert(f);
160 
161  if (options->argc > 0)
162  {
163  get_argument_string(options, &filename, NULL);
164 
165  if (filename && strcmp(filename, "-") == 0)
166  filename = NULL;
167  }
168 
169  if (check_if_parameters_ok(options))
170  return 1;
171 
172  if(filename == NULL)
173  {
174  filename = "(stdin)";
175  *f = stdin;
176 
177  // set binary mode for input stream
178 
179  arch_setbinmode(arch_fileno(stdin));
180  }
181  else
182  {
183  off_t filesize;
184 
185  *f = fopen(filename, "rb");
186  if(*f == NULL)
187  {
188  arch_error(0, arch_get_errno(), "could not open %s", filename);
189  return 1;
190  }
191  if(arch_filesize(filename, &filesize))
192  {
193  arch_error(0, arch_get_errno(), "could not stat %s", filename);
194  return 1;
195  }
196  }
197 
198  if (fn)
199  *fn = filename;
200 
201  return 0;
202 }
203 
204 static int
205 process_individual_option(OPTIONS * const options, const char short_options[], struct option long_options[])
206 {
207  static int firstcall = 1;
208  int option;
209 
210  if (firstcall)
211  {
212  firstcall = 0;
213 
214  optind = 0;
215 
216  --options->argv;
217  ++options->argc;
218  }
219 
220 
221  option = getopt_long(options->argc, options->argv, short_options, long_options, NULL);
222 
223  if (option == EOF)
224  {
225  // skip the options that are already processed
226  //optind --;
227 
228  options->argc -= optind;
229  options->argv += optind;
230  }
231 
232  return option;
233 }
234 
235 
236 static int
237 skip_options(OPTIONS * const options)
238 {
239  if (process_individual_option(options, "+", NULL) != EOF)
240  return 1;
241  else
242  return 0;
243 }
244 
245 static int hex2val(const char ch)
246 {
247  if (ch>='0' && ch<='9')
248  return ch - '0';
249 
250  if (ch>='A' && ch<='F')
251  return ch - 'A' + 10;
252 
253  if (ch>='a' && ch<='f')
254  return ch - 'a' + 10;
255 
256  fprintf(stderr,
257  (ch==0) ? "Not enough digits for hex value given\n"
258  : "Unknown hex character '%c'.\n", ch);
259  return -1;
260 }
261 
262 static int
263 process_specific_byte_parameter(char *string, int stringlen, PETSCII_RAW petsciiraw)
264 {
265  int size = 0;
266  char *pread = string;
267  char *pwrite = pread;
268 
269  while (*pread != 0)
270  {
271  char ch = *pread++;
272 
273  if (ch == '%')
274  {
275  ch = *pread++;
276 
277  if (ch != '%')
278  {
279  int val = hex2val(ch);
280  int val2;
281 
282  if (val < 0)
283  return -1;
284 
285  val2 = hex2val(*pread++);
286 
287  if (val2 < 0)
288  return -1;
289 
290  ch = (val << 4) | val2;
291  }
292 
293  *pwrite++ = ch;
294  size++;
295  }
296  else
297  {
298  if (petsciiraw == PA_PETSCII)
299  ch = cbm_ascii2petscii_c(ch);
300 
301  *pwrite++ = ch;
302  size++;
303  }
304  }
305 
306  *pwrite = 0;
307 
308  return size;
309 }
310 
311 static char *
312 string_concat(const char *string1, int len1, const char *string2, int len2)
313 {
314  char *buffer;
315 
316  if ((string1 != NULL) && (len1 == 0))
317  len1 = strlen(string1);
318 
319  if ((string2 != NULL) && (len2 == 0))
320  len2 = strlen(string2);
321 
322  buffer = calloc(1, len1 + len2 + 1);
323 
324  if (buffer)
325  {
326  if (string1)
327  memcpy(buffer, string1, len1);
328 
329  if (string2)
330  memcpy(buffer + len1, string2, len2);
331  }
332 
333  return buffer;
334 }
335 
336 static int
337 get_extended_argument_string(int extended,
338  OPTIONS * const options,
339  char *string[], unsigned int *stringlen)
340 {
341  int rv;
342  char *commandline = NULL;
343  char *extended_commandline = NULL;
344  unsigned int commandline_len = 0;
345  unsigned int extended_commandline_len = 0;
346 
347  rv = get_argument_string(options, &commandline, &commandline_len);
348 
349  if (rv)
350  return 1;
351 
352  if (extended)
353  {
354  int n = process_specific_byte_parameter(commandline, commandline_len, options->petsciiraw);
355 
356  if (n < 0)
357  return 1;
358 
359  commandline_len = n;
360  }
361  else
362  {
363  // only convert ASCII -> PETSCII if we do not have extended syntax
364  // (with extended syntax, this is done "on the fly" while converting
365  // the % - style values into characters.)
366 
367  if (options->petsciiraw == PA_PETSCII)
368  cbm_ascii2petscii(commandline);
369  }
370 
371  // now, check if there are more command-line parameters
372 
373  if (options->argc > 0)
374  {
375  char *p;
376 
377  // get memory for the additional data
378 
379  // get 2 byte more than we have parameters, as we will append
380  // \r and \0 at the end of the buffer!
381 
382  extended_commandline = malloc(options->argc + 1);
383 
384  if (extended_commandline == NULL)
385  {
386  fprintf(stderr, "Not enough memory for all parameters, aborting...\n");
387  return 1;
388  }
389 
390  // write data in the memory
391 
392  p = extended_commandline;
393 
394  while (--options->argc >= 0)
395  {
396  char *tail;
397  long c;
398 
399  c = strtol(options->argv[0], &tail, 0);
400 
401  if(c < 0 || c > 0xff || *tail)
402  {
403  arch_error(0, 0, "invalid byte: %s", options->argv[0]);
404  return 1;
405  }
406  *p++ = (char) c;
407  extended_commandline_len++;
408 
409  options->argv++;
410  }
411 
412  ++options->argc;
413 
414  // end the string with a \0. This is not really needed, but
415  // convenient when debugging.
416 
417  *p = 0;
418 
419  }
420 
421  *string = string_concat(commandline, commandline_len, extended_commandline, extended_commandline_len);
422  *stringlen = commandline_len + extended_commandline_len;
423 
424  if (extended_commandline)
425  free(extended_commandline);
426 
427  if (check_if_parameters_ok(options))
428  return 1;
429 
430  return 0;
431 }
432 
433 /*
434  * Simple wrapper for lock
435  */
436 static int do_lock(CBM_FILE fd, OPTIONS * const options)
437 {
438  int rv = skip_options(options);
439 
440  rv = rv || check_if_parameters_ok(options);
441 
442  if (rv == 0)
443  cbm_lock(fd);
444 
445  return rv;
446 }
447 
448 /*
449  * Simple wrapper for unlock
450  */
451 static int do_unlock(CBM_FILE fd, OPTIONS * const options)
452 {
453  int rv = skip_options(options);
454 
455  rv = rv || check_if_parameters_ok(options);
456 
457  if (rv == 0)
458  cbm_unlock(fd);
459 
460  return rv;
461 }
462 
463 /*
464  * Simple wrapper for reset
465  */
466 static int do_reset(CBM_FILE fd, OPTIONS * const options)
467 {
468  int rv = skip_options(options);
469 
470  rv = rv || check_if_parameters_ok(options);
471 
472  if (rv == 0)
473  rv = cbm_reset(fd);
474 
475  return rv;
476 }
477 
478 /*
479  * Simple wrapper for clk
480  */
481 static int do_iec_clk(CBM_FILE fd, OPTIONS * const options)
482 {
483  int rv = skip_options(options);
484 
485  rv = rv || check_if_parameters_ok(options);
486 
487  if (rv == 0)
488  cbm_iec_set(fd, IEC_CLOCK);
489 
490  return rv;
491 }
492 
493 /*
494  * Simple wrapper for uclk
495  */
496 static int do_iec_uclk(CBM_FILE fd, OPTIONS * const options)
497 {
498  int rv = skip_options(options);
499 
500  rv = rv || check_if_parameters_ok(options);
501 
502  if (rv == 0)
504 
505  return rv;
506 }
507 
508 /*
509  * Simple wrapper for listen
510  */
511 static int do_listen(CBM_FILE fd, OPTIONS * const options)
512 {
513  int rv;
514  unsigned char unit;
515  unsigned char secondary;
516 
517  rv = skip_options(options);
518 
519  rv = rv || get_argument_char(options, &unit);
520  rv = rv || get_argument_char(options, &secondary);
521 
522  if (rv || check_if_parameters_ok(options))
523  return 1;
524 
525  return cbm_listen(fd, unit, secondary);
526 }
527 
528 /*
529  * Simple wrapper for talk
530  */
531 static int do_talk(CBM_FILE fd, OPTIONS * const options)
532 {
533  int rv;
534  unsigned char unit;
535  unsigned char secondary;
536 
537  rv = skip_options(options);
538 
539  rv = rv || get_argument_char(options, &unit);
540  rv = rv || get_argument_char(options, &secondary);
541 
542  if (rv || check_if_parameters_ok(options))
543  return 1;
544 
545  return cbm_talk(fd, unit, secondary);
546 }
547 
548 /*
549  * Simple wrapper for unlisten
550  */
551 static int do_unlisten(CBM_FILE fd, OPTIONS * const options)
552 {
553  int rv = skip_options(options);
554 
555  rv = rv || check_if_parameters_ok(options);
556 
557  if (rv == 0)
558  rv = cbm_unlisten(fd);
559 
560  return rv;
561 }
562 
563 /*
564  * Simple wrapper for untalk
565  */
566 static int do_untalk(CBM_FILE fd, OPTIONS * const options)
567 {
568  int rv = skip_options(options);
569 
570  rv = rv || check_if_parameters_ok(options);
571 
572  if (rv == 0)
573  rv = cbm_untalk(fd);
574 
575  return rv;
576 }
577 
578 /*
579  * Simple wrapper for open
580  */
581 static int do_open(CBM_FILE fd, OPTIONS * const options)
582 {
583  int rv;
584  unsigned char unit;
585  unsigned char secondary;
586  char *filename;
587  unsigned int filenamelen = 0;
588 
589  int extended = 0;
590  int c;
591  static const char short_options[] = "+e";
592  static struct option long_options[] =
593  {
594  {"extended", no_argument, NULL, 'e'},
595  {NULL, no_argument, NULL, 0 }
596  };
597 
598  // first of all, process the options given
599 
600  while ((c = process_individual_option(options, short_options, long_options)) != EOF)
601  {
602  switch (c)
603  {
604  case 'e':
605  extended = 1;
606  break;
607 
608  default:
609  return 1;
610  }
611  }
612 
613  rv = get_argument_char(options, &unit);
614  rv = rv || get_argument_char(options, &secondary);
615  rv = rv || get_extended_argument_string(extended, options, &filename, &filenamelen);
616 
617  if (rv)
618  return 1;
619 
620  rv = cbm_open(fd, unit, secondary, filename, filenamelen);
621 
622  free(filename);
623 
624  return rv;
625 }
626 
627 /*
628  * Simple wrapper for close
629  */
630 static int do_close(CBM_FILE fd, OPTIONS * const options)
631 {
632  int rv;
633  unsigned char unit;
634  unsigned char secondary;
635 
636  rv = skip_options(options);
637 
638  rv = rv || get_argument_char(options, &unit);
639  rv = rv || get_argument_char(options, &secondary);
640 
641  if (rv || check_if_parameters_ok(options))
642  return 1;
643 
644  return cbm_close(fd, unit, secondary);
645 }
646 
647 /*
648  * read raw data from the IEC bus
649  */
650 static int do_read(CBM_FILE fd, OPTIONS * const options)
651 {
652  int size, rv = 0;
653  unsigned char buf[2048];
654  FILE *f;
655 
656  if (skip_options(options))
657  return 1;
658 
659  if (get_argument_file_for_write(options, &f))
660  return 1;
661 
662  /* fill a buffer with up to 64k of bytes from the IEC bus */
663  while(0 < (size = cbm_raw_read(fd, buf, sizeof(buf))))
664  {
665  // if PETSCII was recognized, convert the data before writing
666 
667  if (options->petsciiraw == PA_PETSCII)
668  {
669  int i;
670  for (i=0; i < size; i++)
671  buf[i] = cbm_petscii2ascii_c(buf[i]);
672  }
673 
674  /* write that to the file */
675  if(size != (int) fwrite(buf, 1, size, f))
676  {
677  rv=1; /* error condition from cbm_raw_read */
678  break; /* do fclose(f) before exiting */
679  }
680  /* if nobody complained, repeat filling the buffer */
681  }
682 
683  if(size < 0) rv=1; /* error condition from cbm_raw_read */
684 
685  fclose(f);
686  return rv;
687 }
688 
689 /*
690  * write raw data to the IEC bus
691  */
692 static int do_write(CBM_FILE fd, OPTIONS * const options)
693 {
694  char *fn = NULL;
695  int size;
696  unsigned char buf[2048];
697  FILE *f;
698 
699  if (skip_options(options))
700  return 1;
701 
702  if (get_argument_file_for_read(options, &f, &fn))
703  return 1;
704 
705  /* fill a buffer with up to 64k of bytes from file/console */
706  size = fread(buf, 1, sizeof(buf), f);
707  /* do this test only on the very first run */
708  if(size == 0 && feof(f))
709  {
710  arch_error(0, 0, "no data: %s", fn);
711  if(f != stdin) fclose(f);
712  return 1;
713  }
714 
715  /* as long as no error occurred */
716  while( ! ferror(f))
717  {
718  /* if requested, convert to PETSCII before writing */
719  if (options->petsciiraw == PA_PETSCII)
720  {
721  int i;
722  for (i=0; i < size; i++)
723  buf[i] = cbm_ascii2petscii_c(buf[i]);
724  }
725 
726  /* write that to the the IEC bus */
727  if(size != cbm_raw_write(fd, buf, size))
728  {
729  /* exit the loop with another error condition */
730  break;
731  }
732 
733  /* fill a buffer with up to 64k of bytes from file/console */
734  size = fread(buf, 1, sizeof(buf), f);
735  if(size == 0 && feof(f))
736  {
737  /* nothing more to read */
738  if(f != stdin) fclose(f);
739  return 0;
740  }
741  }
742 
743  /* the loop has exited, because of an error, check, which one */
744  if(ferror(f))
745  {
746  arch_error(0, 0, "could not read %s", fn);
747  }
748  /* else : size number of bytes could not be written to IEC bus */
749 
750  if(f != stdin) fclose(f);
751  return 1;
752 }
753 
754 /*
755  * put specified data to the IEC bus
756  */
757 static int do_put(CBM_FILE fd, OPTIONS * const options)
758 {
759  int rv;
760  char *commandline;
761  unsigned int commandlinelen = 0;
762 
763 
764  int extended = 0;
765  int c;
766  static const char short_options[] = "+e";
767  static struct option long_options[] =
768  {
769  {"extended", no_argument, NULL, 'e'},
770  {NULL, no_argument, NULL, 0 }
771  };
772 
773  // first of all, process the options given
774 
775  while ((c = process_individual_option(options, short_options, long_options)) != EOF)
776  {
777  switch (c)
778  {
779  case 'e':
780  extended = 1;
781  break;
782 
783  default:
784  return 1;
785  }
786  }
787 
788  rv = get_extended_argument_string(extended, options, &commandline, &commandlinelen);
789 
790  if (rv)
791  return 1;
792 
793  /* if requested, convert to PETSCII before writing */
794  if (options->petsciiraw == PA_PETSCII)
795  {
796  unsigned int i;
797  for (i=0; i < commandlinelen; i++)
798  commandline[i] = cbm_ascii2petscii_c(commandline[i]);
799  }
800 
801  /* write that to the IEC bus */
802  rv = cbm_raw_write(fd, commandline, commandlinelen);
803  if(rv < 0 || commandlinelen != (unsigned int) rv)
804  {
805  rv = 1;
806  }
807  else
808  {
809  rv = 0;
810  }
811 
812  if (commandline != NULL)
813  {
814  free(commandline);
815  }
816 
817  return rv;
818 }
819 
820 /*
821  * display device status w/ PetSCII conversion
822  */
823 static int do_status(CBM_FILE fd, OPTIONS * const options)
824 {
825  char buf[40];
826  unsigned char unit;
827  int rv;
828 
829  rv = skip_options(options);
830 
831  rv = rv || get_argument_char(options, &unit);
832 
833  if (rv || check_if_parameters_ok(options))
834  return 1;
835 
836  rv = cbm_device_status(fd, unit, buf, sizeof(buf));
837 
838  if (options->petsciiraw == PA_PETSCII)
839  cbm_petscii2ascii(buf);
840 
841  printf("%s", buf);
842 
843  return (rv == 99) ? 1 : 0;
844 }
845 
846 
847 /*
848  * send device command
849  */
850 static int do_command(CBM_FILE fd, OPTIONS * const options)
851 {
852  int rv;
853  unsigned char unit;
854  char *commandline;
855  unsigned int commandlinelen = 0;
856 
857  int extended = 0;
858  int c;
859  static const char short_options[] = "+e";
860  static struct option long_options[] =
861  {
862  {"extended", no_argument, NULL, 'e'},
863  {NULL, no_argument, NULL, 0 }
864  };
865 
866  // first of all, process the options given
867 
868  while ((c = process_individual_option(options, short_options, long_options)) != EOF)
869  {
870  switch (c)
871  {
872  case 'e':
873  extended = 1;
874  break;
875 
876  default:
877  return 1;
878  }
879  }
880 
881  rv = get_argument_char(options, &unit);
882  rv = rv || get_extended_argument_string(extended, options, &commandline, &commandlinelen);
883 
884  if (rv)
885  return 1;
886 
887  rv = cbm_listen(fd, unit, 15);
888  if(rv == 0)
889  {
890  if (commandlinelen > 0)
891  cbm_raw_write(fd, commandline, commandlinelen);
892 
893  // make sure the buffer is ended with a '\r'; this is needed
894  // only if the command ends with a '\r', to work around a bug
895  // in the floppy code.
896 
897  cbm_raw_write(fd, "\r", 1);
898 
899  rv = cbm_unlisten(fd);
900  }
901 
902  if (commandline != NULL)
903  {
904  free(commandline);
905  }
906 
907  return rv;
908 }
909 
910 /*
911  * display directory
912  */
913 static int do_dir(CBM_FILE fd, OPTIONS * const options)
914 {
915  char c, buf[40];
916  unsigned char command[] = { '$', '0' };
917  int rv;
918  unsigned char unit;
919 
920  rv = skip_options(options);
921 
922  rv = rv || get_argument_char(options, &unit);
923  /* default is drive '0' */
924  if (options->argc > 0)
925  {
926  rv = rv || get_argument_char(options, command+1);
927  }
928 
929  if (rv || check_if_parameters_ok(options))
930  return 1;
931 
932  rv = cbm_open(fd, unit, 0, command, sizeof(command));
933  if(rv == 0)
934  {
935  if(cbm_device_status(fd, unit, buf, sizeof(buf)) == 0)
936  {
937  cbm_talk(fd, unit, 0);
938  if(cbm_raw_read(fd, buf, 2) == 2)
939  {
940  while(cbm_raw_read(fd, buf, 2) == 2)
941  {
942  if(cbm_raw_read(fd, buf, 2) == 2)
943  {
944  printf("%u ", (unsigned char)buf[0] | (unsigned char)buf[1] << 8 );
945  while((cbm_raw_read(fd, &c, 1) == 1) && c)
946  {
947  if (options->petsciiraw == PA_PETSCII)
948  putchar(cbm_petscii2ascii_c(c));
949  else
950  putchar(c);
951  }
952  putchar('\n');
953  }
954  }
955  cbm_untalk(fd);
956  cbm_device_status(fd, unit, buf, sizeof(buf));
957  printf("%s", cbm_petscii2ascii(buf));
958  }
959  else
960  {
961  cbm_untalk(fd);
962  }
963 
964  }
965  else
966  {
967  printf("%s", cbm_petscii2ascii(buf));
968  }
969  cbm_close(fd, unit, 0);
970  }
971  return rv;
972 }
973 
974 static void show_monkey(unsigned int c)
975 {
976  // const static char monkey[]={"¸,ø¤*º°´`°º*¤ø,¸"}; // for fast moves
977  // const static char monkey[]={"\\|/-"}; // from cbmcopy
978  // const static char monkey[]={"-\\|/"}; // from libtrans (reversed)
979  // const static char monkey[]={"\\-/|"}; // from cbmcopy (reversed)
980  // const static char monkey[]={"-/|\\"}; // from libtrans
981  // const static char monkey[]={",;:!^*Oo"};// for fast moves
982  const static char monkey[]={",oO*^!:;"};// for fast moves
983 
984  c %= sizeof(monkey) - 1;
985  fprintf(stderr, (c != 0) ? "\b%c" : "\b.%c" , monkey[c]);
986  fflush(stderr);
987 }
988 
989 /*
990  * read device memory, dump to stdout or a file
991  */
992 static int do_download(CBM_FILE fd, OPTIONS * const options)
993 {
994  unsigned char unit;
995  unsigned short c;
996  int addr, count, rv = 0;
997  char *tail, buf[256];
998  FILE *f;
999 
1000  char *tmpstring;
1001 
1002  if (skip_options(options))
1003  return 1;
1004 
1005  // process the drive number (unit)
1006 
1007  if (get_argument_char(options, &unit))
1008  return 1;
1009 
1010 
1011  // process the address
1012 
1013  if (get_argument_string(options, &tmpstring, NULL))
1014  return 1;
1015 
1016  addr = strtol(tmpstring, &tail, 0);
1017  if(addr < 0 || addr > 0xffff || *tail)
1018  {
1019  arch_error(0, 0, "invalid address: %s", tmpstring);
1020  return 1;
1021  }
1022 
1023 
1024  // process the count of bytes
1025 
1026  if (get_argument_string(options, &tmpstring, NULL))
1027  return 1;
1028 
1029  count = strtol(tmpstring, &tail, 0);
1030  if((count + addr) > 0x10000 || *tail)
1031  {
1032  arch_error(0, arch_get_errno(), "invalid byte count %s", tmpstring);
1033  return 1;
1034  }
1035 
1036 
1037  // process the filename, if any
1038 
1039  if (get_argument_file_for_write(options, &f))
1040  return 1;
1041 
1042 
1043  // download in chunks of sizeof(buf) (currently: 256) bytes
1044  while(count > 0)
1045  {
1046  show_monkey(count / sizeof(buf));
1047 
1048  c = (count > sizeof(buf)) ? sizeof(buf) : count;
1049 
1050  if (c + (addr & 0xFF) > 0x100) {
1051  c = 0x100 - (addr & 0xFF);
1052  }
1053 
1054  if(c != cbm_download(fd, unit, addr, buf, c))
1055  {
1056  rv = 1;
1057  fprintf(stderr, "A transfer error occurred!\n");
1058  break;
1059  }
1060 
1061  // If the user wants to convert them from PETSCII, do this
1062  // (I find it hard to believe someone would want to do this,
1063  // but who knows?)
1064 
1065  if (options->petsciiraw == PA_PETSCII)
1066  {
1067  int i;
1068  for (i = 0; i < c; i++)
1069  buf[i] = cbm_petscii2ascii_c(buf[i]);
1070  }
1071 
1072  fwrite(buf, 1, c, f);
1073 
1074  addr += c;
1075  count -= c;
1076  }
1077 
1078  fclose(f);
1079  return rv;
1080 }
1081 
1082 /*
1083  * load binary data from file into device memory
1084  */
1085 static int do_upload(CBM_FILE fd, OPTIONS * const options)
1086 {
1087  unsigned char unit;
1088  int addr;
1089  int rv;
1090  size_t size;
1091  char *tail, *fn;
1092  char *tmpstring;
1093  unsigned char addr_buf[2];
1094  unsigned int buflen = 65537;
1095  unsigned char *buf;
1096  FILE *f;
1097 
1098  if (skip_options(options))
1099  return 1;
1100 
1101  // process the drive number (unit)
1102 
1103  if (get_argument_char(options, &unit))
1104  return 1;
1105 
1106 
1107  // process the address
1108 
1109  if (get_argument_string(options, &tmpstring, NULL))
1110  return 1;
1111 
1112  addr = strtol(tmpstring, &tail, 0);
1113  if(addr < -1 || addr > 0xffff || *tail)
1114  {
1115  arch_error(0, 0, "invalid address: %s", tmpstring);
1116  return 1;
1117  }
1118 
1119  if (get_argument_file_for_read(options, &f, &fn))
1120  return 1;
1121 
1122 
1123  // allocate memory for the transfer
1124 
1125  buf = malloc(buflen);
1126  if (!buf)
1127  {
1128  fprintf(stderr, "Not enough memory for buffer.\n");
1129  return 1;
1130  }
1131 
1132  if(addr == -1)
1133  {
1134  /* read address from file */
1135  if(fread(addr_buf, 2, 1, f) != 1)
1136  {
1137  arch_error(0, arch_get_errno(), "could not read %s", fn);
1138  if(f != stdin) fclose(f);
1139  free(buf);
1140  return 1;
1141  }
1142 
1143  /* don't assume a particular endianess, although the cbm4linux
1144  * package is only i386 for now */
1145  addr = addr_buf[0] | (addr_buf[1] << 8);
1146  }
1147 
1148  size = fread(buf, 1, buflen, f);
1149  if(ferror(f))
1150  {
1151  arch_error(0, 0, "could not read %s", fn);
1152  if(f != stdin) fclose(f);
1153  free(buf);
1154  return 1;
1155  }
1156  else if(size == 0 && feof(f))
1157  {
1158  arch_error(0, 0, "no data: %s", fn);
1159  if(f != stdin) fclose(f);
1160  free(buf);
1161  return 1;
1162  }
1163 
1164  if(addr + size > 0x10000)
1165  {
1166  arch_error(0, 0, "program too big: %s", fn);
1167  if (f != stdin) fclose(f);
1168  free(buf);
1169  return 1;
1170  }
1171 
1172  if(f != stdin) fclose(f);
1173 
1174  // If the user wants to convert them from PETSCII, do this
1175  // (I find it hard to believe someone would want to do this,
1176  // but who knows?)
1177 
1178  if (options->petsciiraw == PA_PETSCII)
1179  {
1180  unsigned int i;
1181  for (i = 0; i < size; i++)
1182  buf[i] = cbm_ascii2petscii_c(buf[i]);
1183  }
1184 
1185  rv = (cbm_upload(fd, unit, addr, buf, size) == (int)size) ? 0 : 1;
1186 
1187  if ( rv != 0 ) {
1188  fprintf(stderr, "A transfer error occurred!\n");
1189  }
1190 
1191  free(buf);
1192 
1193  return rv;
1194 }
1195 
1196 /*
1197  * identify connected devices
1198  */
1199 static int do_detect(CBM_FILE fd, OPTIONS * const options)
1200 {
1201  unsigned int num_devices;
1202  unsigned char device;
1203  const char *type_str;
1204 
1205  if (skip_options(options))
1206  return 1;
1207 
1208  if (check_if_parameters_ok(options))
1209  return 1;
1210 
1211  num_devices = 0;
1212 
1213  for( device = 8; device < 16; device++ )
1214  {
1215  enum cbm_device_type_e device_type;
1216  if( cbm_identify( fd, device, &device_type, &type_str ) == 0 )
1217  {
1218  enum cbm_cable_type_e cable_type;
1219  const char *cable_str = "(cannot determine cable type)";
1220 
1221  num_devices++;
1222 
1223  if ( cbm_identify_xp1541( fd, device, &device_type, &cable_type ) == 0 )
1224  {
1225  switch (cable_type)
1226  {
1227  case cbm_ct_none:
1228  cable_str = "";
1229  break;
1230 
1231  case cbm_ct_xp1541:
1232  cable_str = "(XP1541)";
1233  break;
1234 
1235  case cbm_ct_unknown:
1236  default:
1237  break;
1238  }
1239  }
1240  printf( "%2d: %s %s\n", device, type_str, cable_str );
1241  }
1242  }
1243  arch_set_errno(0);
1244  return num_devices > 0 ? 0 : 1;
1245 }
1246 
1247 /*
1248  * wait until user changes the disk
1249  */
1250 static int do_change(CBM_FILE fd, OPTIONS * const options)
1251 {
1252  unsigned char unit;
1253  int rv;
1254 
1255  rv = skip_options(options);
1256 
1257  rv = rv || get_argument_char(options, &unit);
1258 
1259  if (rv || check_if_parameters_ok(options))
1260  return 1;
1261 
1262  do
1263  {
1264  /*
1265  * Determine if we have a supported drive type.
1266  * Note: As we do not recognize all drives reliably,
1267  * we only block a 1581. If we cannot determine
1268  * the drive type, just allow using it!
1269  * \todo: Fix this!
1270  */
1271 
1272  enum cbm_device_type_e device_type;
1273 
1274  if (cbm_identify(fd, unit, &device_type, NULL) == 0)
1275  {
1276  if (device_type == cbm_dt_cbm1581)
1277  {
1278  fprintf(stderr, "Drive %u is a 1581, which is not supported (yet).\n", unit);
1279  rv = 1;
1280  break;
1281  }
1282  }
1283 
1284  /*
1285  * Make sure the drive is on track 18
1286  */
1287  if (cbm_exec_command(fd, unit, "I0:", 0) != 0)
1288  {
1289  /*
1290  * The drive did not react; most probably, there is none,
1291  * thus quit.
1292  */
1293  rv = 1;
1294  break;
1295  }
1296 
1297  if (cbm_upload(fd, unit, 0x500, prog_tdchange, sizeof(prog_tdchange)) != sizeof(prog_tdchange))
1298  {
1299  /*
1300  * The drive code wasn't uploaded fully, thus quit.
1301  */
1302  rv = 1;
1303  break;
1304  }
1305 
1306  cbm_exec_command(fd, unit, "U3:", 0);
1308 
1309  /*
1310  * Now, wait for the drive routine to signal its starting
1311  */
1312  cbm_iec_wait(fd, IEC_DATA, 1);
1313 
1314  /*
1315  * Now, wait until CLOCK is high, too, which tells us that
1316  * a new disk has been successfully read.
1317  */
1318  cbm_iec_wait(fd, IEC_CLOCK, 1);
1319 
1320  /*
1321  * Signal: We recognized this
1322  */
1323  cbm_iec_set(fd, IEC_ATN);
1324 
1325  /*
1326  * Wait for routine ending.
1327  */
1328  cbm_iec_wait(fd, IEC_CLOCK, 0);
1329 
1330  /*
1331  * Release ATN again
1332  */
1333  cbm_iec_release(fd, IEC_ATN);
1334 
1335  } while (0);
1336 
1337  return rv;
1338 }
1339 
1340 struct prog
1341 {
1342  int need_driver;
1343  char *name;
1344  PETSCII_RAW petsciiraw;
1345  mainfunc prog;
1346  char *arglist;
1347  char *shorthelp_text;
1348  char *help_text;
1349 };
1350 
1351 static struct prog prog_table[] =
1352 {
1353  {1, "lock" , PA_UNSPEC, do_lock , "",
1354  "Lock the parallel port for the use by cbm4win/cbm4linux.",
1355  "This command locks the parallel port for the use by cbm4win/cbm4linux,\n"
1356  "so that sequences of e.g. talk/read/untalk commands are not broken by\n"
1357  "concurrent processes wanting to access the parallel port." },
1358 
1359  {1, "unlock" , PA_UNSPEC, do_unlock , "",
1360  "Unlock the parallel port for the use by cbm4win/cbm4linux.",
1361  "This command unlocks the parallel port again so that other processes\n"
1362  "get a chance to access the parallel port." },
1363 
1364  {1, "listen" , PA_UNSPEC, do_listen , "<device> <secadr>",
1365  "perform a listen on the IEC bus",
1366  "Output a listen command on the IEC bus.\n"
1367  "<device> is the device number,\n"
1368  "<secadr> the secondary address to use for this.\n\n"
1369  "This has to be undone later with an unlisten command." },
1370 
1371  {1, "talk" , PA_UNSPEC, do_talk , "<device> <secadr>",
1372  "perform a talk on the IEC bus",
1373  "Output a talk command on the IEC bus.\n"
1374  "<device> is the device number,\n"
1375  "<secadr> the secondary address to use for this.\n\n"
1376  "This has to be undone later with an untalk command." },
1377 
1378  {1, "unlisten", PA_UNSPEC, do_unlisten, "",
1379  "perform an unlisten on the IEC bus",
1380  "Undo one or more previous listen commands.\n"
1381  "This affects all drives." },
1382 
1383  {1, "untalk" , PA_UNSPEC, do_untalk , "",
1384  "perform an untalk on the IEC bus",
1385  "Undo one or more previous talk commands.\n"
1386  "This affects all drives." },
1387 
1388  {1, "open" , PA_RAW, do_open , "[-e|--extended] <device> <secadr> <filename> [<file1>...<fileN>]",
1389  "perform an open on the IEC bus",
1390  "Output an open command on the IEC bus.\n"
1391  "<device> is the device number,\n"
1392  "<secadr> the secondary address to use for this.\n"
1393  "<filename> is the name of the file to be opened.\n"
1394  " It must be given, but may be empty by specifying it as \"\".\n"
1395  "<file1..N> are additional bytes to append to the filename <filename>.\n"
1396  " Single bytes can be given as decimal, octal (0 prefix) or\n"
1397  " sedecimal (0x prefix).\n\n"
1398  "If the option -e or --extended is given, an extended format is used:\n"
1399  " You can specify extra characters by given their ASCII value in hex,\n"
1400  " prepended with '%', that is: '%20' => SPACE, '%41' => 'A', '%35' => '5',\n"
1401  " and-so-on. A '%' is given by giving its hex ASCII: '%25' => '%'.\n\n"
1402  "Opening a file has to be undone later with a close command.\n\n"
1403  "NOTES:\n"
1404  "- You cannot do an open without a filename.\n"
1405  " Although a CBM machine (i.e., a C64) allows this, it is an internal operation\n"
1406  " to the computer only.\n"
1407  "- If used with the global --petscii option, this action is equivalent\n"
1408  " to the deprecated command-line 'cbmctrl popen'.\n"
1409  "- If using both --petscii and --extended option, the bytes given via the\n"
1410  " '%' meta-character or as <file1> .. <fileN> are *not* converted to petscii." },
1411 
1412  {1, "popen" , PA_PETSCII, do_open , "<device> <secadr> <filename>",
1413  "deprecated; use 'cbmctrl --petscii open' instead!",
1414  "This command is deprecated; use 'cbmctrl -p open' instead!" },
1415 
1416  {1, "close" , PA_UNSPEC, do_close , "<device> <secadr>",
1417  "perform a close on the IEC bus",
1418  "Undo a previous open command." },
1419 
1420  {1, "read" , PA_RAW, do_read , "[<file>]",
1421  "read raw data from the IEC bus",
1422  "With this command, you can read raw data from the IEC bus.\n"
1423  "<file> (optional) file name of a file to write the contents to.\n"
1424  " If this name is not given or it is a dash ('-'), the\n"
1425  " contents will be written to stdout, normally the console." },
1426 
1427  {1, "write" , PA_RAW, do_write , "[<file>]",
1428  "write raw data to the IEC bus",
1429  "With this command, you can write raw data to the IEC bus.\n"
1430  "<file> (optional) file name of a file to read the values from.\n"
1431  " If this name is not given or it is a dash ('-'), the\n"
1432  " contents will be read from stdin, normally the console." },
1433 
1434  {1, "put" , PA_RAW, do_put , "[-e|--extended] <datastr> [<data1> ... <dataN>]",
1435  "put specified data to the IEC bus",
1436  "With this command, you can write raw data to the IEC bus.\n"
1437  "<datastr> is the string to output to the IEC bus.\n"
1438  " It must be given, but may be empty by specifying it as \"\".\n\n"
1439  "<data1..N> are additional bytes to append to the string <datastr>.\n"
1440  " Single bytes can be given as decimal, octal (0 prefix) or\n"
1441  " sedecimal (0x prefix).\n\n"
1442  "If the option -e or --extended is given, an extended format is used:\n"
1443  " You can specify extra characters by given their ASCII value in hex,\n"
1444  " prepended with '%', that is: '%20' => SPACE, '%41' => 'A', '%35' => '5',\n"
1445  " and-so-on. A '%' is given by giving its hex ASCII: '%25' => '%'.\n\n"
1446  "NOTES:\n"
1447  "- If using both --pestscii and --extended option, the bytes given via the\n"
1448  " '%' meta-character or as <data1> .. <dataN> are *not* converted to petscii." },
1449 
1450  {1, "status" , PA_PETSCII, do_status , "<device>",
1451  "give the status of the specified drive",
1452  "This command gets the status (the so-called 'error channel')"
1453  "of the given drive and outputs it on the screen.\n"
1454  "<device> is the device number of the drive." },
1455 
1456  {1, "command" , PA_RAW, do_command , "[-e|--extended] <device> <cmdstr> [<cmd1> ... <cmdN>]",
1457  "issue a command to the specified drive",
1458  "This command issues a command to a specific drive.\n"
1459  "This command is a command that you normally give to\n"
1460  "channel 15 (i.e., N: to format a drive, V: to validate, etc.).\n\n"
1461  "<device> is the device number of the drive.\n\n"
1462  "<cmdstr> is the command to execute in the drive.\n"
1463  " It must be given, but may be empty by specifying it as \"\".\n\n"
1464  "<cmd1..N> are additional bytes to append to the command string <cmdstr>.\n"
1465  " Single bytes can be given as decimal, octal (0 prefix) or\n"
1466  " sedecimal (0x prefix).\n\n"
1467  "If the option -e or --extended is given, an extended format is used:\n"
1468  " You can specify extra characters by given their ASCII value in hex,\n"
1469  " prepended with '%', that is: '%20' => SPACE, '%41' => 'A', '%35' => '5',\n"
1470  " and-so-on. A '%' is given by giving its hex ASCII: '%25' => '%'.\n\n"
1471  "Example: Write the bytes $29, $49 into memory locations $0077 and $0078\n"
1472  " of drive 8 (which gives the drive the address 9):\n"
1473  " cbmctrl -p command -e 8 m-w 119 0 2 41 73\n"
1474  " or cbmctrl -p command -e 8 m-w%77%00 2 0x29 0x49\n\n"
1475  "NOTES:\n"
1476  "- If --raw is used (default), you have to give the commands in uppercase\n"
1477  " letters, lowercase will NOT work!\n"
1478  " If --petscii is used, you must give the commands in lowercase.\n"
1479  "- If used with the global --petscii option, this action is equivalent\n"
1480  " to the deprecated command-line 'cbmctrl pcommand'.\n"
1481  "- If using both --petscii and --extended option, the bytes given via the\n"
1482  " '%' meta-character or as <cmd1> .. <cmdN> are *not* converted to petscii." },
1483 
1484  {1, "pcommand", PA_PETSCII, do_command , "[-e|--extended] <device> <cmdstr>",
1485  "deprecated; use 'cbmctrl --petscii command' instead!",
1486  "This command is deprecated; use 'cbmctrl -p command' instead!\n\n"
1487  "NOTE: You have to give the commands in lower-case letters.\n"
1488  " Upper case will NOT work!\n" },
1489 
1490  {1, "dir" , PA_PETSCII, do_dir , "<device> [<drive>]",
1491  "output the directory of the disk in the specified drive",
1492  "This command gets the directory of a disk in the drive.\n\n"
1493  "<device> is the device number of the drive (bus ID).\n"
1494  "<drive> is the drive number of a dual drive (LUN), default is 0." },
1495 
1496  {1, "download", PA_RAW, do_download, "<device> <adr> <count> [<file>]",
1497  "download memory contents from the floppy drive",
1498  "With this command, you can get data from the floppy drive memory.\n"
1499  "<device> is the device number of the drive.\n"
1500  "<adr> is the starting address of the memory region to get.\n"
1501  " it can be given in decimal or in hex (with a 0x prefix).\n"
1502  "<count> is the number of bytes to read.\n"
1503  " it can be given in decimal or in hex (with a 0x prefix).\n"
1504  "<file> (optional) file name of a file to write the contents to.\n"
1505  " If this name is not given or it is a dash ('-'), the\n"
1506  " contents will be written to stdout, normally the console.\n\n"
1507  "Example:\n"
1508  " cbmctrl download 8 0xc000 0x4000 1541ROM.BIN\n"
1509  " * reads the 1541 ROM (from $C000 to $FFFF) from drive 8 into 1541ROM.BIN" },
1510 
1511  {1, "upload" , PA_RAW, do_upload , "<device> <adr> [<file>]",
1512  "upload memory contents to the floppy drive",
1513  "With this command, you can write data to the floppy drive memory.\n"
1514  "<device> is the device number of the drive.\n"
1515  "<adr> is the starting address of the memory region to write to.\n"
1516  " it can be given in decimal or in hex (with a 0x prefix).\n"
1517  " If this is -1, the first two bytes from the file are\n"
1518  " considered as start address.\n"
1519  "<file> (optional) file name of a file to read the values from.\n"
1520  " If this name is not given or it is a dash ('-'), the\n"
1521  " contents will be read from stdin, normally the console."
1522  "Example:\n"
1523  " cbmctrl upload 8 0x500 BUFFER2.BIN\n"
1524  " * writes the file BUFFER2.BIN to drive 8, address $500." },
1525 
1526  {1, "clk" , PA_UNSPEC, do_iec_clk , "",
1527  "Set the clk line on the IEC bus.",
1528  "This command unconditionally sets the CLK line on the IEC bus." },
1529 
1530  {1, "uclk" , PA_UNSPEC, do_iec_uclk , "",
1531  "Unset the clk line on the IEC bus.",
1532  "This command unconditionally unsets the CLK line on the IEC bus." },
1533 
1534  {1, "reset" , PA_UNSPEC, do_reset , "",
1535  "reset all drives on the IEC bus",
1536  "This command performs a (physical) reset of all drives on the IEC bus." },
1537 
1538  {1, "detect" , PA_UNSPEC, do_detect , "",
1539  "detect all drives on the IEC bus",
1540  "This command tries to detect all drives on the IEC bus.\n"
1541  "For this, this command accesses all possible drives and tries to read\n"
1542  "some bytes from their memory. If a drive is detected, its name is output.\n"
1543  "Additionally, this routine determines if the drive is connected via a\n"
1544  "parallel cable (XP1541 companion cable)." },
1545 
1546  {1, "change" , PA_UNSPEC, do_change , "<device>",
1547  "wait for a disk to be changed in the specified drive",
1548  "This command waits for a disk to be changed in the specified drive.\n\n"
1549  "For this, it makes the following assumptions:\n\n"
1550  "* there is already a disk in the drive.\n"
1551  "* that disk will be removed and replaced by another disk.\n"
1552  "* we do not want to return from this command until the disk is completely\n"
1553  " inserted and ready to be read/written.\n\n"
1554  "Because of this, just opening the drive and closing it again (without\n"
1555  "actually removing the disk) will not work in most cases." },
1556 
1557  {0, NULL, PA_UNSPEC, NULL, NULL, NULL}
1558 };
1559 
1560 static struct prog *
1561 process_cmdline_find_command(OPTIONS *options)
1562 {
1563  if (options->argc >= 1)
1564  {
1565  const char * const name = options->argv[0];
1566  int i;
1567 
1568  for (i=0; prog_table[i].name; i++)
1569  {
1570  if (strcmp(name, prog_table[i].name) == 0)
1571  {
1572  // advance to the next command-line argument
1573  options->argc -= 1;
1574  options->argv += 1;
1575 
1576  // return: We found the command
1577  return &prog_table[i];
1578  }
1579  }
1580  }
1581 
1582  return NULL;
1583 }
1584 
1585 /*
1586  * Output a help screen
1587  */
1588 static int do_help(CBM_FILE fd, OPTIONS * const options)
1589 {
1590  int i;
1591 
1592  do_version(fd, options);
1593 
1594  printf("\n");
1595 
1596  if (options->argc == 0)
1597  {
1598  printf("control serial CBM devices\n"
1599  "\n"
1600  " Synopsis: cbmctrl [global_options] [action] [action_opt] [--] [action_args]\n"
1601  "\n"
1602  " Global options:\n"
1603  "\n"
1604  " -h, --help: Output this help screen if specified without an action.\n"
1605  " Outputs some help information about a specific action\n"
1606  " if an action is specified.\n"
1607  " -V, --version: Output version information\n"
1608  " -@, --adapter: Tell OpenCBM which backend plugin and bus to use. This option\n"
1609  " requires an argument of the form <plugin>[:<bus>].\n"
1610  " <plugin> is the backend plugin's name to use (e.g.: xa1541)\n"
1611  " <bus> is a bus unit identifier, if supported by the backend;\n"
1612  " look up the backend's documentation for the supported\n"
1613  " bus unit identifier(s) and the format for <bus>\n"
1614  " -p, --petscii: Convert input or output parameter from CBM format (PETSCII)\n"
1615  " into PC format (ASCII). Default with all actions but 'open'\n"
1616  " and 'command'\n"
1617  " -r, --raw: Do not convert data between CBM and PC format.\n"
1618  " Default with 'open' and 'command'.\n"
1619  " -- Delimiter between action_opt and action_args; if any of the\n"
1620  " arguments in action_args starts with a '-', make sure to set\n"
1621  " the '--' so the argument is not treated as an option,\n"
1622  " accidentially.\n"
1623  "\n"
1624  " action: one of:\n"
1625  "\n");
1626 
1627  for(i=0; prog_table[i].prog; i++)
1628  {
1629  printf(" %-9s %s\n", prog_table[i].name, prog_table[i].shorthelp_text);
1630  }
1631 
1632  printf("\nFor more information on a specific action, try --help <action>.\n");
1633 
1634  }
1635  else
1636  {
1637  struct prog *pprog;
1638 
1639  pprog = process_cmdline_find_command(options);
1640 
1641  if (pprog)
1642  {
1643  printf(" cbmctrl %s %s\n\n %s\n\n%s\n",
1644  pprog->name, pprog->arglist, pprog->shorthelp_text, pprog->help_text);
1645  }
1646  else
1647  {
1648  printf(" Nothing known about \"cbmctrl %s\".\n", options->argv[0]);
1649  }
1650  }
1651 
1652  return 0;
1653 }
1654 
1655 static int
1656 set_option(int *Where, int NewValue, int OldValue, const char * const Description)
1657 {
1658  int error = 0;
1659 
1660  if (*Where != OldValue)
1661  {
1662  error = 1;
1663  fprintf(stderr, "Specified option '%s', but there is a conflicting option,\n"
1664  "or it is specified twice!\n", Description);
1665  }
1666 
1667  *Where = NewValue;
1668 
1669  return error;
1670 }
1671 
1672 static int
1673 set_option_petsciiraw(PETSCII_RAW *Where, PETSCII_RAW NewValue, int IgnoreError)
1674 {
1675  int error = 0;
1676 
1677  if (!IgnoreError && (*Where != PA_UNSPEC))
1678  {
1679  error = 1;
1680  fprintf(stderr, "Specified option '--petscii' and '--raw' in one command!\n");
1681  }
1682 
1683  *Where = NewValue;
1684 
1685  return error;
1686 }
1687 
1688 static int
1689 process_cmdline_common_options(int argc, char **argv, OPTIONS *options)
1690 {
1691  unsigned int use_rc = 1;
1692  int option_index;
1693  int option;
1694 
1695  static const char short_options[] = "+fhVpr@:";
1696  static struct option long_options[] =
1697  {
1698  { "adapter" , required_argument, NULL, '@' },
1699  { "forget" , no_argument, NULL, 'f' },
1700  { "help" , no_argument, NULL, 'h' },
1701  { "version" , no_argument, NULL, 'V' },
1702  { "petscii" , no_argument, NULL, 'p' },
1703  { "raw" , no_argument, NULL, 'r' },
1704  { NULL , no_argument, NULL, 0 }
1705  };
1706 
1707  // clear all options
1708  memset(options, 0, sizeof(*options));
1709  options->adapter = 0;
1710 
1711  // remember argc and argv, so they can be used later
1712  options->argc = argc;
1713  options->argv = argv;
1714 
1715  // first of all, parse the command-line (before the command):
1716  // is -f specified (thus, we should ignore .opencbmrc)?
1717  //
1718  optind = 0;
1719 
1720  while ((option = getopt_long(options->argc, options->argv, short_options, long_options, &option_index)) != EOF)
1721  {
1722  if (option == 'f')
1723  use_rc = 0;
1724  }
1725 
1726  // if we should read .opencbmrc, do it now:
1727  if (use_rc)
1728  ; // @TODO: TBD
1729 
1730  // now, we start the "real" scanning
1731  optind = 0;
1732 
1733  while ((option = getopt_long(options->argc, options->argv, short_options, long_options, &option_index)) != EOF)
1734  {
1735  switch (option)
1736  {
1737  case '?':
1738  fprintf(stderr, "unknown option %s specified!\n",
1739  options->argv[optind-1]);
1740  options->error = 1;
1741  break;
1742 
1743  case 'f':
1744  // this option has already been processed,
1745  // ignore it
1746  break;
1747 
1748  case 'h':
1749  set_option(&options->help, 1, 0, "--help");
1750  break;
1751 
1752  case '@':
1753  if (options->adapter != NULL)
1754  options->error = 1;
1755  else
1756  options->adapter = cbmlibmisc_strdup(optarg);
1757  break;
1758 
1759  case 'V':
1760  set_option(&options->version, 1, 0, "--version");
1761  break;
1762 
1763  case 'p':
1764  options->error |= set_option_petsciiraw(&options->petsciiraw, PA_PETSCII, 0);
1765  break;
1766 
1767  case 'r':
1768  options->error |= set_option_petsciiraw(&options->petsciiraw, PA_RAW, 0);
1769  break;
1770  };
1771  }
1772 
1773  // skip the options that are already processed
1774  options->argc -= optind;
1775  options->argv += optind;
1776 
1777  return options->error;
1778 }
1779 
1780 static void
1781 free_options(OPTIONS * const options)
1782 {
1783  cbmlibmisc_strfree(options->adapter);
1784 }
1785 
1786 static int
1787 process_version_help(OPTIONS * const options)
1788 {
1789  int processed = 0;
1790 
1791  if (options->help)
1792  {
1793  do_help(0, options);
1794  processed = 1;
1795  }
1796 
1797  if (options->version)
1798  {
1799  do_version(0, NULL);
1800  processed = 1;
1801  }
1802 
1803  if (processed && options->argc > 0)
1804  fprintf(stderr, "Extra parameters on command-line are ignored.\n");
1805 
1806  return processed;
1807 }
1808 
1809 int ARCH_MAINDECL main(int argc, char *argv[])
1810 {
1811  struct prog *pprog;
1812 
1813  OPTIONS options;
1814 
1815  int rv = 0;
1816 
1817  do {
1818  CBM_FILE fd;
1819 
1820  // first of all, process the options which affect all commands
1821 
1822  rv = process_cmdline_common_options(argc, argv, &options);
1823 
1824  // check if we have --version or --help specified:
1825 
1826  if (process_version_help(&options))
1827  break;
1828 
1829  pprog = process_cmdline_find_command(&options);
1830 
1831  if (pprog == NULL)
1832  {
1833  printf("invalid command. For info on possible commands, try the --help parameter.\n\n");
1834  rv = 2;
1835  break;
1836  }
1837 
1838  // if neither PETSCII or RAW was specified, use default for that command
1839  if (options.petsciiraw == PA_UNSPEC)
1840  options.petsciiraw = pprog->petsciiraw;
1841 
1842  if (pprog->need_driver)
1843  rv = cbm_driver_open_ex(&fd, options.adapter);
1844 
1845  if (rv != 0)
1846  {
1847  if (arch_get_errno())
1848  {
1849  arch_error(0, arch_get_errno(), "%s", cbm_get_driver_name_ex(options.adapter));
1850  }
1851  else
1852  {
1853  fprintf(stderr, "An error occurred opening OpenCBM, aborting...\n");
1854  }
1855  break;
1856  }
1857 
1858  rv = pprog->prog(fd, &options) != 0;
1859  if (rv && arch_get_errno())
1860  {
1861  arch_error(0, arch_get_errno(), "%s", pprog->name);
1862  }
1863 
1864  if (pprog->need_driver)
1865  cbm_driver_close(fd);
1866 
1867  } while (0);
1868 
1869  free_options(&options);
1870 
1871  return rv;
1872 }
int CBMAPIDECL cbm_identify_xp1541(CBM_FILE HandleDevice, unsigned char DeviceAddress, enum cbm_device_type_e *CbmDeviceType, enum cbm_cable_type_e *CableType)
Identify the cable connected to a specific floppy drive.
Definition: detectxp1541.c:187
int version
option: print version information
Definition: cbmctrl.c:38
int CBMAPIDECL cbm_talk(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress)
Send a TALK on the IEC serial bus.
Definition: cbm.c:976
char * cbmlibmisc_strdup(const char *const OldString)
Duplicate a given string.
Definition: libstring.c:84
int CBMAPIDECL cbm_listen(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress)
Send a LISTEN on the IEC serial bus.
Definition: cbm.c:945
char ** argv
a (modifieable) copy of the argument list, as given to main()
Definition: cbmctrl.c:34
void CBMAPIDECL cbm_unlock(CBM_FILE HandleDevice)
Unlock the parallel port for the driver.
Definition: cbm.c:832
void cbmlibmisc_strfree(const char *String)
Free a string.
Definition: libstring.c:172
int error
there was an error in processing the options (=1), or not (=0)
Definition: cbmctrl.c:36
int CBMAPIDECL cbm_device_status(CBM_FILE HandleDevice, unsigned char DeviceAddress, void *Buffer, size_t BufferLength)
Read the drive status from a floppy.
Definition: cbm.c:1525
int help
option: the user requested help for the specified command
Definition: cbmctrl.c:37
int ownargv
remember: This is the original argv (=0), or this is a malloc()ed copy of it (=1) ...
Definition: cbmctrl.c:35
int CBMAPIDECL cbm_close(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress)
Close a file on the IEC serial bus.
Definition: cbm.c:1072
struct to remember the general options to the program
Definition: cbmctrl.c:32
const char *CBMAPIDECL cbm_get_driver_name_ex(char *Adapter)
Get the name of the driver for a specific parallel port, extended version.
Definition: cbm.c:583
int ARCH_MAINDECL main(int argc, char **argv)
Initialize the xum1541 device This function tries to find and identify the xum1541 device...
Definition: usbcfg.c:38
int CBMAPIDECL cbm_driver_open_ex(CBM_FILE *HandleDevice, char *Adapter)
Opens the driver, extended version.
Definition: cbm.c:683
void CBMAPIDECL cbm_lock(CBM_FILE HandleDevice)
Lock the parallel port for the driver.
Definition: cbm.c:804
#define IEC_DATA
Definition: opencbm.h:97
void CBMAPIDECL cbm_iec_set(CBM_FILE HandleDevice, int Line)
Activate a line on the IEC serial bus.
Definition: cbm.c:1341
char *CBMAPIDECL cbm_petscii2ascii(char *Str)
Convert an null-termined PETSCII string to ASCII.
Definition: petscii.c:136
int CBMAPIDECL cbm_raw_write(CBM_FILE HandleDevice, const void *Buffer, size_t Count)
Write data to the IEC serial bus.
Definition: cbm.c:870
Definition: getopt.h:94
void CBMAPIDECL cbm_driver_close(CBM_FILE HandleDevice)
Closes the driver.
Definition: cbm.c:768
#define IEC_RESET
Definition: opencbm.h:100
#define CBM_FILE
Definition: opencbm.h:87
int CBMAPIDECL cbm_unlisten(CBM_FILE HandleDevice)
Send an UNLISTEN on the IEC serial bus.
Definition: cbm.c:1100
int arch_filesize(const char *Filename, off_t *Filesize)
Obtain the size of a given file.
Definition: file.c:38
#define IEC_ATN
Definition: opencbm.h:99
void CBMAPIDECL cbm_iec_release(CBM_FILE HandleDevice, int Line)
Deactivate a line on the IEC serial bus.
Definition: cbm.c:1372
Definition: cbmctrl.c:1340
int CBMAPIDECL cbm_open(CBM_FILE HandleDevice, unsigned char DeviceAddress, unsigned char SecondaryAddress, const void *Filename, size_t FilenameLength)
Open a file on the IEC serial bus.
Definition: cbm.c:1012
#define arch_set_errno(_x)
Definition: arch.h:93
int argc
a (modifieable) copy of the number of arguments, as given to main()
Definition: cbmctrl.c:33
int CBMAPIDECL cbm_raw_read(CBM_FILE HandleDevice, void *Buffer, size_t Count)
Read data from the IEC serial bus.
Definition: cbm.c:906
int CBMAPIDECL cbm_reset(CBM_FILE HandleDevice)
RESET all devices.
Definition: cbm.c:1209
int CBMAPIDECL cbm_iec_wait(CBM_FILE HandleDevice, int Line, int State)
Wait for a line to have a specific state.
Definition: cbm.c:1448
char *CBMAPIDECL cbm_ascii2petscii(char *Str)
Convert an null-termined ASCII string to PETSCII.
Definition: petscii.c:160
int CBMAPIDECL cbm_exec_command(CBM_FILE HandleDevice, unsigned char DeviceAddress, const void *Command, size_t Size)
Executes a command in the floppy drive.
Definition: cbm.c:1599
cbm_device_type_e
Definition: opencbm.h:114
char CBMAPIDECL cbm_petscii2ascii_c(char Character)
Convert a PETSCII character to ASCII.
Definition: petscii.c:64
int CBMAPIDECL cbm_identify(CBM_FILE HandleDevice, unsigned char DeviceAddress, enum cbm_device_type_e *CbmDeviceType, const char **CbmDeviceString)
Identify the connected floppy drive.
Definition: detect.c:66
DLL interface for accessing the driver.
char CBMAPIDECL cbm_ascii2petscii_c(char Character)
Convert an ASCII character to PETSCII.
Definition: petscii.c:105
cbm_cable_type_e
Definition: opencbm.h:132
char * adapter
option: an explicit adapter was specified
Definition: cbmctrl.c:39
int CBMAPIDECL cbm_untalk(CBM_FILE HandleDevice)
Send an UNTALK on the IEC serial bus.
Definition: cbm.c:1128
Define makros and functions which account for differences between the different architectures.
int CBMAPIDECL cbm_upload(CBM_FILE HandleDevice, unsigned char DeviceAddress, int DriveMemAddress, const void *Program, size_t Size)
Upload a program into a floppy's drive memory.
Definition: upload.c:133
#define IEC_CLOCK
Definition: opencbm.h:98
PETSCII_RAW petsciiraw
option: The user requested PETSCII or RAW, or nothing
Definition: cbmctrl.c:40
Some functions for string handling.