|
|
1.1 root 1: /*
2: * Hatari - console.c
3: *
4: * Copyright (C) 2012 by Eero Tamminen
5: *
6: * This file is distributed under the GNU General Public License, version 2
7: * or at your option any later version. Read the file gpl.txt for details.
8: *
9: * console.c - catching of emulated console output with minimal VT52 emulation.
10: */
11: const char Console_fileid[] = "Hatari console.c : " __DATE__ " " __TIME__;
12:
13: #include <stdio.h>
14: #include <string.h>
15: #include <SDL.h>
16:
17: #include "main.h"
18: #include "m68000.h"
19: #include "stMemory.h"
20: #include "hatari-glue.h"
21: #include "console.h"
22: #include "options.h"
23:
24: /**
25: * Maps Atari characters to their closest ASCII equivalents.
26: */
27: static void map_character(Uint8 value)
28: {
29: static const Uint8 map_0_31[32] = {
30: '.', '.', '.', '.', '.', '.', '.', '.', /* 0x00 */
31: /* white space */
32: '\b','\t','\n','.','.','\r', '.', '.', /* 0x08 */
33: /* LED numbers */
34: '0', '1', '2', '3', '4', '5', '6', '7', /* 0x10 */
35: '8', '9', '.', '.', '.', '.', '.', '.' /* 0x18 */
36: };
37: static const Uint8 map_128_255[128] = {
38: /* accented characters */
39: 'C', 'U', 'e', 'a', 'a', 'a', 'a', 'c', /* 0x80 */
40: 'e', 'e', 'e', 'i', 'i', 'i', 'A', 'A', /* 0x88 */
41: 'E', 'a', 'A', 'o', 'o', 'o', 'u', 'u', /* 0x90 */
42: 'y', 'o', 'u', 'c', '.', 'Y', 'B', 'f', /* 0x98 */
43: 'a', 'i', 'o', 'u', 'n', 'N', 'a', 'o', /* 0xA0 */
44: '?', '.', '.', '.', '.', 'i', '<', '>', /* 0xA8 */
45: 'a', 'o', 'O', 'o', 'o', 'O', 'A', 'A', /* 0xB0 */
46: 'O', '"','\'', '.', '.', 'C', 'R', '.', /* 0xB8 */
47: 'j', 'J', '.', '.', '.', '.', '.', '.', /* 0xC0 */
48: '.', '.', '.', '.', '.', '.', '.', '.', /* 0xC8 */
49: '.', '.', '.', '.', '.', '.', '.', '.', /* 0xD0 */
50: '.', '.', '.', '.', '.', '.', '^', '.', /* 0xD8 */
51: '.', '.', '.', '.', '.', '.', '.', '.', /* 0xE0 */
52: '.', '.', '.', '.', '.', '.', '.', '.', /* 0xE8 */
53: '.', '.', '.', '.', '.', '.', '.', '.', /* 0xF0 */
54: '.', '.', '.', '.', '.', '.', '.', '.' /* 0xF8 */
55: };
56: /* map normal characters to host console */
57: if (value < 32) {
58: fputc(map_0_31[value], stderr);
59: } else if (value > 127) {
60: fputc(map_128_255[value-128], stderr);
61: } else {
62: fputc(value, stderr);
63: }
64: }
65:
66:
67: /**
68: * Convert given console character output to ASCII.
69: * Accepts one character at the time, parses VT52 escape codes
70: * and outputs them on console.
71: *
72: * On host, TOS cursor forwards movement is done with spaces,
73: * backwards movement is delayed until next non-white character
74: * at which point output switches to next line. Other VT52
75: * escape sequences than cursor movement are ignored.
76: */
77: static void vt52_emu(Uint8 value)
78: {
79: /* state machine to handle/ignore VT52 escape sequence */
80: static int escape_index;
81: static int escape_target;
82: static int hpos_host, hpos_tos;
83: static bool need_nl;
84: static enum {
85: ESCAPE_NONE, ESCAPE_POSITION
86: } escape_type;
87:
88: if (escape_target) {
89: if (++escape_index == 1) {
90: /* VT52 escape sequences */
91: switch(value) {
92: case 'E': /* clear screen+home -> newline */
93: fputs("\n", stderr);
94: hpos_host = 0;
95: break;
96: /* sequences with arguments */
97: case 'b': /* foreground color */
98: case 'c': /* background color */
99: escape_target = 2;
100: return;
101: case 'Y': /* cursor position */
102: escape_type = ESCAPE_POSITION;
103: escape_target = 3;
104: return;
105: }
106: } else if (escape_index < escape_target) {
107: return;
108: }
109: if (escape_type == ESCAPE_POSITION) {
110: /* last item gives horizontal position */
111: hpos_tos = value - ' ';
112: if (hpos_tos > 79) {
113: hpos_tos = 79;
114: } else if (hpos_tos < 0) {
115: hpos_tos = 0;
116: }
117: if (hpos_tos > hpos_host) {
118: fprintf(stderr, "%*s", hpos_tos - hpos_host, "");
119: hpos_host = hpos_tos;
120: } else if (hpos_tos < hpos_host) {
121: need_nl = true;
122: }
123: }
124: /* escape sequence end */
125: escape_target = 0;
126: return;
127: }
128: if (value == 27) {
129: /* escape sequence start */
130: escape_type = ESCAPE_NONE;
131: escape_target = 1;
132: escape_index = 0;
133: return;
134: }
135:
136: /* do newline & indent for backwards movement only when necessary */
137: if (need_nl) {
138: /* TOS cursor horizontal movement until host output */
139: switch (value) {
140: case ' ':
141: hpos_tos++;
142: return;
143: case '\b':
144: hpos_tos--;
145: return;
146: case '\t':
147: hpos_tos = (hpos_tos + 8) & 0xfff0;
148: return;
149: case '\r':
150: case '\n':
151: hpos_tos = 0;
152: break;
153: }
154: fputs("\n", stderr);
155: if (hpos_tos > 0 && hpos_tos < 80) {
156: fprintf(stderr, "%*s", hpos_tos, "");
157: hpos_host = hpos_tos;
158: } else {
159: hpos_host = 0;
160: }
161: need_nl = false;
162: }
163:
164: /* host cursor horizontal movement */
165: switch (value) {
166: case '\b':
167: hpos_host--;
168: break;
169: case '\t':
170: hpos_host = (hpos_host + 8) & 0xfff0;
171: break;
172: case '\r':
173: case '\n':
174: hpos_host = 0;
175: break;
176: default:
177: hpos_host++;
178: break;
179: }
180: map_character(value);
181: }
182:
183:
184: /**
185: * Catch requested xconout vector calls and show their output on console
186: */
187: void Console_Check(void)
188: {
189: Uint32 pc, xconout, stack, stackbeg, stackend;
190: int increment;
191: Uint16 chr;
192:
193: /* xconout vector for requested device? */
194: xconout = STMemory_ReadLong(0x57e + ConOutDevice * SIZE_LONG);
195: pc = M68000_GetPC();
196: if (pc != xconout) {
197: return;
198: }
199:
200: /* assumptions about xconout function:
201: * - c declaration: leftmost item on top of stackframe
202: * - args: WORD device, WORD character to output
203: * - can find the correct stackframe arguments by skipping
204: * wrong looking stack content from intermediate functions
205: * (bsr/jsr return addresses are > 0xff, local stack args
206: * could be an issue but hopefully don't match device number
207: * in any of the TOSes nor in MiNT or its conout devices)
208: */
209: stackbeg = stack = Regs[REG_A7];
210: stackend = stack + 16;
211: increment = SIZE_LONG;
212: while (STMemory_ReadWord(stack) != ConOutDevice) {
213: stack += increment;
214: if (stack > stackend) {
215: if (increment == SIZE_LONG) {
216: /* skipping return addresses not enough,
217: * try skipping potential local args too
218: */
219: fprintf(stderr, "WARNING: xconout stack args not found by skipping return addresses, trying short skipping.\n");
220: increment = SIZE_WORD;
221: stack = stackbeg;
222: continue;
223: }
224: /* failed */
225: fprintf(stderr, "WARNING: xconout args not found from stack.\n");
226: return;
227: }
228: }
229: chr = STMemory_ReadWord(stack + SIZE_WORD);
230: if (chr & 0xff00) {
231: /* allow 0xff high byte (sign extension?) */
232: if ((chr & 0xff00) != 0xff00) {
233: fprintf(stderr, "WARNING: xconout character has unknown high byte bits: 0x%x '%c'.\n", chr, chr&0xff);
234: /* higher bits, assume not correct arg */
235: return;
236: }
237: chr &= 0xff;
238: }
239: switch(ConOutDevice) {
240: case 2: /* EmuTOS/TOS/MiNT/etc console, VT-52 terminal */
241: vt52_emu(chr);
242: break;
243: case 0: /* Printer/Parallel port */
244: case 1: /* Aux device, the RS-232 port */
245: case 3: /* MIDI port */
246: case 4: /* Keyboard port */
247: case 5: /* Raw screen device (no escape sequence / control char processing) */
248: case 6: /* ST compatible RS-232 port (Modem 1) */
249: case 7: /* SCC channel B (Modem 2) */
250: map_character(chr);
251: break;
252: }
253: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.