Adding upstream version 3.30~pre4.
[syslinux-debian/hramrach.git] / com32 / lib / sys / ansicon_write.c
blob594a01fa303cffb80db6aab63073717ab6c04897
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004 H. Peter Anvin - All Rights Reserved
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * ----------------------------------------------------------------------- */
29 * ansicon_write.c
31 * Write to the screen using ANSI control codes (about as capable as
32 * DOS' ANSI.SYS.)
35 #include <errno.h>
36 #include <string.h>
37 #include <com32.h>
38 #include <minmax.h>
39 #include <colortbl.h>
40 #include <klibc/compiler.h>
41 #include "file.h"
43 struct curxy {
44 uint8_t x, y;
45 } __attribute__((packed));
46 #define BIOS_CURXY ((struct curxy *)0x450) /* Array for each page */
47 #define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */
48 #define BIOS_COLS (*(uint16_t *)0x44A)
49 #define BIOS_PAGE (*(uint8_t *)0x462)
51 enum ansi_state {
52 st_init, /* Normal (no ESC seen) */
53 st_esc, /* ESC seen */
54 st_csi, /* CSI seen */
55 st_soh, /* SOH seen */
56 st_sohc, /* SOH # seen */
57 st_sohc1, /* SOH # digit seen */
60 #define MAX_PARMS 16
62 struct term_state {
63 int disabled;
64 int attr; /* Current display attribute */
65 int vtgraphics; /* VT graphics on/off */
66 int intensity;
67 int underline;
68 int blink;
69 int reverse;
70 int fg;
71 int bg;
72 int autocr;
73 struct curxy saved_xy;
74 uint16_t cursor_type;
75 enum ansi_state state;
76 int pvt; /* Private code? */
77 int nparms; /* Number of parameters seen */
78 int parms[MAX_PARMS];
81 static const struct term_state default_state =
83 .disabled = 0,
84 .attr = 0x07, /* Grey on black */
85 .vtgraphics = 0,
86 .intensity = 1,
87 .underline = 0,
88 .blink = 0,
89 .reverse = 0,
90 .fg = 7,
91 .bg = 0,
92 .autocr = 0,
93 .saved_xy = { 0, 0 },
94 .cursor_type = 0x0607,
95 .state = st_init,
96 .pvt = 0,
97 .nparms = 0,
100 static struct term_state st;
102 /* DEC VT graphics to codepage 437 table (characters 0x60-0x7F only) */
103 static const char decvt_to_cp437[] =
104 { 0004, 0261, 0007, 0007, 0007, 0007, 0370, 0361, 0007, 0007, 0331, 0277, 0332, 0300, 0305, 0304,
105 0304, 0304, 0137, 0137, 0303, 0264, 0301, 0302, 0263, 0363, 0362, 0343, 0330, 0234, 0007, 00 };
107 /* Reference counter to the screen, to keep track of if we need reinitialization. */
108 static int ansicon_counter = 0;
110 /* Common setup */
111 int __ansicon_open(struct file_info *fp)
113 static com32sys_t ireg; /* Auto-initalized to all zero */
114 com32sys_t oreg;
116 (void)fp;
118 if (!ansicon_counter) {
119 /* Initial state */
120 memcpy(&st, &default_state, sizeof st);
122 /* Are we disabled? */
123 ireg.eax.w[0] = 0x000b;
124 __intcall(0x22, &ireg, &oreg);
126 if ( (signed char)oreg.ebx.b[1] < 0 ) {
127 st.disabled = 1;
128 } else {
129 /* Force text mode */
130 ireg.eax.w[0] = 0x0005;
131 __intcall(0x22, &ireg, NULL);
133 /* Get cursor shape */
134 ireg.eax.b[1] = 0x03;
135 ireg.ebx.b[1] = BIOS_PAGE;
136 __intcall(0x10, &ireg, &oreg);
137 st.cursor_type = oreg.ecx.w[0];
141 ansicon_counter++;
142 return 0;
145 int __ansicon_close(struct file_info *fp)
147 (void)fp;
149 ansicon_counter--;
150 return 0;
153 /* Erase a region of the screen */
154 static void ansicon_erase(int x0, int y0, int x1, int y1)
156 static com32sys_t ireg;
158 ireg.eax.w[0] = 0x0600; /* Clear window */
159 ireg.ebx.b[1] = st.attr; /* Fill with current attribute */
160 ireg.ecx.b[0] = x0;
161 ireg.ecx.b[1] = y0;
162 ireg.edx.b[0] = x1;
163 ireg.edx.b[1] = y1;
164 __intcall(0x10, &ireg, NULL);
167 /* Show or hide the cursor */
168 static void showcursor(int yes)
170 static com32sys_t ireg;
172 ireg.eax.b[1] = 0x01;
173 ireg.ecx.w[0] = yes ? st.cursor_type : 0x2020;
174 __intcall(0x10, &ireg, NULL);
177 static void ansicon_putchar(int ch)
179 static com32sys_t ireg;
180 const int rows = BIOS_ROWS ? BIOS_ROWS+1 : 25;
181 const int cols = BIOS_COLS;
182 const int page = BIOS_PAGE;
183 struct curxy xy = BIOS_CURXY[page];
185 switch ( st.state ) {
186 case st_init:
187 switch ( ch ) {
188 case 1:
189 st.state = st_soh;
190 break;
191 case '\b':
192 if ( xy.x > 0 ) xy.x--;
193 break;
194 case '\t':
196 int nsp = 8 - (xy.x & 7);
197 while ( nsp-- )
198 ansicon_putchar(' ');
200 return; /* Cursor already updated */
201 case '\n':
202 case '\v':
203 case '\f':
204 xy.y++;
205 if ( st.autocr )
206 xy.x = 0;
207 break;
208 case '\r':
209 xy.x = 0;
210 break;
211 case 127:
212 /* Ignore delete */
213 break;
214 case 14:
215 st.vtgraphics = 1;
216 break;
217 case 15:
218 st.vtgraphics = 0;
219 break;
220 case 27:
221 st.state = st_esc;
222 break;
223 default:
224 /* Print character */
225 if ( ch >= 32 ) {
226 if ( st.vtgraphics && (ch & 0xe0) == 0x60 )
227 ch = decvt_to_cp437[ch - 0x60];
229 ireg.eax.b[1] = 0x09;
230 ireg.eax.b[0] = ch;
231 ireg.ebx.b[1] = page;
232 ireg.ebx.b[0] = st.attr;
233 ireg.ecx.w[0] = 1;
234 __intcall(0x10, &ireg, NULL);
235 xy.x++;
237 break;
239 break;
241 case st_esc:
242 switch ( ch ) {
243 case '%':
244 case '(':
245 case ')':
246 case '#':
247 /* Ignore this plus the subsequent character, allows
248 compatibility with Linux sequence to set charset */
249 break;
250 case '[':
251 st.state = st_csi;
252 st.nparms = st.pvt = 0;
253 memset(st.parms, 0, sizeof st.parms);
254 break;
255 case 'c':
256 /* Reset terminal */
257 memcpy(&st, &default_state, sizeof st);
258 ansicon_erase(0, 0, cols-1, rows-1);
259 xy.x = xy.y = 1;
260 break;
261 default:
262 /* Ignore sequence */
263 st.state = st_init;
264 break;
266 break;
268 case st_csi:
270 int p0 = st.parms[0] ? st.parms[0] : 1;
272 if ( ch >= '0' && ch <= '9' ) {
273 st.parms[st.nparms] = st.parms[st.nparms]*10 + (ch-'0');
274 } else if ( ch == ';' ) {
275 st.nparms++;
276 if ( st.nparms >= MAX_PARMS )
277 st.nparms = MAX_PARMS-1;
278 break;
279 } else if ( ch == '?' ) {
280 st.pvt = 1;
281 } else {
282 switch ( ch ) {
283 case 'A':
285 int y = xy.y - p0;
286 xy.y = (y < 0) ? 0 : y;
288 break;
289 case 'B':
291 int y = xy.y + p0;
292 xy.y = (y >= rows) ? rows-1 : y;
294 break;
295 case 'C':
297 int x = xy.x + p0;
298 xy.x = (x >= cols) ? cols-1 : x;
300 break;
301 case 'D':
303 int x = xy.x - p0;
304 xy.x = (x < 0) ? 0 : x;
306 break;
307 case 'E':
309 int y = xy.y + p0;
310 xy.y = (y >= rows) ? rows-1 : y;
311 xy.x = 0;
313 break;
314 case 'F':
316 int y = xy.y - p0;
317 xy.y = (y < 0) ? 0 : y;
318 xy.x = 0;
320 break;
321 case 'G':
322 case '\'':
324 int x = st.parms[0] - 1;
325 xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x;
327 break;
328 case 'H':
329 case 'f':
331 int y = st.parms[0] - 1;
332 int x = st.parms[1] - 1;
334 xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x;
335 xy.y = (y >= rows) ? rows-1 : (y < 0) ? 0 : y;
337 break;
338 case 'J':
340 switch ( st.parms[0] ) {
341 case 0:
342 ansicon_erase(xy.x, xy.y, cols-1, xy.y);
343 if ( xy.y < rows-1 )
344 ansicon_erase(0, xy.y+1, cols-1, rows-1);
345 break;
347 case 1:
348 if ( xy.y > 0 )
349 ansicon_erase(0, 0, cols-1, xy.y-1);
350 if ( xy.y > 0 )
351 ansicon_erase(0, xy.y, xy.x-1, xy.y);
352 break;
354 case 2:
355 ansicon_erase(0, 0, cols-1, rows-1);
356 break;
358 default:
359 /* Ignore */
360 break;
363 break;
364 case 'K':
366 switch ( st.parms[0] ) {
367 case 0:
368 ansicon_erase(xy.x, xy.y, cols-1, xy.y);
369 break;
371 case 1:
372 if ( xy.x > 0 )
373 ansicon_erase(0, xy.y, xy.x-1, xy.y);
374 break;
376 case 2:
377 ansicon_erase(0, xy.y, cols-1, xy.y);
378 break;
380 default:
381 /* Ignore */
382 break;
385 break;
386 case 'h':
387 case 'l':
389 int set = (ch == 'h');
390 switch ( st.parms[0] ) {
391 case 20:
392 st.autocr = set;
393 break;
394 case 25:
395 showcursor(set);
396 break;
397 default:
398 /* Ignore */
399 break;
402 break;
403 case 'm':
405 static const int ansi2pc[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
407 int i;
408 for ( i = 0 ; i <= st.nparms ; i++ ) {
409 int a = st.parms[i];
410 switch ( a ) {
411 case 0:
412 st.fg = 7;
413 st.bg = 0;
414 st.intensity = 1;
415 st.underline = 0;
416 st.blink = 0;
417 st.reverse = 0;
418 break;
419 case 1:
420 st.intensity = 2;
421 break;
422 case 2:
423 st.intensity = 0;
424 break;
425 case 4:
426 st.underline = 1;
427 break;
428 case 5:
429 st.blink = 1;
430 break;
431 case 7:
432 st.reverse = 1;
433 break;
434 case 21:
435 case 22:
436 st.intensity = 1;
437 break;
438 case 24:
439 st.underline = 0;
440 break;
441 case 25:
442 st.blink = 0;
443 break;
444 case 27:
445 st.reverse = 0;
446 break;
447 case 30 ... 37:
448 st.fg = ansi2pc[a-30];
449 break;
450 case 38:
451 st.fg = 7;
452 st.underline = 1;
453 break;
454 case 39:
455 st.fg = 7;
456 st.underline = 0;
457 break;
458 case 40 ... 47:
459 st.bg = ansi2pc[a-40];
460 break;
461 case 49:
462 st.bg = 7;
463 break;
464 default:
465 /* Do nothing */
466 break;
470 /* Turn into an attribute code */
472 int bg = st.bg;
473 int fg;
475 if ( st.underline )
476 fg = 0x01;
477 else if ( st.intensity == 0 )
478 fg = 0x08;
479 else
480 fg = st.fg;
482 if ( st.reverse ) {
483 bg = fg & 0x07;
484 fg &= 0x08;
485 fg |= st.bg;
488 if ( st.blink )
489 bg ^= 0x08;
491 if ( st.intensity == 2 )
492 fg ^= 0x08;
494 st.attr = (bg << 4) | fg;
497 break;
498 case 's':
499 st.saved_xy = xy;
500 break;
501 case 'u':
502 xy = st.saved_xy;
503 break;
504 default: /* Includes CAN and SUB */
505 break; /* Drop unknown sequence */
507 st.state = st_init;
510 break;
512 case st_soh:
513 if ( ch == '#' )
514 st.state = st_sohc;
515 else
516 st.state = st_init;
517 break;
519 case st_sohc:
521 int n = (unsigned char)ch - '0';
522 if (n < 10) {
523 st.parms[0] = n*10;
524 st.state = st_sohc1;
525 } else {
526 st.state = st_init;
529 break;
531 case st_sohc1:
533 int n = (unsigned char)ch - '0';
534 const char *p;
536 if (n < 10) {
537 st.parms[0] += n;
538 /* Emulate the appropriate CSI m sequence */
539 if (st.parms[0] < console_color_table_size) {
540 st.state = st_csi;
541 for (p = console_color_table[st.parms[0]].ansi; *p; p++)
542 ansicon_putchar(*p);
543 ansicon_putchar('m');
547 st.state = st_init;
549 break;
552 /* If we fell off the end of the screen, adjust */
553 if ( xy.x >= cols ) {
554 xy.x = 0;
555 xy.y++;
557 while ( xy.y >= rows ) {
558 xy.y--;
559 ireg.eax.w[0] = 0x0601;
560 ireg.ebx.b[1] = st.attr;
561 ireg.ecx.w[0] = 0;
562 ireg.edx.b[1] = rows-1;
563 ireg.edx.b[0] = cols-1;
564 __intcall(0x10, &ireg, NULL); /* Scroll */
567 /* Update cursor position */
568 ireg.eax.b[1] = 0x02;
569 ireg.ebx.b[1] = page;
570 ireg.edx.b[1] = xy.y;
571 ireg.edx.b[0] = xy.x;
572 __intcall(0x10, &ireg, NULL);
576 ssize_t __ansicon_write(struct file_info *fp, const void *buf, size_t count)
578 const unsigned char *bufp = buf;
579 size_t n = 0;
581 (void)fp;
583 if ( st.disabled )
584 return n; /* Nothing to do */
586 while ( count-- ) {
587 ansicon_putchar(*bufp++);
588 n++;
591 return n;
594 const struct output_dev dev_ansicon_w = {
595 .dev_magic = __DEV_MAGIC,
596 .flags = __DEV_TTY | __DEV_OUTPUT,
597 .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
598 .write = __ansicon_write,
599 .close = __ansicon_close,
600 .open = __ansicon_open,