x68kConfig.c revision 15b0c026
1/* $NetBSD: x68kConfig.c,v 1.7 2020/11/16 16:46:28 tsutsui Exp $ */
2/*-------------------------------------------------------------------------
3 * Copyright (c) 1996 Yasushi Yamasaki
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *-----------------------------------------------------------------------*/
26
27#include <stdarg.h>
28#include "x68k.h"
29#include "opaque.h"
30
31static PixmapFormatRec *x68kFormat = NULL;
32static PixmapFormatRec defaultFormat = {
33	1,
34	1,
35	BITMAP_SCANLINE_PAD
36};
37static X68kScreenRec x68kScreen[X68K_FB_TYPES];
38static X68kFbProcRec x68kFbProc[X68K_FB_TYPES];
39
40/*-------------------------------------------------------------------------
41 * function "x68kGetScreenRec"
42 *
43 *  purpose:  get corresponding screen record
44 *  argument: (int)sindex       : screen index
45 *  returns:  (X68kScreenRec *) : X68k dependent screen record
46 *-----------------------------------------------------------------------*/
47X68kScreenRec *
48x68kGetScreenRec(int sindex)
49{
50    return &x68kScreen[sindex];
51}
52
53/*-------------------------------------------------------------------------
54 * function "x68kGetScreenRecByType"
55 *
56 *  purpose:  search screen record by type
57 *  argument: (int)type         : screen type
58 *  returns:  (X68kScreenRec *) : X68k dependent screen record
59 *-----------------------------------------------------------------------*/
60X68kScreenRec *
61x68kGetScreenRecByType(int type)
62{
63    int i;
64
65    for (i = 0; i < X68K_FB_TYPES; i++) {
66        if (x68kScreen[i].type == type)
67            return &x68kScreen[i];
68    }
69    return NULL;
70}
71
72/*-------------------------------------------------------------------------
73 * function "x68kGetFbProcRec"
74 *
75 *  purpose:  get corresponding frame buffer procedure record
76 *  argument: (int)sindex       : screen index
77 *  returns:  (X68kFbProcRec *) : frame buffer procedure record
78 *-----------------------------------------------------------------------*/
79X68kFbProcRec *
80x68kGetFbProcRec(int sindex)
81{
82    return &x68kFbProc[sindex];
83}
84
85/*-------------------------------------------------------------------------
86 * function "x68kRegisterPixmapFormats"
87 *
88 *  purpose:  register pixmap formats into ScreenInfo struct
89 *  argument: (ScreenInfo *)pScreenInfo
90 *  returns:  nothing
91 *-----------------------------------------------------------------------*/
92void
93x68kRegisterPixmapFormats(ScreenInfo *pScreenInfo)
94{
95    /* supports only one pixmap format for each screen type */
96    pScreenInfo->numPixmapFormats = 1;
97    pScreenInfo->formats[0] = defaultFormat;
98
99    if (x68kFormat)
100	pScreenInfo->formats[pScreenInfo->numPixmapFormats++] = *x68kFormat;
101}
102
103/*-------------------------------------------------------------------------
104 * function "x68kConfig"
105 *
106 *  purpose:  process general configuration by reading "X68kConfig" file
107 *            <X11_LIBDIR> is the default location of this file
108 *  argument: nothing
109 *  returns:  the number of screens
110 *-----------------------------------------------------------------------*/
111const char *hostConfigFilename = "/etc/X68kConfig";
112const char *siteConfigFilename = X11_LIBDIR "/X68kConfig";
113const char *configFilename = NULL;
114static FILE *config;
115static char modeSet = FALSE;
116
117static int parseCommand(void);
118static void logConfig(void);
119
120int
121x68kConfig(void)
122{
123    MessageType filefrom = X_DEFAULT;
124
125    if (configFilename) {
126	config = fopen(configFilename, "r");
127	filefrom = X_CMDLINE;
128    } else {
129	configFilename = hostConfigFilename;
130	config = fopen(configFilename, "r");
131	if (config == NULL) {
132	    configFilename = siteConfigFilename;
133	    config = fopen(configFilename, "r");
134	}
135    }
136    if (config == NULL)
137	FatalError("Can't open X68kConfig file");
138
139    LogMessage(filefrom, "Using config file: \"%s\"\n", configFilename);
140
141    while (parseCommand())
142        ;
143    fclose(config);
144    if (!modeSet)
145        FatalError("No mode set.");
146    logConfig();
147    return 1;
148}
149
150
151/*-------------------------------------------------------------------------
152 *                       X68KConfig parsing part
153 *-----------------------------------------------------------------------*/
154static void parseError(int line, const char *str, ...);
155
156enum TokenType {
157    TOKEN_EOF,
158    TOKEN_SYMBOL,
159    TOKEN_LITERAL,
160    TOKEN_OPEN_PARENTHESIS,
161    TOKEN_CLOSE_PARENTHESIS
162};
163
164typedef struct {
165    enum TokenType type;
166    int line;
167    union {
168        char *symbol;
169        int literal;
170    } content;
171} Token;
172
173/*-------------------------------------------------------------------------
174 * function "getToken"
175 *
176 *  purpose:  cut out a token from configration file stream
177 *  argument: nothing
178 *  returns:  (Token *)  : cut token
179 *-----------------------------------------------------------------------*/
180static Token *
181getToken(void)
182{
183    int c;
184    static int line = 1;
185    Token *ret;
186
187    ret = malloc(sizeof(Token));
188    if (ret == NULL)
189        FatalError("Out of memory");
190    while (TRUE) {
191        /* slip white spaces */
192        do {
193            c = fgetc(config);
194            if (c == '\n')
195                line++;
196        } while (isspace(c));
197        if (c != ';')
198            break;
199        /* skip a comment */
200        do {
201            c = fgetc(config);
202        } while (c != '\n');
203        line++;
204    }
205    ret->line = line;
206    if (c == EOF) {
207        ret->type = TOKEN_EOF;
208        return ret;
209    }
210    /* is a symbol? */
211    if (isalpha(c)) {
212        int i = 0;
213        ret->content.symbol = malloc(32 * sizeof(char));
214        if (ret->content.symbol == NULL)
215            FatalError("Out of memory");
216        do {
217            if (i < 31)
218                ret->content.symbol[i++] = c;
219            c = fgetc(config);
220        } while (isalnum(c) || c == '_');
221        ungetc(c, config);
222        ret->content.symbol[i] = '\0';
223        ret->type = TOKEN_SYMBOL;
224        return ret;
225    }
226    /* is a literal number? */
227    if (isdigit(c)) {
228        char tmp[32];
229        int i = 0;
230        do {
231            if (i < 31)
232                tmp[i++] = c;
233            c = fgetc(config);
234        } while (isdigit(c));
235        ungetc(c, config);
236        tmp[i] = '\0';
237        if (sscanf(tmp, "%d", &ret->content.literal) != 1)
238            parseError(line, "illegal literal value");
239        ret->type = TOKEN_LITERAL;
240        return ret;
241    }
242    /* others */
243    switch(c) {
244        case '(':
245            ret->type = TOKEN_OPEN_PARENTHESIS;
246            break;
247        case ')':
248            ret->type = TOKEN_CLOSE_PARENTHESIS;
249            break;
250        default:
251            parseError(line, NULL);
252    }
253    return ret;
254}
255
256typedef struct {
257    const char *symbol;
258    void (*proc)(int argc, Token **argv);
259} Command;
260
261static void parseModeDef(int argc, Token **argv);
262static void parseMouse(int argc, Token **argv);
263static void parseKeyboard(int argc, Token **argv);
264static void parseMode(int argc, Token **argv);
265
266static const Command command[] = {
267    { "ModeDef", parseModeDef },
268    { "Mouse", parseMouse },
269    { "Keyboard", parseKeyboard },
270    { "Mode", parseMode },
271};
272#define NCOMMANDS (sizeof(command)/sizeof(command[0]))
273
274static const char *x68kTypeStr[] = {
275	[X68K_FB_NULL]    = NULL,
276	[X68K_FB_TEXT]    = "Text",
277	[X68K_FB_GRAPHIC] = "Graphic",
278};
279#define NTYPES (sizeof(x68kTypeStr) / sizeof(x68kTypeStr[0]))
280
281static const char *x68kClassStr[] = {
282	[StaticGray]  = "StaticGray",
283	[GrayScale]   = "GrayScale",
284	[StaticColor] = "StaticColor",
285	[PseudoColor] = "PseudoColor",
286	[TrueColor]   = "TrueColor",
287	[DirectColor] = "DirectColor",
288};
289#define NCLASSES (sizeof(x68kClassStr) / sizeof(x68kClassStr[0]))
290#define ClassInvalid	(-1)
291
292/*-------------------------------------------------------------------------
293 * function "parseCommand"
294 *
295 *  purpose:  parse generic command. every command parsing departs here.
296 *  argument: nothing
297 *  returns:  (int) : FALSE if there's no rest
298 *                    TRUE  otherwise
299 *-----------------------------------------------------------------------*/
300static int
301parseCommand(void)
302{
303    Token **argv = 0, *token;
304    int argc = 0;
305    int i;
306
307    token = getToken();
308    if (token->type == TOKEN_EOF)
309        return FALSE;
310    if (token->type != TOKEN_OPEN_PARENTHESIS)
311        parseError(token->line, "missing parenthesis");
312    free(token);
313
314    /* get command name and arguments */
315    while (TRUE) {
316        token = getToken();
317        if (token->type == TOKEN_EOF)
318            parseError(token->line, "reached EOF");
319        if (token->type == TOKEN_CLOSE_PARENTHESIS) {
320            free(token);
321            break;
322        }
323        argc++;
324        argv = realloc(argv, sizeof(Token *) * argc);
325        if (argv == NULL)
326            FatalError("Out of memory");
327        argv[argc-1] = token;
328    }
329    if (argc == 0)
330        return TRUE;
331
332    /* call corresponding command procedure */
333    if (argv[0]->type != TOKEN_SYMBOL)
334        parseError(argv[0]->line, "command name required");
335    for (i = 0; i < NCOMMANDS; i++) {
336        if (strcasecmp(command[i].symbol, argv[0]->content.symbol) == 0) {
337            /* parse command */
338            command[i].proc(argc, argv);
339            break;
340        }
341    }
342    if (i == NCOMMANDS)
343        parseError(argv[0]->line, "unknown command `%s'",
344                   argv[0]->content.symbol);
345
346    /* free arguments */
347    for (i = 0; i < argc; i++) {
348        if (argv[i]->type == TOKEN_SYMBOL)
349            free(argv[i]->content.symbol);
350        free(argv[i]);
351    }
352    free(argv);
353    return TRUE;
354}
355
356/*-------------------------------------------------------------------------
357 * function "checkArguments"
358 *
359 *  purpose:  examine the number of arguments and the type of each
360 *            argument.
361 *  argument: (int)n                 : correct number of arguments
362 *            (const enum TokenType *)type : table of types
363 *            (int)argc_m1           : actual number of arguments
364 *            (Token **)argv         : command and arguments
365 *  returns:  nothing
366 *-----------------------------------------------------------------------*/
367static void
368checkArguments(int n, const enum TokenType *type, int argc_m1, Token **argv)
369{
370    int i;
371
372    if (argc_m1 < n)
373        parseError(argv[0]->line, "too few arguments to command `%s'",
374                   argv[0]->content.symbol);
375    if (argc_m1 > n)
376        parseError(argv[0]->line, "too many arguments to command `%s'",
377                   argv[0]->content.symbol);
378    for (i = 0; i < n; i++) {
379        if (argv[i+1]->type != type[i])
380            parseError(argv[i+1]->line,
381                       "type mismatch. argument %d to command `%s'",
382                       i+1, argv[0]->content.symbol);
383    }
384}
385
386typedef struct _Mode {
387    struct _Mode *next;
388    const char *name;
389    int type;
390    int depth;
391    int class;
392    int width, height;
393    X68kFbReg reg;
394} Mode;
395
396static Mode *modeList = NULL;
397static Mode *modeChosen;
398
399/*-------------------------------------------------------------------------
400 * function "parseModeDef"
401 *
402 *  purpose:  define a mode
403 *  argument: (int)argc, (Token **)argv : command and arguments
404 *  returns:  nothing
405 *-----------------------------------------------------------------------*/
406static void
407parseModeDef(int argc, Token **argv)
408{
409    const enum TokenType argtype[] = {
410        /* name       type          depth          class      */
411        TOKEN_SYMBOL, TOKEN_SYMBOL, TOKEN_LITERAL, TOKEN_SYMBOL,
412        /* width       height       */
413        TOKEN_LITERAL, TOKEN_LITERAL,
414        /* register values */
415        TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL,
416        TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL,
417        TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL, TOKEN_LITERAL
418    };
419    Mode *mode;
420    char *symbol;
421    int type, class, width, height;
422
423    checkArguments(18, argtype, argc-1, argv);
424
425    mode = malloc(sizeof(Mode));
426    if (mode == NULL)
427        FatalError("Out of memory");
428    mode->name = strdup(argv[1]->content.symbol);
429
430    /* parse frame buffer type */
431    symbol = argv[2]->content.symbol;
432    mode->type = X68K_FB_NULL;
433    for (type = 1; type < NTYPES; type++) {
434        if (strcasecmp(x68kTypeStr[type], symbol) == 0) {
435            mode->type = type;
436            break;
437        }
438    }
439    if (mode->type == X68K_FB_NULL)
440        parseError(argv[2]->line, "unknown frame buffer type `%s'", symbol);
441
442    mode->depth = argv[3]->content.literal;
443
444    /* parse frame buffer class */
445    symbol = argv[4]->content.symbol;
446    mode->class = ClassInvalid;
447    for (class = 0; class < NCLASSES; class++) {
448	if (strcasecmp(x68kClassStr[class], symbol) == 0) {
449            mode->class = class;
450            break;
451	}
452    }
453    if (mode->class == ClassInvalid)
454        parseError(argv[4]->line, "unknown frame buffer class `%s'", symbol);
455
456    width = mode->width = argv[5]->content.literal;
457    height = mode->height = argv[6]->content.literal;
458
459    /* examine whether type, depth, class, width, and height are
460       a legal combination or not, and then set mode registers */
461    switch (type) {
462        case X68K_FB_TEXT:
463            if (mode->depth == 1 && class == StaticGray &&
464                width <= 1024 && height <= 1024) {
465                mode->reg.videoc.r1 = 0x21e4;
466                mode->reg.videoc.r2 = 0x0020;
467                goto legal;
468            }
469            break;
470        case X68K_FB_GRAPHIC:
471            switch (mode->depth) {
472                case 4:
473                    if ( (class == StaticGray || class == PseudoColor) &&
474                         width <= 1024 && height <= 1024 ) {
475                        mode->reg.videoc.r1 = 0x21e4;
476                        mode->reg.videoc.r2 = 0x0010;
477                        goto legal;
478                    }
479                    break;
480                case 8:
481                    if (class == PseudoColor &&
482                        width <= 512 && height <= 512) {
483                        mode->reg.videoc.r1 = 0x21e4;
484                        mode->reg.videoc.r2 = 0x0003;
485                        goto legal;
486                    }
487                    break;
488                case 15:
489                    if (class == TrueColor &&
490                        width <= 512 && height <= 512) {
491                        mode->reg.videoc.r1 = 0x21e4;
492                        mode->reg.videoc.r2 = 0x000f;
493                        goto legal;
494                    }
495                    break;
496            }
497            break;
498    }
499    parseError(argv[0]->line, "illegal combination of mode parameters");
500  legal:
501
502    /* store register values */
503    mode->reg.crtc.r00 = argv[7]->content.literal;
504    mode->reg.crtc.r01 = argv[8]->content.literal;
505    mode->reg.crtc.r02 = argv[9]->content.literal;
506    mode->reg.crtc.r03 = argv[10]->content.literal;
507    mode->reg.crtc.r04 = argv[11]->content.literal;
508    mode->reg.crtc.r05 = argv[12]->content.literal;
509    mode->reg.crtc.r06 = argv[13]->content.literal;
510    mode->reg.crtc.r07 = argv[14]->content.literal;
511    mode->reg.crtc.r08 = argv[15]->content.literal;
512    mode->reg.crtc.r20 = argv[16]->content.literal;
513    mode->reg.videoc.r0 = argv[17]->content.literal;
514    mode->reg.dotClock = argv[18]->content.literal;
515
516    /* set scroll registers to zero */
517    mode->reg.crtc.r12 = 0;    mode->reg.crtc.r13 = 0;
518    mode->reg.crtc.r14 = 0;    mode->reg.crtc.r15 = 0;
519    mode->reg.crtc.r16 = 0;    mode->reg.crtc.r17 = 0;
520    mode->reg.crtc.r18 = 0;    mode->reg.crtc.r19 = 0;
521
522    /* add new mode to linked mode list */
523    mode->next = modeList;
524    modeList = mode;
525}
526
527/*-------------------------------------------------------------------------
528 * function "parseMode"
529 *
530 *  purpose:  choose a mode from predefined modes
531 *  argument: (int)argc, (Token **)argv : command and arguments
532 *  returns:  nothing
533 *-----------------------------------------------------------------------*/
534static void
535parseMode(int argc, Token **argv)
536{
537    const enum TokenType argtype[]= { TOKEN_SYMBOL };
538    Mode *mode;
539
540    checkArguments(1, argtype, argc-1, argv);
541
542    /* search mode to set from mode list */
543    for (mode = modeList; mode != NULL; mode = mode->next) {
544        if (strcmp(mode->name, argv[1]->content.symbol) == 0)
545            break;
546    }
547    if (mode == NULL)
548        parseError(argv[1]->line, "undefined mode `%s'",
549                   argv[1]->content.symbol);
550
551    x68kScreen[0].type = mode->type;
552    x68kScreen[0].depth = mode->depth;
553    x68kScreen[0].class = mode->class;
554    x68kScreen[0].dpi = 75;
555    x68kScreen[0].x68kreg = mode->reg;
556    x68kScreen[0].scr_width = mode->width;
557    x68kScreen[0].scr_height = mode->height;
558
559    switch (mode->type) {
560        /* for TVRAM frame buffer */
561        case X68K_FB_TEXT:
562            x68kFbProc[0].open = x68kTextOpen;
563            x68kFbProc[0].init = x68kTextInit;
564            x68kFbProc[0].close = x68kTextClose;
565            x68kScreen[0].fb_width = 1024;
566            x68kScreen[0].fb_height = 1024;
567            break;
568        /* for GVRAM frame buffer */
569        case X68K_FB_GRAPHIC:
570            x68kFbProc[0].open = x68kGraphOpen;
571            x68kFbProc[0].init = x68kGraphInit;
572            x68kFbProc[0].close = x68kGraphClose;
573	    x68kFormat = malloc (sizeof(PixmapFormatRec));
574	    x68kFormat->scanlinePad = BITMAP_SCANLINE_PAD;
575            x68kFormat->bitsPerPixel = 16;
576            switch (mode->depth) {
577                case 4:
578                    x68kFormat->depth = 4;
579                    x68kScreen[0].fb_width = 1024;
580                    x68kScreen[0].fb_height = 1024;
581                    break;
582                case 8:
583                    x68kFormat->depth = 8;
584                    x68kScreen[0].fb_width = 512;
585                    x68kScreen[0].fb_height = 512;
586                    break;
587                case 15:
588                    x68kFormat->depth = 15;
589                    x68kScreen[0].fb_width = 512;
590                    x68kScreen[0].fb_height = 512;
591            }
592    }
593    modeSet = TRUE;
594    modeChosen = mode;
595}
596
597/*-------------------------------------------------------------------------
598 * function "parseMouse"
599 *
600 *  purpose:  set mouse attribute.
601 *  argument: (int)argc, (Token **)argv : command and arguments
602 *  returns:  nothing
603 *-----------------------------------------------------------------------*/
604static void
605parseMouse(int argc, Token **argv)
606{
607    const enum TokenType argtype[] = { TOKEN_SYMBOL };
608
609    checkArguments(1, argtype, argc-1, argv);
610    /* only `standard' mouse allowed */
611    if (strcasecmp("standard", argv[1]->content.symbol) != 0)
612        parseError(argv[1]->line, "unknown mouse type `%s'",
613                   argv[1]->content.symbol);
614}
615
616/*-------------------------------------------------------------------------
617 * function "parseKeyboard"
618 *
619 *  purpose:  select keyboard map
620 *  argument: (int)argc, (Token **)argv : command and arguments
621 *  returns:  nothing
622 *-----------------------------------------------------------------------*/
623static void
624parseKeyboard(int argc, Token **argv)
625{
626    const enum TokenType argtype[] = { TOKEN_SYMBOL };
627
628    checkArguments(1, argtype, argc-1, argv);
629    if (strcasecmp("standard", argv[1]->content.symbol) == 0) {
630        x68kKeySyms = &jisKeySyms;
631        x68kKbdPriv.type = X68K_KB_STANDARD;
632    } else if (strcasecmp("ascii", argv[1]->content.symbol) == 0) {
633        x68kKeySyms = &asciiKeySyms;
634        x68kKbdPriv.type = X68K_KB_ASCII;
635    } else
636        parseError(argv[1]->line, "unknown keyboard type `%s'",
637                   argv[1]->content.symbol);
638}
639
640/*-------------------------------------------------------------------------
641 * function "parseError"
642 *
643 *  purpose:  print error message to log and stderr and abort Xserver.
644 *  argument: (int)line         : the line in which some error was detected
645 *            (const char *)str : error message
646 *  returns:  nothing
647 *-----------------------------------------------------------------------*/
648static void
649parseError(int line, const char *str, ...)
650{
651    va_list arglist;
652
653    LogMessageVerb(X_ERROR, 0, "parse error in %s at line %d",
654        configFilename, line);
655    if (str != NULL) {
656        LogMessageVerb(X_NONE, 0, ":\n\t");
657	va_start(arglist, str);
658	LogVMessageVerb(X_NONE, 0, str, arglist);
659	va_end(arglist);
660    }
661    LogMessageVerb(X_NONE, 0, "\n");
662    FatalError("Error in X68k server config file. Exiting.\n");
663}
664
665/*-------------------------------------------------------------------------
666 * function "logConfig"
667 *
668 *  purpose:  print specified config settings to log.
669 *  argument: nothing
670 *  returns:  nothing
671 *-----------------------------------------------------------------------*/
672static void
673logConfig(void)
674{
675
676    LogMessage(X_CONFIG, "Using specified mode: \"%s\"\n", modeChosen->name);
677    LogMessage(X_CONFIG, "Type: %s, Class: %s, Size: %dx%d, Depth: %d\n",
678        x68kTypeStr[modeChosen->type], x68kClassStr[modeChosen->class],
679        modeChosen->width, modeChosen->height, modeChosen->depth);
680    LogMessage(X_CONFIG, "Keyboard: %s\n",
681        x68kKbdPriv.type == X68K_KB_ASCII ? "ascii" : "standard");
682    LogMessage(X_CONFIG, "Mouse: %s\n", "standard");
683}
684
685/* EOF x68kConfig.c */
686