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