Initial code release
[external/syslinux.git] / com32 / lib / sys / ansicon_write.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
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 <colortbl.h>
41 #include <klibc/compiler.h>
42 #include <syslinux/config.h>
43 #include "file.h"
44 #include "ansi.h"
45
46 static void ansicon_erase(const struct term_state *, int, int, int, int);
47 static void ansicon_write_char(int, int, uint8_t, const struct term_state *);
48 static void ansicon_showcursor(const struct term_state *);
49 static void ansicon_scroll_up(const struct term_state *);
50 static void ansicon_set_cursor(int, int, bool);
51
52 static struct term_state ts;
53 struct ansi_ops __ansicon_ops = {
54     .erase = ansicon_erase,
55     .write_char = ansicon_write_char,
56     .showcursor = ansicon_showcursor,
57     .set_cursor = ansicon_set_cursor,
58     .scroll_up = ansicon_scroll_up,
59     .beep = __ansicon_beep,
60 };
61
62 static struct term_info ti = {
63     .disabled = 0,
64     .ts = &ts,
65     .op = &__ansicon_ops
66 };
67
68 #define BIOS_CURXY ((struct curxy *)0x450)      /* Array for each page */
69 #define BIOS_ROWS (*(uint8_t *)0x484)   /* Minus one; if zero use 24 (= 25 lines) */
70 #define BIOS_COLS (*(uint16_t *)0x44A)
71 #define BIOS_PAGE (*(uint8_t *)0x462)
72
73 /* Reference counter to the screen, to keep track of if we need
74    reinitialization. */
75 static int ansicon_counter = 0;
76
77 static uint16_t cursor_type;    /* Saved cursor pattern */
78
79 /* Common setup */
80 int __ansicon_open(struct file_info *fp)
81 {
82     static com32sys_t ireg;     /* Auto-initalized to all zero */
83     com32sys_t oreg;
84
85     if (!ansicon_counter) {
86         /* Are we disabled? */
87         if (syslinux_serial_console_info()->flowctl & 0x8000) {
88             ti.disabled = 1;
89             ti.rows = 25;
90             ti.cols = 80;
91         } else {
92             /* Force text mode */
93             ireg.eax.w[0] = 0x0005;
94             __intcall(0x22, &ireg, NULL);
95
96             /* Initial state */
97             ti.rows = BIOS_ROWS ? BIOS_ROWS + 1 : 25;
98             ti.cols = BIOS_COLS;
99             __ansi_init(&ti);
100
101             /* Get cursor shape and position */
102             ireg.eax.b[1] = 0x03;
103             ireg.ebx.b[1] = BIOS_PAGE;
104             __intcall(0x10, &ireg, &oreg);
105             cursor_type = oreg.ecx.w[0];
106             ti.ts->xy.x = oreg.edx.b[0];
107             ti.ts->xy.y = oreg.edx.b[1];
108         }
109     }
110
111     fp->o.rows = ti.rows;
112     fp->o.cols = ti.cols;
113
114     ansicon_counter++;
115     return 0;
116 }
117
118 int __ansicon_close(struct file_info *fp)
119 {
120     (void)fp;
121
122     ansicon_counter--;
123     return 0;
124 }
125
126 /* Turn ANSI attributes into VGA attributes */
127 static uint8_t ansicon_attribute(const struct term_state *st)
128 {
129     int bg = st->bg;
130     int fg;
131
132     if (st->underline)
133         fg = 0x01;
134     else if (st->intensity == 0)
135         fg = 0x08;
136     else
137         fg = st->fg;
138
139     if (st->reverse) {
140         bg = fg & 0x07;
141         fg &= 0x08;
142         fg |= st->bg;
143     }
144
145     if (st->blink)
146         bg ^= 0x08;
147
148     if (st->intensity == 2)
149         fg ^= 0x08;
150
151     return (bg << 4) | fg;
152 }
153
154 /* Erase a region of the screen */
155 static void ansicon_erase(const struct term_state *st,
156                           int x0, int y0, int x1, int y1)
157 {
158     static com32sys_t ireg;
159
160     ireg.eax.w[0] = 0x0600;     /* Clear window */
161     ireg.ebx.b[1] = ansicon_attribute(st);
162     ireg.ecx.b[0] = x0;
163     ireg.ecx.b[1] = y0;
164     ireg.edx.b[0] = x1;
165     ireg.edx.b[1] = y1;
166     __intcall(0x10, &ireg, NULL);
167 }
168
169 /* Show or hide the cursor */
170 static void ansicon_showcursor(const struct term_state *st)
171 {
172     static com32sys_t ireg;
173
174     ireg.eax.b[1] = 0x01;
175     ireg.ecx.w[0] = st->cursor ? cursor_type : 0x2020;
176     __intcall(0x10, &ireg, NULL);
177 }
178
179 static void ansicon_set_cursor(int x, int y, bool visible)
180 {
181     const int page = BIOS_PAGE;
182     struct curxy xy = BIOS_CURXY[page];
183     static com32sys_t ireg;
184
185     (void)visible;
186
187     if (xy.x != x || xy.y != y) {
188         ireg.eax.b[1] = 0x02;
189         ireg.ebx.b[1] = page;
190         ireg.edx.b[1] = y;
191         ireg.edx.b[0] = x;
192         __intcall(0x10, &ireg, NULL);
193     }
194 }
195
196 static void ansicon_write_char(int x, int y, uint8_t ch,
197                                const struct term_state *st)
198 {
199     static com32sys_t ireg;
200
201     ansicon_set_cursor(x, y, false);
202
203     ireg.eax.b[1] = 0x09;
204     ireg.eax.b[0] = ch;
205     ireg.ebx.b[1] = BIOS_PAGE;
206     ireg.ebx.b[0] = ansicon_attribute(st);
207     ireg.ecx.w[0] = 1;
208     __intcall(0x10, &ireg, NULL);
209 }
210
211 static void ansicon_scroll_up(const struct term_state *st)
212 {
213     static com32sys_t ireg;
214
215     ireg.eax.w[0] = 0x0601;
216     ireg.ebx.b[1] = ansicon_attribute(st);
217     ireg.ecx.w[0] = 0;
218     ireg.edx.b[1] = ti.rows - 1;
219     ireg.edx.b[0] = ti.cols - 1;
220     __intcall(0x10, &ireg, NULL);       /* Scroll */
221 }
222
223 ssize_t __ansicon_write(struct file_info *fp, const void *buf, size_t count)
224 {
225     const unsigned char *bufp = buf;
226     size_t n = 0;
227
228     (void)fp;
229
230     if (ti.disabled)
231         return count;           /* Nothing to do */
232
233     while (count--) {
234         __ansi_putchar(&ti, *bufp++);
235         n++;
236     }
237
238     return n;
239 }
240
241 void __ansicon_beep(void)
242 {
243     static com32sys_t ireg;
244
245     ireg.eax.w[0] = 0x0e07;
246     ireg.ebx.b[1] = BIOS_PAGE;
247     __intcall(0x10, &ireg, NULL);
248 }
249
250 const struct output_dev dev_ansicon_w = {
251     .dev_magic = __DEV_MAGIC,
252     .flags = __DEV_TTY | __DEV_OUTPUT,
253     .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
254     .write = __ansicon_write,
255     .close = __ansicon_close,
256     .open = __ansicon_open,
257 };