1 /* ibm1130_stddev.c: IBM 1130 standard I/O devices simulator
3 Based on the SIMH simulator package written by Robert M Supnik
9 2004.10.22 - Removed stub for xio_1134_papertape as it's now a supported device
11 2003.11.23 - Fixed bug in new routine "quotefix" that made sim crash
12 for all non-Windows builds :(
14 2003.06.15 - added output translation code to accomodate APL font
15 added input translation feature to assist emulation of 1130 console keyboard for APL
16 changes to console input and output IO emulation, fixed bugs exposed by APL interpreter
18 2002.09.13 - pulled 1132 printer out of this file into ibm1130_prt.c
20 * (C) Copyright 2002, Brian Knittel.
21 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
22 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the
23 * usual yada-yada. Please keep this notice and the copyright in any distributions
26 * This is not a supported product, but I welcome bug reports and fixes.
27 * Mail to simh@ibm1130.org
29 * Notes about overstrike mapping:
30 * The 1130 console printer used a Selectric typewriter element. The APL interpreter
31 * used overprinting to construct some APL symbols, for example, a round O overstruck]
32 * with | to get the greek phi. This doesn't accomodate a glass terminal! Instead,
33 * modern APL fonts have separate character codes for the complex characters.
34 * To have APL\1130 output appear correctly, we have to do three things:
36 * use simh's telnet feature to connect to the 1130 console stream
37 * have the telnet program use an APL font
38 * detect combinations of overstruck symbols, and generate the approrpiate alternate codes.
40 * There is a built-in table of font mappings and overstrike mappings, for the APLPLUS.TTF
41 * truetype font widely available on the Internet. An font descriptor file can be used
42 * to specify alternate mappings.
44 * The APL font codes and overstrike mapping can be enabled with the simh command
50 * set tto ascii (this is the default)
52 * APL also uses the red and black ribbon selection. The emulator will output
53 * ansi red/black foreground commands with the setting
57 * The codes can be disabled with
59 * set tto noansi (this is the default)
61 * Finally, when APL mode is active, the emulator does some input key translations
62 * to let the standard ASCII keyboard more closely match the physical layout of the
63 * 1130 console keyboard. The numeric and punctuation key don't have their
64 * traditional meaning under APL. The input mapping lets you use the APL keyboard
65 * layout shown in the APL documentation.
67 * The translations are:
69 * ASCII Position on keyboard To 1130 Key APL interpretation
70 * ------------------------------------ --------------------------------
71 * [ (key to right of P) \r Enter left arrow
72 * ; (1st key to right of L) \b Backspace [
73 * ' (2nd key to right of L) ^U Erase Fld ]
74 * 2 (key above Q) @ @ up shift
75 * 3 (key above W) % % up right shift
76 * 4 (key above E) * * +
77 * 5 (key above R) < < multiply
78 * 8 (key above U) - - Return
79 * 9 (key above I) / / Backspace
80 * - (key above P) ^Q INT REQ ATTN
82 * backsp / / Backspace
85 #include "ibm1130_defs.h"
88 /* #define DEBUG_CONSOLE */
90 /* ---------------------------------------------------------------------------- */
92 static void badio (char *dev
)
94 /* the real 1130 just ignores attempts to use uninstalled devices. They get tested
95 * at times, so it's best to just be quiet about this
96 * printf("%s I/O is not yet supported", dev);
100 void xio_1231_optical (int32 addr
, int32 func
, int32 modify
) {badio("optical mark");}
101 void xio_system7 (int32 addr
, int32 func
, int32 modify
) {badio("System 7");}
103 /* ---------------------------------------------------------------------------- */
105 #define MAX_OUTPUT_COLUMNS 100 /* width of 1130 console printer */
106 #define MAX_OS_CHARS 4 /* maximum number of overstruck characters that can be mapped */
107 #define MAX_OS_MAPPINGS 100 /* maximum number of overstrike mappings */
109 typedef struct tag_os_map
{ /* os_map = overstrike mapping */
110 int ch
; /* ch = output character */
111 int nin
; /* nin = number of overstruck characters */
112 unsigned char inlist
[MAX_OS_CHARS
]; /* inlist = overstruck ASCII characters, sorted. NOT NULL TERMINATED */
115 extern UNIT
*sim_clock_queue
;
118 static int32 tti_dsw
= 0; /* device status words */
119 static int32 tto_dsw
= 0;
122 static unsigned char conout_map
[256]; /* 1130 console code to ASCII translation. 0 = undefined, 0xFF = IGNR_ = no output */
123 static unsigned char conin_map
[256]; /* input mapping */
124 static int curcol
= 0; /* current typewriter element column, leftmost = 0 */
125 static int maxcol
= 0; /* highest curcol seen in this output line */
126 static unsigned char black_ribbon
[30]; /* output escape sequence for black ribbon shift */
127 static unsigned char red_ribbon
[30]; /* output escape sequence for red ribbon shift */
129 static OS_MAP os_buf
[MAX_OUTPUT_COLUMNS
]; /* current typewriter output line, holds character struck in each column */
130 static OS_MAP os_map
[MAX_OS_MAPPINGS
]; /* overstrike mapping entries */
131 static int n_os_mappings
; /* number of overstrike mappings */
133 static t_stat
tti_svc(UNIT
*uptr
);
134 static t_stat
tto_svc(UNIT
*uptr
);
135 static t_stat
tti_reset(DEVICE
*dptr
);
136 static t_stat
tto_reset(DEVICE
*dptr
);
138 static t_stat
emit_conout_character(int ch
);
139 static t_stat
map_conout_character(int ch
);
140 static void reset_mapping (void);
141 static void set_conout_mapping(int32 flags
);
142 static t_stat
validate_conout_mapping(UNIT
*uptr
, int32 match
, char *cvptr
, void *desc
);
143 static void set_default_mapping(int32 flags
);
144 static void finish_conout_mapping(int32 flags
);
145 static void strsort (int n
, unsigned char *s
); /* sorts an array of n characters */
146 static int os_map_comp (OS_MAP
*a
, OS_MAP
*b
); /* compares two mapping entries */
147 static t_stat
font_cmd(int32 flag
, char *cptr
); /* handles font command */
148 static void read_map_file(FILE *fd
); /* reads a font map file */
149 static t_bool
str_match(char *str
, char *keyword
); /* keyword/string comparison */
150 static char * handle_map_ansi_definition(char **pc
); /* input line parsers for map file sections */
151 static char * handle_map_input_definition(char **pc
);
152 static char * handle_map_output_definition(char **pc
);
153 static char * handle_map_overstrike_definition(char **pc
);
155 extern t_stat
sim_poll_kbd(void);
156 extern t_stat
sim_wait_kbd(void);
157 extern t_stat
sim_putchar(int32 out
);
159 #define UNIT_V_CSET (UNIT_V_UF + 0) /* user flag: character set */
160 #define UNIT_V_LOCKED (UNIT_V_UF + 2) /* user flag: keyboard locked */
161 #define UNIT_V_ANSI (UNIT_V_UF + 3)
163 #define CSET_ASCII (0u << UNIT_V_CSET)
164 #define CSET_1130 (1u << UNIT_V_CSET)
165 #define CSET_APL (2u << UNIT_V_CSET)
166 #define CSET_MASK (3u << UNIT_V_CSET)
167 #define ENABLE_ANSI (1u << UNIT_V_ANSI)
169 #define KEYBOARD_LOCKED (1u << UNIT_V_LOCKED)
171 #define IRQ_KEY 0x11 /* ctrl-Q */
172 #define PROGRAM_STOP_KEY 0x10 /* ctrl-P */
174 #include "ibm1130_conout.h" /* conout_to_ascii table */
175 #include "ibm1130_conin.h" /* ascii_to_conin table */
177 /* TTI data structures
179 tti_dev TTI device descriptor
180 tti_unit TTI unit descriptor
181 tti_reg TTI register list
184 UNIT tti_unit
= { UDATA (&tti_svc
, 0, 0), KBD_POLL_WAIT
};
187 { ORDATA (BUF
, tti_unit
.buf
, 16) },
188 { ORDATA (DSW
, tti_dsw
, 16) },
189 { DRDATA (POS
, tti_unit
.pos
, 31), PV_LEFT
},
190 { DRDATA (STIME
, tti_unit
.wait
, 24), REG_NZ
+ PV_LEFT
},
194 { CSET_MASK
, CSET_ASCII
, "ASCII", "ASCII", NULL
},
195 { CSET_MASK
, CSET_1130
, "1130", "1130", NULL
},
199 "KEYBOARD", &tti_unit
, tti_reg
, tti_mod
,
201 NULL
, NULL
, &tti_reset
,
202 NULL
, basic_attach
, NULL
};
204 /* TTO data structures
206 tto_dev TTO device descriptor
207 tto_unit TTO unit descriptor
208 tto_reg TTO register list
211 /* 14-Nov-03 -- the wait time was SERIAL_OUT_WAIT, but recent versions of SIMH reduced
212 * this to 100, and wouldn't you know it, APL\1130 has about 120 instructions between the XIO WRITE
213 * to the console and the associated WAIT.
216 UNIT tto_unit
= { UDATA (&tto_svc
, 0, 0), 200 };
219 { ORDATA (BUF
, tto_unit
.buf
, 16) },
220 { ORDATA (DSW
, tto_dsw
, 16) },
221 { DRDATA (POS
, tto_unit
.pos
, 31), PV_LEFT
},
222 { DRDATA (STIME
, tto_unit
.wait
, 24), PV_LEFT
},
226 { CSET_MASK
, CSET_ASCII
, "ASCII", "ASCII", validate_conout_mapping
, NULL
, NULL
},
227 { CSET_MASK
, CSET_1130
, "1130", "1130", validate_conout_mapping
, NULL
, NULL
},
228 { CSET_MASK
, CSET_APL
, "APL", "APL", validate_conout_mapping
, NULL
, NULL
},
229 { ENABLE_ANSI
,0, "NOANSI", "NOANSI", NULL
},
230 { ENABLE_ANSI
,ENABLE_ANSI
, "ANSI", "ANSI", NULL
},
234 "TTO", &tto_unit
, tto_reg
, tto_mod
,
236 NULL
, NULL
, &tto_reset
,
237 NULL
, basic_attach
, NULL
};
239 /* Terminal input routines
241 tti_svc process event (character ready)
242 tti_reset process reset
243 tto_svc process event (print character)
244 tto_reset process reset
247 #define TT_DSW_PRINTER_RESPONSE 0x8000
248 #define TT_DSW_KEYBOARD_RESPONSE 0x4000
249 #define TT_DSW_INTERRUPT_REQUEST 0x2000
250 #define TT_DSW_KEYBOARD_CONSOLE 0x1000
251 #define TT_DSW_PRINTER_BUSY 0x0800
252 #define TT_DSW_PRINTER_NOT_READY 0x0400
253 #define TT_DSW_KEYBOARD_BUSY 0x0200
255 void xio_1131_console (int32 iocc_addr
, int32 func
, int32 modify
)
262 SETBIT(tti_dsw
, TT_DSW_KEYBOARD_BUSY
); /* select and unlock the keyboard */
263 keyboard_selected(TRUE
);
264 CLRBIT(tti_unit
.flags
, KEYBOARD_LOCKED
);
265 tti_unit
.buf
= 0; /* no key character yet */
269 WriteW(iocc_addr
, tti_unit
.buf
); /* return keycode */
270 CLRBIT(tti_dsw
, TT_DSW_KEYBOARD_BUSY
); /* this ends selected mode */
271 keyboard_selected(FALSE
);
272 SETBIT(tti_unit
.flags
, KEYBOARD_LOCKED
); /* keyboard is locked when not selected */
273 tti_unit
.buf
= 0; /* subsequent reads will return zero */
277 ch
= (ReadW(iocc_addr
) >> 8) & 0xFF; /* get character to write */
278 tto_unit
.buf
= emit_conout_character(ch
); /* output character and save write status */
280 /* fprintf(stderr, "[CONOUT] %02x\n", ch); */
282 SETBIT(tto_dsw
, TT_DSW_PRINTER_BUSY
);
283 sim_activate(&tto_unit
, tto_unit
.wait
); /* schedule interrupt */
287 ACC
= tto_dsw
| tti_dsw
;
288 if (modify
& 0x01) { /* reset interrupts */
289 CLRBIT(tto_dsw
, TT_DSW_PRINTER_RESPONSE
);
290 CLRBIT(tti_dsw
, TT_DSW_KEYBOARD_RESPONSE
);
291 CLRBIT(tti_dsw
, TT_DSW_INTERRUPT_REQUEST
);
292 CLRBIT(ILSW
[4], ILSW_4_CONSOLE
);
297 sprintf(msg
, "Invalid console XIO function %x", func
);
301 /* fprintf(stderr, "After XIO %04x %04x\n", tti_dsw, tto_dsw); */
304 /* emit_conout_character - write character with 1130 console code 'ch' */
306 t_stat
emit_conout_character (int ch
)
311 printf("{%02x}", ch
);
314 if ((tto_unit
.flags
& CSET_MASK
) == CSET_1130
) /* 1130 (binary) mode, write the raw 8-bit value */
315 return sim_putchar(ch
);
317 if (ch
& COUT_IS_CTRL
) {
318 /* red/black shift can be combined with another control */
319 /* if present, emit the color shift characters alone */
321 if (ch
& COUT_CTRL_BLACK
) {
322 if ((status
= map_conout_character(COUT_IS_CTRL
|COUT_CTRL_BLACK
)) != SCPE_OK
)
325 else if (ch
& COUT_CTRL_RED
) {
326 if ((status
= map_conout_character(COUT_IS_CTRL
|COUT_CTRL_RED
)) != SCPE_OK
)
330 ch
&= ~(COUT_CTRL_BLACK
|COUT_CTRL_RED
); /* remove the ribbon shift bits */
332 if (ch
& ~COUT_IS_CTRL
) { /* if another control remains, emit it */
333 if ((status
= map_conout_character(ch
)) != SCPE_OK
)
340 return map_conout_character(ch
);
343 static void Beep (void) /* notify user keyboard was locked or key was bad */
348 /* tti_svc - keyboard polling (never stops) */
350 static t_stat
tti_svc (UNIT
*uptr
)
354 if (cgi
) /* if running in CGI mode, no keyboard and no keyboard polling! */
356 /* otherwise, so ^E can interrupt the simulator, */
357 sim_activate(&tti_unit
, tti_unit
.wait
); /* always continue polling keyboard */
359 assert(sim_clock_queue
!= NULL
);
361 temp
= sim_poll_kbd();
363 if (temp
< SCPE_KFLAG
)
364 return temp
; /* no char or error? */
366 temp
&= 0xFF; /* remove SCPE_KFLAG */
368 if ((tti_unit
.flags
& CSET_MASK
) == CSET_ASCII
)
369 temp
= conin_map
[temp
] & 0xFF; /* perform input translation */
371 if (temp
== IRQ_KEY
) { /* INT REQ (interrupt request) key */
372 SETBIT(tti_dsw
, TT_DSW_INTERRUPT_REQUEST
); /* queue interrupt */
373 SETBIT(ILSW
[4], ILSW_4_CONSOLE
);
376 CLRBIT(tti_unit
.flags
, KEYBOARD_LOCKED
); /* keyboard restore, according to func. char. manual */
381 tti_unit
.buf
= 0; /* subsequent reads need to return 0 (required by APL\1130) */
385 if (temp
== PROGRAM_STOP_KEY
) { /* simulate the program stop button */
386 SETBIT(con_dsw
, CPU_DSW_PROGRAM_STOP
);
387 SETBIT(ILSW
[5], ILSW_5_INT_RUN_PROGRAM_STOP
);
397 if ((tti_unit
.flags
& KEYBOARD_LOCKED
) || ! (tti_dsw
& TT_DSW_KEYBOARD_BUSY
)) {
402 if ((tti_unit
.flags
& CSET_MASK
) == CSET_ASCII
)
403 temp
= ascii_to_conin
[temp
];
405 if (temp
== 0) { /* ignore invalid characters */
411 tti_unit
.buf
= temp
& 0xFFFE; /* save keystroke except last bit (not defined) */
412 tti_unit
.pos
= tti_unit
.pos
+ 1; /* but it lets us distinguish 0 from no punch ' ' */
415 printf("[%04x]", tti_unit
.buf
& 0xFFFF);
418 SETBIT(tti_unit
.flags
, KEYBOARD_LOCKED
); /* prevent further keystrokes */
420 SETBIT(tti_dsw
, TT_DSW_KEYBOARD_RESPONSE
); /* queue interrupt */
421 SETBIT(ILSW
[4], ILSW_4_CONSOLE
);
424 /* fprintf(stderr, "TTI interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */
429 static t_stat
tti_reset (DEVICE
*dptr
)
434 CLRBIT(ILSW
[4], ILSW_4_CONSOLE
);
436 keyboard_selected(FALSE
);
438 SETBIT(tti_unit
.flags
, KEYBOARD_LOCKED
);
441 sim_cancel(&tti_unit
); /* in cgi mode, never poll keyboard */
443 sim_activate(&tti_unit
, tti_unit
.wait
); /* otherwise, always poll keyboard */
448 /* basic_attach - fix quotes in filename, then call standard unit attach routine */
450 t_stat
basic_attach (UNIT
*uptr
, char *cptr
)
452 return attach_unit(uptr
, quotefix(cptr
)); /* fix quotes in filenames & attach */
455 /* quotefix - strip off quotes around filename, if present */
457 char * quotefix (char * cptr
)
459 #ifdef WIN32 /* do this only for Windows builds, for the time being */
463 if (*cptr
== '"' || *cptr
== '\'') {
464 quote
= *cptr
++; /* remember quote and skip over it */
466 for (c
= cptr
; *c
&& *c
!= quote
; c
++)
467 ; /* find closing quote, or end of string */
469 if (*c
) /* terminate string at closing quote */
474 return cptr
; /* return pointer to cleaned-up name */
477 t_bool
keyboard_is_busy (void) /* return TRUE if keyboard is not expecting a character */
479 return (tti_dsw
& TT_DSW_KEYBOARD_BUSY
);
482 static t_stat
tto_svc (UNIT
*uptr
)
484 CLRBIT(tto_dsw
, TT_DSW_PRINTER_BUSY
);
485 SETBIT(tto_dsw
, TT_DSW_PRINTER_RESPONSE
);
487 SETBIT(ILSW
[4], ILSW_4_CONSOLE
);
490 /* fprintf(stderr, "TTO interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */
492 return (t_stat
) tto_unit
.buf
; /* return status saved during output conversion */
495 static t_stat
tto_reset (DEVICE
*dptr
)
500 CLRBIT(ILSW
[4], ILSW_4_CONSOLE
);
503 sim_cancel(&tto_unit
); /* deactivate unit */
505 set_conout_mapping(tto_unit
.flags
); /* initialize the overstrike mappings */
506 /* register the font-mapping command */
507 register_cmd("FONT", font_cmd
, 0, "font MAPFILE use font mapping definitions in MAPFILE\n");
513 # pragma warning(disable:4245) /* enable int->char demotion warning caused by characters with high-bit set */
516 static struct { /* default input mapping for APL */
520 { /* these map input keys to those in like positions on 1130 keyboard */
521 '[', '\r', /* enter (EOF) is APL left arrow */
522 ';', '\b', /* backspace is APL [ */
523 '\'', '\x15', /* ctrl-U, erase field, is APL ]*/
524 '2', '@', /* APL upshift */
525 '3', '%', /* APL rightshift */
526 '4', '*', /* APL + and - */
527 '5', '<', /* APL x and divide */
528 '8', '-', /* APL return */
529 '9', '/', /* APL backspace */
530 '-', IRQ_KEY
, /* ctrl-q (INT REQ), APL ATTN */
531 '\r', '-', /* APL return */
532 '\b', '/' /* APL backspace */
535 #define NCONIN_TO_APL (sizeof(conin_to_APL)/sizeof(conin_to_APL[0]))
537 static struct { /* default output mapping for APLPLUS font */
542 '\x01', IGNR_
, /* controls */
544 '\x05', IGNR_
, /* (black and red are handled by ansi sequences) */
551 '\xC4', '\x30', /* (if you're curious, order here is position on APL typeball) */
644 #define NCONOUT_TO_APL (sizeof(conout_to_APL)/sizeof(conout_to_APL[0]))
646 static OS_MAP default_os_map
[] = /* overstrike mapping for APLPLUS font */
648 '\x8a', 2, "\x5e\x7e",
649 '\x8b', 2, "\x9f\x7e",
650 '\x8d', 2, "\x8c\x27",
651 '\x8e', 3, "\x8c\x2d\x3a",
652 '\x8f', 2, "\x91\x5f",
653 '\x90', 2, "\x92\x7e",
654 '\x93', 2, "\x91\x7c",
655 '\x94', 2, "\x92\x7c",
656 '\x95', 2, "\xb0\x82",
657 '\x96', 2, "\xb0\x83",
658 '\x99', 2, "\x2d\x5c",
659 '\x9a', 2, "\x2d\x2f",
660 '\xae', 2, "\x2c\x2d",
661 '\xb2', 2, "\xb1\x7c",
662 '\xb3', 2, "\xb1\x5c",
663 '\xb4', 2, "\xb1\x2d",
664 '\xb5', 2, "\xb1\x2a",
665 '\xba', 2, "\xb9\x5f",
666 '\xd0', 2, "\x30\x7e",
667 '\xd8', 2, "\x4f\x2f",
668 '\x21', 2, "\x27\x2e",
669 '\xa4', 2, "\xb0\xb1", /* map degree in circle to circle cross (APL uses this as character error symbol) */
670 '\xf0', 2, "\xb0\xa8",
671 '\xfe', 2, "\x3a\xa8",
675 # pragma warning(default:4245) /* enable int->char demotion warning */
678 /* os_map_comp - compare to OS_MAP entries */
680 static int os_map_comp (OS_MAP
*a
, OS_MAP
*b
)
682 unsigned char *sa
, *sb
;
694 for (i
= a
->nin
; --i
>= 0;) {
708 /* strsort - sorts the n characters of array 's' using insertion sort */
710 static void strsort (int n
, unsigned char *s
)
715 while (--n
> 0) { /* repeatedly */
716 big
= 0; /* find largest value of s[0]...s[n] */
717 for (i
= 1; i
<= n
; i
++)
718 if (s
[i
] > s
[big
]) big
= i
;
720 temp
= s
[n
]; /* put largest value at end of array */
728 [font XXX] font named XXX
729 OUT failure character
730 OUT IN single character mapping
731 OUT IN IN ... overstrike mapping
735 static void set_conout_mapping (int32 flags
)
740 /* set the default mappings. We may later override them with settings from an ini file */
742 set_default_mapping(flags
);
745 /* finish_conout_mapping - sort the finalized overstrike mapping */
747 static void finish_conout_mapping (int32 flags
)
752 for (i
= 0; i
< n_os_mappings
; i
++) /* sort the inlist strings individually */
753 strsort(os_map
[i
].nin
, os_map
[i
].inlist
);
755 for (n
= n_os_mappings
; --n
> 0; ) { /* then sort the os_map array itself with insertion sort */
756 big
= 0; /* find largest value of s[0]...s[n] */
757 for (i
= 1; i
<= n
; i
++)
758 if (os_map_comp(os_map
+i
, os_map
+big
) > 0) big
= i
;
761 temp
= os_map
[n
]; /* put largest value at end of array */
762 os_map
[n
] = os_map
[big
];
768 /* validate_conout_mapping - called when set command gets a new value */
770 static t_stat
validate_conout_mapping (UNIT
*uptr
, int32 match
, char *cvptr
, void *desc
)
772 set_conout_mapping(match
);
776 static void reset_mapping (void)
780 black_ribbon
[0] = '\0'; /* erase the ribbon sequences */
781 red_ribbon
[0] = '\0';
783 memset(conout_map
, 0, sizeof(conout_map
)); /* erase output mapping */
785 n_os_mappings
= 0; /* erase overstrike mapping */
787 for (i
= (sizeof(conin_map
)/sizeof(conin_map
[0])); --i
>= 0; )
788 conin_map
[i
] = (unsigned char) i
; /* default conin_map is identity map */
791 /* set_default_mapping - create standard font and overstrike map */
793 static void set_default_mapping (int32 flags
)
799 strcpy((char *) black_ribbon
, "\033[30m");
800 strcpy((char *) red_ribbon
, "\033[31m");
802 switch (flags
& CSET_MASK
) {
807 memcpy(conout_map
, conout_to_ascii
, sizeof(conout_to_ascii
));
811 for (i
= NCONOUT_TO_APL
; --i
>= 0; )
812 conout_map
[conout_to_APL
[i
].in
] = conout_to_APL
[i
].out
;
814 for (i
= NCONIN_TO_APL
; --i
>= 0; )
815 conin_map
[conin_to_APL
[i
].in
] = conin_to_APL
[i
].out
;
817 memcpy(os_map
, default_os_map
, sizeof(default_os_map
));
818 n_os_mappings
= (sizeof(default_os_map
) / sizeof(default_os_map
[0]));
822 finish_conout_mapping(flags
); /* sort conout mapping if necessary */
825 /* sim_putstr - write a string to the console */
827 t_stat
sim_putstr (char *s
)
832 if ((status
= sim_putchar(*s
)) != SCPE_OK
)
841 /* map_conout_character - translate and write a single character */
843 static t_stat
map_conout_character (int ch
)
848 if (ch
== (COUT_IS_CTRL
|COUT_CTRL_BLACK
))
849 return (tto_unit
.flags
& ENABLE_ANSI
) ? sim_putstr((char *) black_ribbon
) : SCPE_OK
;
851 if (ch
== (COUT_IS_CTRL
|COUT_CTRL_RED
))
852 return (tto_unit
.flags
& ENABLE_ANSI
) ? sim_putstr((char *) red_ribbon
) : SCPE_OK
;
854 if ((ch
= conout_map
[ch
& 0xFF]) == 0)
855 ch
= '?'; /* unknown character? print ? */
857 if (ch
== '\n') { /* newline: reset overstrike buffer */
861 else if (ch
== '\r') { /* carriage return: rewind to column 0 */
863 maxcol
= -1; /* assume it advances paper too */
865 else if (ch
== '\b') { /* backspace: back up one character */
869 else if (n_os_mappings
&& ch
!= (unsigned char) IGNR_
) {
870 if (curcol
>= MAX_OUTPUT_COLUMNS
)
871 map_conout_character('\x81'); /* precede with automatic carriage return/line feed, I guess */
873 if (curcol
> maxcol
) { /* first time in this column, no overstrike possible yet */
874 os_buf
[curcol
].nin
= 0;
878 if (ch
!= ' ' && ch
!= 0) { /* (if it's not a blank or unknown) */
879 os_buf
[curcol
].inlist
[os_buf
[curcol
].nin
] = (unsigned char) ch
;
880 strsort(++os_buf
[curcol
].nin
, os_buf
[curcol
].inlist
);
883 if (os_buf
[curcol
].nin
== 0) /* if nothing but blanks seen, */
884 ch
= ' '; /* output is a blank */
885 else if (os_buf
[curcol
].nin
== 1) { /* if only one printing character seen, display it */
886 ch
= os_buf
[curcol
].inlist
[0];
888 else { /* otherwise look up mapping */
891 for (i
= 0; i
< n_os_mappings
; i
++) {
892 cmp
= os_map_comp(&os_buf
[curcol
], &os_map
[i
]);
893 if (cmp
== 0) { /* a hit */
897 else if (cmp
< 0) /* not found */
902 if (curcol
< MAX_OUTPUT_COLUMNS
) /* this should now never happen, as we automatically return */
912 if ((status
= sim_putchar('\r')) != SCPE_OK
)
918 if ((status
= sim_putchar('\n')) != SCPE_OK
)
921 tto_unit
.pos
++; /* hmm, why do we count these? */
925 if ((status
= sim_putchar(ch
)) != SCPE_OK
)
935 /* font_cmd - parse a font mapping file. Sets input and output translations */
937 static t_stat
font_cmd (int32 flag
, char *cptr
)
942 while (*cptr
&& (*cptr
<= ' ')) cptr
++; /* skip blanks */
943 if (! *cptr
) return SCPE_2FARG
; /* argument missing */
945 fname
= cptr
; /* save start */
946 if (*cptr
== '\'' || *cptr
== '"') { /* quoted string */
947 quote
= *cptr
++; /* remember quote character */
948 fname
++; /* skip the quote */
950 while (*cptr
&& (*cptr
!= quote
)) /* find closing quote */
954 while (*cptr
&& (*cptr
> ' ')) /* find terminating blank */
957 *cptr
= '\0'; /* terminate name */
959 if ((fd
= fopen(fname
, "r")) == NULL
)
962 reset_mapping(); /* remove all default mappings */
967 finish_conout_mapping(tto_unit
.flags
);
971 /* str_match - compare the string str to the keyword, case insensitive */
973 static t_bool
str_match (char *str
, char *keyword
)
977 while (*keyword
) { /* see if str matches the keyword... */
978 kch
= *keyword
++; /* get pair of characters */
981 if (BETWEEN(kch
, 'A', 'Z')) kch
+= 32; /* change upper to lower case */
982 if (BETWEEN(sch
, 'A', 'Z')) sch
+= 32;
984 if (kch
!= sch
) /* characters must match; if not, quit */
988 return *str
<= ' ' || *str
== ';'; /* success if the input string ended or is in whitespace or comment */
991 /* read_map_file - process definition lines in opened mapping file */
993 static void read_map_file (FILE *fd
)
995 char str
[256], *c
, *errmsg
;
997 enum {SECT_UNDEFINED
, SECT_DEFAULT
, SECT_ANSI
, SECT_INPUT
, SECT_OUTPUT
, SECT_OVERSTRIKE
}
998 section
= SECT_UNDEFINED
;
1000 while (fgets(str
, sizeof(str
), fd
) != NULL
) {
1001 ++lineno
; /* count input lines */
1003 if ((c
= strchr(str
, '\n')) != NULL
) /* terminate at newline */
1006 for (c
= str
; *c
&& *c
<= ' '; c
++) /* skip blanks */
1009 if (c
[0] == '\0' || c
[0] == ';') /* ignore blank lines and lines starting with ; */
1013 if (str_match(c
, "[default]")) { /* check for section separators */
1014 set_default_mapping(tto_unit
.flags
);
1015 section
= SECT_UNDEFINED
;
1018 if (str_match(c
, "[ansi]")) {
1019 section
= SECT_ANSI
;
1022 if (str_match(c
, "[input]")) {
1023 section
= SECT_INPUT
;
1026 if (str_match(c
, "[output]")) {
1027 section
= SECT_OUTPUT
;
1030 if (str_match(c
, "[overstrike]")) {
1031 section
= SECT_OVERSTRIKE
;
1036 switch (section
) { /* if we get here, we have a definition line */
1038 errmsg
= handle_map_ansi_definition(&c
);
1041 errmsg
= handle_map_input_definition(&c
);
1044 errmsg
= handle_map_output_definition(&c
);
1046 case SECT_OVERSTRIKE
:
1047 errmsg
= handle_map_overstrike_definition(&c
);
1050 errmsg
= "line occurs before valid [section]";
1054 if (errmsg
== NULL
) { /* if no other error detected, */
1055 while (*c
&& *c
<= ' ') /* skip past any whitespace */
1058 if (*c
&& *c
!= ';') /* if line doesn't end or run into a comment, complain */
1059 errmsg
= "too much stuff on input line";
1062 if (errmsg
!= NULL
) { /* print error message and offending line */
1063 printf("* Warning: %s", errmsg
);
1065 switch (section
) { /* add section name if possible */
1066 case SECT_ANSI
: errmsg
= "ansi"; break;
1067 case SECT_INPUT
: errmsg
= "input"; break;
1068 case SECT_OUTPUT
: errmsg
= "output"; break;
1069 case SECT_OVERSTRIKE
: errmsg
= "overstrike"; break;
1070 default: errmsg
= NULL
; break;
1073 printf(" in [%s] section", errmsg
);
1075 printf(", line %d\n%s\n", lineno
, str
);
1080 /* get_num_char - read an octal or hex character specification of exactly 'ndigits' digits
1081 * the input pointers is left pointing to the last character of the number, so that it
1082 * may be incremented by the caller
1085 static char * get_num_char (char **pc
, unsigned char *out
, int ndigits
, int base
, char *errmsg
)
1090 while (--ndigits
>= 0) { /* collect specified number of digits */
1091 if (BETWEEN(*c
, '0', '9'))
1093 else if (BETWEEN(*c
, 'A', 'F'))
1094 digit
= *c
- 'A' + 10;
1095 else if (BETWEEN(*c
, 'a', 'f'))
1096 digit
= *c
- 'a' + 10;
1100 if (digit
>= base
) /* bad digit */
1103 ch
= ch
* base
+ digit
; /* accumulate digit */
1107 *out
= (unsigned char) ch
; /* return parsed character */
1108 *pc
= c
-1; /* make input pointer point to last character seen */
1109 return NULL
; /* no error */
1112 /* get_characters - read character specification(s) from input string pointed to
1113 * by *pc. Results stored in outstr; up to nmax characters parsed. Actual number
1114 * found returned in *nout. Returns NULL on success or error message if syntax
1115 * error encountered. *pc is advanced to next whitespace or whatever followed input.
1118 static char * get_characters (char **pc
, unsigned char *outstr
, int nmax
, int *nout
)
1120 char *c
= *pc
, *errstr
;
1121 unsigned char *out
= outstr
;
1123 while (*c
&& *c
<= ' ') /* skip leading whitespace */
1126 while (--nmax
>= 0) { /* get up to maximum number of characters */
1127 if (*c
== ';' || *c
<= ' ') /* we ran into a comment, whitespace or end of string: we're done */
1130 if (*c
== '\\') { /* backslash escape of some sort */
1132 case 'b': /* backspace */
1137 case 'e': /* ascii ESCAPE */
1142 case 'f': /* formfeed */
1147 case 'n': /* newline */
1152 case 'r': /* return */
1162 case 'x': /* hex specification */
1165 if ((errstr
= get_num_char(&c
, out
, 2, 16, "bad hex character")) != NULL
)
1168 out
++; /* advance out pointer */
1171 default: /* anything else */
1172 if (BETWEEN(*c
, '0', '7')) { /* octal specification */
1173 if ((errstr
= get_num_char(&c
, out
, 3, 8, "bad octal character")) != NULL
)
1176 out
++; /* advance out pointer */
1178 else if (BETWEEN(*c
, 'A', 'Z') || BETWEEN(*c
, 'a', 'z'))
1179 return "invalid \\ escape"; /* other \x letters are bad */
1181 *out
++ = (unsigned char) *c
;/* otherwise, accept \x as literal character x */
1186 else if (*c
== '^') { /* control character */
1188 if (BETWEEN(*c
, 'A', 'Z')) /* convert alpha, e.g. A -> 1 */
1189 *out
++ = (unsigned char) (*c
- 'A' + 1);
1190 else if (BETWEEN(*c
, 'a', 'z'))
1191 *out
++ = (unsigned char) (*c
- 'z' + 1);
1192 else /* non alpha is bad */
1193 return "invalid control letter";
1195 else if (str_match(c
, "IGNORE")) { /* magic word: a character that will never be output */
1196 *out
++ = (unsigned char) IGNR_
;
1200 *out
++ = (unsigned char) *c
; /* save literal character */
1206 if (*c
&& *c
!= ';' && *c
> ' ') /* we should be at end of string, whitespace or comment */
1207 return "too many characters specified";
1209 *pc
= c
; /* save advanced pointer */
1210 *nout
= out
-outstr
; /* save number of characters stored */
1212 return NULL
; /* no error */
1215 /* handle_map_ansi_definition - process line in [ansi] section */
1217 static char * handle_map_ansi_definition (char **pc
)
1219 unsigned char *outstr
;
1223 if (str_match(*pc
, "black")) { /* find which string we're setting */
1224 outstr
= black_ribbon
; /* this is where we'll save the output string */
1225 *pc
+= 5; /* skip over the token */
1227 else if (str_match(*pc
, "red")) {
1228 outstr
= red_ribbon
;
1232 return "invalid variable name";
1233 /* get list of characters */
1234 if ((errmsg
= get_characters(pc
, outstr
, sizeof(black_ribbon
)-1, &n
)) != NULL
)
1237 outstr
[n
] = '\0'; /* null terminate the string */
1239 return (n
> 0) ? NULL
: "missing output string"; /* NULL if OK, error msg if no characters */
1242 /* handle_map_input_definition - process line in [input] section */
1244 static char * handle_map_input_definition (char **pc
)
1246 unsigned char cin
, cout
;
1250 if ((errmsg
= get_characters(pc
, &cin
, 1, &n
)) != NULL
) /* get input character */
1254 return "missing input character";
1256 if ((errmsg
= get_characters(pc
, &cout
, 1, &n
)) != NULL
) /* get output character */
1260 return "missing output character";
1262 conin_map
[cin
] = cout
; /* set the mapping */
1266 /* handle_map_output_definition - process line in [output] section */
1268 static char * handle_map_output_definition (char **pc
)
1270 unsigned char cin
, cout
;
1274 if ((errmsg
= get_characters(pc
, &cin
, 1, &n
)) != NULL
) /* get input character */
1278 return "missing input character";
1280 if ((errmsg
= get_characters(pc
, &cout
, 1, &n
)) != NULL
) /* get output character */
1284 return "missing output character";
1286 conout_map
[cin
] = cout
; /* set the mapping */
1290 /* handle_map_overstrike_definition - process line in [overstrike] section */
1292 static char * handle_map_overstrike_definition (char **pc
)
1294 unsigned char ch
, inlist
[MAX_OS_CHARS
];
1298 if (n_os_mappings
>= MAX_OS_MAPPINGS
) /* os_map is full, no more room */
1299 return "too many overstrike mappings";
1300 /* get output character */
1301 if ((errmsg
= get_characters(pc
, &ch
, 1, &nin
)) != NULL
)
1305 return "missing output character";
1306 /* get input list */
1307 if ((errmsg
= get_characters(pc
, inlist
, MAX_OS_CHARS
, &nin
)) != NULL
)
1310 if (nin
< 2) /* expect at least two characters overprinted */
1311 return "missing input list";
1313 os_map
[n_os_mappings
].ch
= ch
; /* save in next os_map slot */
1314 os_map
[n_os_mappings
].nin
= nin
;
1315 memmove(os_map
[n_os_mappings
].inlist
, inlist
, nin
);