1 /////////////////////////////////////////////////////////////////////////
2 // $Id: sb16ctrl.c,v 1.7 2009-02-08 09:05:52 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 //  Copyright (C) 2001-2014  The Bochs Project
6 //
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Lesser General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Lesser General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Lesser General Public
18 //  License along with this library; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 
21 // This file (SB16CTRL.C) written and donated by Josef Drexler
22 
23 
24 
25 
26 // The purpose of this program is to provide runtime configuration
27 // options to the SB16 Emulator until Bochs has a more sophisticated
28 // user interface allowing to do these things better.
29 
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 
36 #ifdef dos
37 #	include <dos.h>
38 #endif
39 
40 #if defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
41 #	include <sys/io.h>
42 #	include <errno.h>
43 #	define inp inb
44 #	define outp(a,b) outb(b,a)
45 #endif
46 
47 /*
48 #define DEBUG
49 */
50 
51 #define EMULPORT       0x333
52 
53 int checked = 0;
54 int filelevel = 0;
55 int verbose = 0;
56 char ofline[256] = " in the command line";
57 
58 /******************************
59  * System dependent functions *
60  ******************************/
61 
62 /* Read a value from the emulator port */
reademul()63 int reademul()
64 {
65   return inp(EMULPORT);
66 }
67 
68 /* Write a value to the emulator port */
writeemul(int value)69 void writeemul(int value)
70 {
71   outp(EMULPORT, value);
72 }
73 
74 /* Enable access to the emulator port */
enableport()75 void enableport()
76 {
77 #if defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
78   if (ioperm(EMULPORT, 1, 1)) {
79     printf("Could not access emulator port %03x: %s.\n", EMULPORT, strerror(errno));
80     exit(1);
81   }
82 #endif
83 }
84 
85 /********************************
86  * System independent functions *
87  ********************************/
88 
89 /* Forward definitions*/
90 void command(int cmd, char *arg);
91 void checkemulator();
92 
93 /* Convert a string into a number */
strtoi(char * nptr,char ** endptr,int base)94 int strtoi(char *nptr, char **endptr, int base)
95 {
96   int value, digit;
97   int sign = 1;
98 
99   /* Skip leading white space */
100   while( isspace(*nptr) )
101     nptr++;
102 
103   /* Check if there is a sign */
104   if (*nptr == '-')
105     {
106       sign = -1;
107       nptr++;
108     }
109   else if (*nptr == '+')
110     {
111       sign = 1;
112       nptr++;
113     }
114 
115   /* Check the base if undefined and determine it */
116   if (base == 0)
117     {
118       if (*nptr == '0')
119 	{
120 	  nptr++;
121 	  if ( (*nptr == 'x') ||
122 	       (*nptr == 'X') )
123 	    {
124 	      nptr++;
125 	      base = 16;
126 	    }
127 	  else
128 	    base = 8;
129 	}
130       else
131 	base = 10;
132     }
133 
134   /* Convert the number */
135   value = 0;
136   digit = -1;
137   while ( isalnum(*nptr) )
138     {
139       if ( isdigit(*nptr) )
140 	digit = *nptr - '0';
141       else
142 	digit = tolower(*nptr) - 'a' + 10;
143 
144       if ( (digit >= base) || (digit < 0) )
145 	break;     /* Isn't a valid char, abort conversion */
146 
147       value = value * base + digit;
148       nptr++;
149     }
150 
151   if (endptr != NULL)
152     *endptr = nptr;
153 
154   return sign * value;
155 }
156 
157 /* Print the command line usage */
usage()158 void usage()
159 {
160   printf("SB16 emulator control program for Bochs.\n"
161          "\n"
162 	 "Usage: sb16ctrl [-i #] [-t #,#,#,#,#,#] [-r] [-m #,..] [-f filename]\n"
163 	 "\n"
164 	 "This program is used to control the operation of the SB16 emulator\n"
165 	 "until it has a more sophisticated interface.\n"
166 	 "Any number of commands can be given. If none are present \"-f -\"\n"
167 	 "is assumed.\n"
168 	 "\n"
169 	 "-i # show the selected emulator info string\n"
170 	 "-t #,#,#,#,#,# load the patch translation into the emulator\n"
171 	 "-r resets the patch translation table\n"
172 	 "-m #,... sends the given midi message (up to 255 bytes)\n"
173 	 "-f filename loads commands from the given file, or stdin if \"-\"\n"
174 	 "-v be verbose\n"
175 	 "\n"
176 	 "# can be decimal, octal (first digit 0) or hexadecimal (0x..)\n"
177 	 "\n"
178 	 );
179 }
180 
181 /* Execute the given command */
emulcommand(int command)182 void emulcommand(int command)
183 {
184   if (checked == 0)
185     checkemulator();
186 
187   writeemul(command);
188 }
189 
190 /* Check if we got the expected response */
testemul(int value,char * msg)191 int testemul(int value, char *msg)
192 {
193   int i;
194 
195   if (checked == 0)
196     checkemulator();
197 
198   i = reademul();
199 #ifndef DEBUG
200   if ( (i != value) && (msg) )
201     {
202       printf("Bochs emulator/SB16 error: %s\n", msg);
203       exit(1);
204     }
205 #endif
206   return i;
207 }
208 
209 /* Check if we are running inside the emulator */
checkemulator()210 void checkemulator()
211 {
212   int i;
213 
214   checked = 1;
215 
216   enableport();
217 
218   /* Check emulator status */
219   for (i=0; i<9; i++)
220     writeemul(1);          /* Clear input queue */
221   writeemul(10);           /* Check Emulator present */
222   testemul(254, "no check ACK: Emulator not present");
223   testemul(0x55, "Emulator not present");    /* should return 0x55 */
224 }
225 
226 /* Read internal emulator string and print it */
showemul(int i)227 void showemul(int i)
228 {
229   int j;
230 
231   emulcommand(5);   /* 5 means dump info */
232   writeemul(i);   /* Argument to command 5; info number */
233   testemul(254, "no info ACK");
234   printf("Emulator info string %d:", i);
235   do {
236     j = reademul();
237     if (j == 255)
238       break;
239     printf("%c", j);
240   } while (j != 10);
241 }
242 
243 /* Process a string - change "," and "/" into a space,
244    and ignore everything after comments "#" */
procstr(char * str)245 void procstr(char *str)
246 {
247   int i;
248 
249   for (i=0; str[i] != 0; i++)
250     switch(str[i])
251       {
252       case ',':
253       case '/':
254       case 10:
255       case 13:
256 	str[i] = ' ';
257 	break;
258       case '#':
259 	str[i] = 0;
260 	str[i+1] = 0;
261 	break;
262       }
263 }
264 
265 /* Upload a mapping to the emulator */
loadmap(char * map)266 void loadmap(char *map)
267 {
268   int i;
269   int trans[6];
270   char *nextnum;
271 
272   procstr(map);
273 
274   nextnum = map;
275   for (i=0;i<6;i++) {
276     if (nextnum) {
277       trans[i] = strtoi(nextnum, &nextnum, 0);
278       if ( (!nextnum) ||
279 	   ( (trans[i] > 127) && (trans[i] < 255) ) ||
280 	   (trans[i] > 255) )
281 	printf("Parse error in value %d%s. Command ignored.", i, ofline);
282     }
283     if (!nextnum)
284       trans[i] = 255;
285   }
286 
287   /* Load the remap into the emulator, command 4 */
288   emulcommand(4);   /* 4 load remap */
289   testemul(254, "no load remap ACK");
290 
291   for (i=0;i<6;i++)
292     writeemul(trans[i]);
293 
294   testemul(6, "insufficient data");	     /* test receipt of 6 arguments */
295 }
296 
297 /* Reset the translation table */
resettable()298 void resettable()
299 {
300   emulcommand(7);
301   testemul(254, "no table reset ACK");
302 }
303 
304 /* Send a series of midi bytes to the sound device */
loadmidi(char * msg)305 void loadmidi(char *msg)
306 {
307   int i;
308 
309   procstr(msg);
310 
311   while ( (msg) && (*msg) ) {
312     i = strtoi(msg, &msg, 0);
313 
314     emulcommand(11);     /* 11: Send midi data byte */
315     writeemul(i);
316     testemul(254, "no midi data ACK");
317   }
318 }
319 
320 /* read a file of commands */
loadfile(char * filename)321 void loadfile(char *filename)
322 {
323   FILE *file;
324   char cmd;
325   char *pos;
326   char msg[256];
327   int lineno = 0;
328 
329   filelevel++;
330   if (filelevel > 10)
331     {
332       printf("Error - Too many nested \"f\" commands.\n");
333       exit(1);
334     }
335 
336   if (strcmp(filename, "-") == 0)
337     file = stdin;
338   else
339     file = fopen(filename, "r");
340 
341   if (!file) {
342     printf("File %s not found.\n", filename);
343     return;
344   }
345 
346   while (!feof(file)) {
347     fgets(msg, sizeof msg, file);
348     lineno++;
349 
350     pos = msg;
351 
352     procstr(msg);
353 
354     while ( (*pos == ' ') || (*pos == 9) )
355       pos++;
356 
357     if (*pos)
358       cmd = *pos++;
359     else
360       continue;
361 
362     if (cmd == '#')   /* it's a comment */
363       continue;
364     while ( (*pos == ' ') || (*pos == 9) )
365       pos++;
366 
367     sprintf(ofline, " in line %d of file %s", lineno, filename);
368 
369     command(cmd, pos);
370   }
371   if (strcmp(filename, "-") != 0)
372     fclose(file);
373 
374   filelevel--;
375 }
376 
command(int cmd,char * arg)377 void command(int cmd, char *arg)
378 {
379   if (verbose)
380     {
381       if (arg)
382 	printf("Executing command %c %s%s\n", cmd, arg, ofline);
383       else
384 	printf("Executing command %c %s\n", cmd, ofline);
385     }
386 
387   switch (cmd)
388     {
389     case 't':
390       loadmap(arg);
391       break;
392 
393     case 'i':
394       showemul(strtoi(arg, NULL, 0));
395       break;
396 
397     case 'm':
398       loadmidi(arg);
399       break;
400 
401     case 'f':
402       loadfile(arg);
403       break;
404 
405     case 'h':
406       usage();
407       break;
408 
409     case 'r':
410       resettable();
411       break;
412 
413     default:
414       printf("Command %c %s not recognized.\n", cmd, ofline);
415       break;
416     }
417 }
418 
main(int argc,char ** argv)419 int main(int argc, char **argv)
420 {
421   int i, opt, optargnum;
422   char *optarg;
423 
424   /* No args given, read from stdin */
425   if (argc < 2)
426     {
427       loadfile("-");
428       return 0;
429     }
430 
431   /* Check command line*/
432   i = 1;
433   while (i < argc)
434     {
435       if ( (argv[i][0] != '/') &&
436 	   (argv[i][0] != '-') )
437 	{
438 	  printf("Unknown command '%s'.\n"
439 		 "sb16ctrl -h gives a list of command line options.\n",
440 		 argv[i]);
441 	  return 1;
442 	}
443 
444       optargnum = -1;
445 
446       opt = argv[i++][1];
447 
448       switch (opt) {
449 	case 'h':
450 		usage();
451 		return 0;
452 
453         case 'v':
454 	        verbose++;
455 		break;
456 
457 	case 'r':
458 		optargnum = 0;
459 
460 	case 't':
461 	case 'i':
462 	case 'm':
463 	case 'f':
464 		if (optargnum == -1)
465 		  optargnum = 1;
466 
467 		/* Fallthrough for all commands to here */
468 		if (optargnum > 0)
469 		  {
470 		    if ( (i >= argc) ||
471 			 (argv[i][0] == '-') ||
472 			 (argv[i][0] == '/') )
473 		      {
474 			printf("Option '%c' needs an argument.\n"
475 			       "sb16ctrl -h gives a list of command line options.\n",
476 			       opt);
477 			return 1;
478 		      }
479 		    optarg = argv[i++];
480 		  }
481 		else
482 		  optarg = NULL;
483 
484 		command(opt, optarg);
485 		break;
486 
487 	default:
488 	  printf("Unknown option '%c'.\n"
489 		 "sb16ctrl -h gives a list of command line options.\n",
490 		 opt);
491 	  return 1;
492       }
493     }
494 
495   return 0;
496 }
497