syslinux-3.08-2 sources from FC4
[bootcd.git] / syslinux / com32 / lib / sys / ansicon_write.c
1 #ident "$Id: ansicon_write.c,v 1.9 2005/01/05 07:45:23 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2004 H. Peter Anvin - All Rights Reserved
5  *
6  *   Permission is hereby granted, free of charge, to any person
7  *   obtaining a copy of this software and associated documentation
8  *   files (the "Software"), to deal in the Software without
9  *   restriction, including without limitation the rights to use,
10  *   copy, modify, merge, publish, distribute, sublicense, and/or
11  *   sell copies of the Software, and to permit persons to whom
12  *   the Software is furnished to do so, subject to the following
13  *   conditions:
14  *   
15  *   The above copyright notice and this permission notice shall
16  *   be included in all copies or substantial portions of the Software.
17  *   
18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * ----------------------------------------------------------------------- */
28
29 /*
30  * ansicon_write.c
31  *
32  * Write to the screen using ANSI control codes (about as capable as
33  * DOS' ANSI.SYS.)
34  */
35
36 #include <errno.h>
37 #include <string.h>
38 #include <com32.h>
39 #include <minmax.h>
40 #include <klibc/compiler.h>
41 #include "file.h"
42
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)
50
51 enum ansi_state {
52   st_init,                      /* Normal (no ESC seen) */
53   st_esc,                       /* ESC seen */
54   st_csi,                       /* CSI seen */
55 };
56
57 #define MAX_PARMS       16
58
59 struct term_state {
60   int disabled;
61   int attr;                     /* Current display attribute */
62   int vtgraphics;               /* VT graphics on/off */
63   int intensity;
64   int underline;
65   int blink;
66   int reverse;
67   int fg;
68   int bg;
69   int autocr;
70   struct curxy saved_xy;
71   uint16_t cursor_type;
72   enum ansi_state state;
73   int pvt;                      /* Private code? */
74   int nparms;                   /* Number of parameters seen */
75   int parms[MAX_PARMS];
76 };
77
78 static const struct term_state default_state =
79 {
80   .disabled = 0,
81   .attr = 0x07,                 /* Grey on black */
82   .vtgraphics = 0,
83   .intensity = 1,
84   .underline = 0,
85   .blink = 0,
86   .reverse = 0,
87   .fg = 7,
88   .bg = 0,
89   .autocr = 0,
90   .saved_xy = { 0, 0 },
91   .cursor_type = 0x0607,
92   .state = st_init,
93   .pvt = 0,
94   .nparms = 0,
95 };
96
97 static struct term_state st;
98
99 /* DEC VT graphics to codepage 437 table (characters 0x60-0x7F only) */
100 static const char decvt_to_cp437[] =
101   { 0004, 0261, 0007, 0007, 0007, 0007, 0370, 0361, 0007, 0007, 0331, 0277, 0332, 0300, 0305, 0304,
102     0304, 0304, 0137, 0137, 0303, 0264, 0301, 0302, 0263, 0363, 0362, 0343, 0330, 0234, 0007, 00 };
103
104 /* Common setup */
105 static void __constructor ansicon_init(void)
106 {
107   static com32sys_t ireg;       /* Auto-initalized to all zero */
108   com32sys_t oreg;
109
110   /* Initial state */
111   memcpy(&st, &default_state, sizeof st);
112
113   /* Are we disabled? */
114   ireg.eax.w[0] = 0x000b;
115   __intcall(0x22, &ireg, &oreg);
116
117   if ( (signed char)oreg.ebx.b[1] < 0 ) {
118     st.disabled = 1;
119     return;
120   }
121
122   /* Force text mode */
123   ireg.eax.w[0] = 0x0005;
124   __intcall(0x22, &ireg, NULL);
125
126   /* Get cursor shape */
127   ireg.eax.b[1] = 0x03;
128   ireg.ebx.b[1] = BIOS_PAGE;
129   __intcall(0x10, &ireg, &oreg);
130   st.cursor_type = oreg.ecx.w[0];
131 }
132
133 /* Erase a region of the screen */
134 static void ansicon_erase(int x0, int y0, int x1, int y1)
135 {
136   static com32sys_t ireg;
137   
138   ireg.eax.w[0] = 0x0600;       /* Clear window */
139   ireg.ebx.b[1] = st.attr;      /* Fill with current attribute */
140   ireg.ecx.b[0] = x0;
141   ireg.ecx.b[1] = y0;
142   ireg.edx.b[0] = x1;
143   ireg.edx.b[1] = y1;
144   __intcall(0x10, &ireg, NULL);
145 }
146
147 /* Show or hide the cursor */
148 static void showcursor(int yes)
149 {
150   static com32sys_t ireg;
151
152   ireg.eax.b[1] = 0x01;
153   ireg.ecx.w[0] = yes ? st.cursor_type : 0x2020;
154   __intcall(0x10, &ireg, NULL);
155 }
156
157 static void ansicon_putchar(int ch)
158 {
159   static com32sys_t ireg;
160   const int rows  = BIOS_ROWS ? BIOS_ROWS+1 : 25;
161   const int cols  = BIOS_COLS;
162   const int page  = BIOS_PAGE;
163   struct curxy xy = BIOS_CURXY[page];
164
165   switch ( st.state ) {
166   case st_init:
167     switch ( ch ) {
168     case '\b':
169       if ( xy.x > 0 ) xy.x--;
170       break;
171     case '\t':
172       {
173         int nsp = 8 - (xy.x & 7);
174         while ( nsp-- )
175           ansicon_putchar(' ');
176       }
177       return;                   /* Cursor already updated */
178     case '\n':
179     case '\v':
180     case '\f':
181       xy.y++;
182       if ( st.autocr )
183         xy.x = 0;
184       break;
185     case '\r':
186       xy.x = 0;
187       break;
188     case 127:
189       /* Ignore delete */
190       break;
191     case 14:
192       st.vtgraphics = 1;
193       break;
194     case 15:
195       st.vtgraphics = 0;
196       break;
197     case 27:
198       st.state = st_esc;
199       break;
200     default:
201       /* Print character */
202       if ( ch >= 32 ) {
203         if ( st.vtgraphics && (ch & 0xe0) == 0x60 )
204           ch = decvt_to_cp437[ch - 0x60];
205
206         ireg.eax.b[1] = 0x09;
207         ireg.eax.b[0] = ch;
208         ireg.ebx.b[1] = page;
209         ireg.ebx.b[0] = st.attr;
210         ireg.ecx.w[0] = 1;
211         __intcall(0x10, &ireg, NULL);
212         xy.x++;
213       }
214       break;
215     }
216     break;
217       
218   case st_esc:
219     switch ( ch ) {
220     case '%':
221     case '(':
222     case ')':
223     case '#':
224       /* Ignore this plus the subsequent character, allows
225          compatibility with Linux sequence to set charset */
226       break;
227     case '[':
228       st.state = st_csi;
229       st.nparms = st.pvt = 0;
230       memset(st.parms, 0, sizeof st.parms);
231       break;
232     case 'c':
233       /* Reset terminal */
234       memcpy(&st, &default_state, sizeof st);
235       ansicon_erase(0, 0, cols-1, rows-1);
236       xy.x = xy.y = 1;
237       break;
238     default:
239       /* Ignore sequence */
240       st.state = st_init;
241       break;
242     }
243     break;
244       
245   case st_csi:
246     {
247       int p0 = st.parms[0] ? st.parms[0] : 1;
248
249       if ( ch >= '0' && ch <= '9' ) {
250         st.parms[st.nparms] = st.parms[st.nparms]*10 + (ch-'0');
251       } else if ( ch == ';' ) {
252         st.nparms++;
253         if ( st.nparms >= MAX_PARMS )
254           st.nparms = MAX_PARMS-1;
255         break;
256       } else if ( ch == '?' ) {
257         st.pvt = 1;
258       } else {
259         switch ( ch ) {
260         case 'A':
261           {
262             int y = xy.y - p0;
263             xy.y = (y < 0) ? 0 : y;
264           }
265           break;
266         case 'B':
267           {
268             int y = xy.y + p0;
269             xy.y = (y >= rows) ? rows-1 : y;
270           }
271           break;
272         case 'C':
273           {
274             int x = xy.x + p0;
275             xy.x = (x >= cols) ? cols-1 : x;
276           }
277           break;
278         case 'D':
279           {
280             int x = xy.x - p0;
281             xy.x = (x < 0) ? 0 : x;
282           }
283           break;
284         case 'E':
285           {
286             int y = xy.y + p0;
287             xy.y = (y >= rows) ? rows-1 : y;
288             xy.x = 0;
289           }
290           break;
291         case 'F':
292           {
293             int y = xy.y - p0;
294             xy.y = (y < 0) ? 0 : y;
295             xy.x = 0;
296           }
297           break;
298         case 'G':
299         case '\'':
300           {
301             int x = st.parms[0] - 1;
302             xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x;
303           }
304           break;
305         case 'H':
306         case 'f':
307           {
308             int y = st.parms[0] - 1;
309             int x = st.parms[1] - 1;
310
311             xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x;
312             xy.y = (y >= rows) ? rows-1 : (y < 0) ? 0 : y;
313           }
314           break;
315         case 'J':
316           {
317             switch ( st.parms[0] ) {
318             case 0:
319               ansicon_erase(xy.x, xy.y, cols-1, xy.y);
320               if ( xy.y < rows-1 )
321                 ansicon_erase(0, xy.y+1, cols-1, rows-1);
322               break;
323
324             case 1:
325               if ( xy.y > 0 )
326                 ansicon_erase(0, 0, cols-1, xy.y-1);
327               if ( xy.y > 0 )
328                 ansicon_erase(0, xy.y, xy.x-1, xy.y);
329               break;
330
331             case 2:
332               ansicon_erase(0, 0, cols-1, rows-1);
333               break;
334
335             default:
336               /* Ignore */
337               break;
338             }
339           }
340           break;
341         case 'K':
342           {
343             switch ( st.parms[0] ) {
344             case 0:
345               ansicon_erase(xy.x, xy.y, cols-1, xy.y);
346               break;
347
348             case 1:
349               if ( xy.x > 0 )
350                 ansicon_erase(0, xy.y, xy.x-1, xy.y);
351               break;
352
353             case 2:
354               ansicon_erase(0, xy.y, cols-1, xy.y);
355               break;
356           
357             default:
358               /* Ignore */
359               break;
360             }
361           }
362           break;
363         case 'h':
364         case 'l':
365           {
366             int set = (ch == 'h');
367             switch ( st.parms[0] ) {
368             case 20:
369               st.autocr = set;
370               break;
371             case 25:
372               showcursor(set);
373               break;
374             default:
375               /* Ignore */
376               break;
377             }
378           }
379           break;
380         case 'm':
381           {
382             static const int ansi2pc[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
383
384             int i;
385             for ( i = 0 ; i <= st.nparms ; i++ ) {
386               int a = st.parms[i];
387               switch ( a ) {
388               case 0:
389                 st.fg = 7;
390                 st.bg = 0;
391                 st.intensity = 1;
392                 st.underline = 0;
393                 st.blink = 0;
394                 st.reverse = 0;
395                 break;
396               case 1:
397                 st.intensity = 2;
398                 break;
399               case 2:
400                 st.intensity = 0;
401                 break;
402               case 4:
403                 st.underline = 1;
404                 break;
405               case 5:
406                 st.blink = 1;
407                 break;
408               case 7:
409                 st.reverse = 1;
410                 break;
411               case 21:
412               case 22:
413                 st.intensity = 1;
414                 break;
415               case 24:
416                 st.underline = 0;
417                 break;
418               case 25:
419                 st.blink = 0;
420                 break;
421               case 27:
422                 st.reverse = 0;
423                 break;
424               case 30 ... 37:
425                 st.fg = ansi2pc[a-30];
426                 break;
427               case 38:
428                 st.fg = 7;
429                 st.underline = 1;
430                 break;
431               case 39:
432                 st.fg = 7;
433                 st.underline = 0;
434                 break;
435               case 40 ... 47:
436                 st.bg = ansi2pc[a-40];
437                 break;
438               case 49:
439                 st.bg = 7;
440                 break;
441               default:
442                 /* Do nothing */
443                 break;
444               }
445             }
446           
447             /* Turn into an attribute code */
448             {
449               int bg = st.bg;
450               int fg;
451
452               if ( st.underline )
453                 fg = 0x01;
454               else if ( st.intensity == 0 )
455                 fg = 0x08;
456               else
457                 fg = st.fg;
458
459               if ( st.reverse ) {
460                 bg = fg & 0x07;
461                 fg &= 0x08;
462                 fg |= st.bg;
463               }
464
465               if ( st.blink )
466                 bg ^= 0x08;
467
468               if ( st.intensity == 2 )
469                 fg ^= 0x08;
470
471               st.attr = (bg << 4) | fg;
472             }
473           }
474           break;
475         case 's':
476           st.saved_xy = xy;
477           break;
478         case 'u':
479           xy = st.saved_xy;
480           break;
481         default:                /* Includes CAN and SUB */
482           break;                /* Drop unknown sequence */
483         }
484         st.state = st_init;
485       }
486     }
487     break;
488   }
489
490   /* If we fell off the end of the screen, adjust */
491   if ( xy.x >= cols ) {
492     xy.x = 0;
493     xy.y++;
494   }
495   while ( xy.y >= rows ) {
496     xy.y--;
497     ireg.eax.w[0] = 0x0601;
498     ireg.ebx.b[1] = st.attr;
499     ireg.ecx.w[0] = 0;
500     ireg.edx.b[1] = rows-1;
501     ireg.edx.b[0] = cols-1;
502     __intcall(0x10, &ireg, NULL); /* Scroll */
503   }
504
505   /* Update cursor position */
506   ireg.eax.b[1] = 0x02;
507   ireg.ebx.b[1] = page;
508   ireg.edx.b[1] = xy.y;
509   ireg.edx.b[0] = xy.x;
510   __intcall(0x10, &ireg, NULL);
511 }       
512       
513
514 ssize_t __ansicon_write(struct file_info *fp, const void *buf, size_t count)
515 {
516   const unsigned char *bufp = buf;
517   size_t n = 0;
518
519   (void)fp;
520
521   if ( st.disabled )
522     return n;                   /* Nothing to do */
523
524   while ( count-- ) {
525     ansicon_putchar(*bufp++);
526     n++;
527   }
528
529   return n;
530 }
531
532 const struct output_dev dev_ansicon_w = {
533   .dev_magic  = __DEV_MAGIC,
534   .flags      = __DEV_TTY | __DEV_OUTPUT,
535   .fileflags  = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
536   .write      = __ansicon_write,
537   .close      = NULL,
538 };