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