]>
Commit | Line | Data |
---|---|---|
1 | /** @file console.c | |
2 | * @brief A console driver. | |
3 | * | |
4 | * @author Joshua Wise (jwise) <joshua@joshuawise.com> | |
5 | * @bug None known | |
6 | */ | |
7 | ||
8 | #include <console.h> | |
9 | #include "console-ext.h" | |
10 | #include <io.h> | |
11 | ||
12 | #define POS_IS_VALID(row, col) ((row) >= 0 && (row) < CONSOLE_HEIGHT && (col) >= 0 && (col) < CONSOLE_WIDTH) | |
13 | ||
14 | /** @brief A structure describing a console that is backed by memory. | |
15 | * | |
16 | * In the mode of designing for extensibility, all of the console driver's | |
17 | * state is encapsulated in a 'struct console', which would ostensibly make | |
18 | * it easier to create virtual consoles later. All of the console driver's | |
19 | * workings touch this struct; ideally, there are no references to | |
20 | * CONSOLE_MEM_BASE or other hardware outside of this. In practice, this | |
21 | * isn't quite the case (see update_cursor()).... but we're close. | |
22 | */ | |
23 | struct console { | |
24 | int row; /**< The current row. */ | |
25 | int col; /**< The current column. */ | |
26 | unsigned char attr; /**< The current color/attribute. */ | |
27 | unsigned char *base; /**< The current base to write data to. | |
28 | * Might not be equal to physbase if we're | |
29 | * in a different virtual console right | |
30 | * now or we have requested a backbuffer! | |
31 | */ | |
32 | unsigned char *physbase; /**< The physical memory base for | |
33 | * this console. | |
34 | */ | |
35 | int showcursor; /**< Whether the cursor should be shown | |
36 | * by update_cursor(). | |
37 | */ | |
38 | int initialized; /**< Whether the console has been | |
39 | * initialized. Functions should check | |
40 | * this and call clear_console() before | |
41 | * running if this is zero. | |
42 | */ | |
43 | int backbuffer; /**< Whether we're currently writing to a | |
44 | * backbuffer instead of the physical | |
45 | * video memory. | |
46 | */ | |
47 | }; | |
48 | ||
49 | /** @brief The structure describing the one console on the system. Should | |
50 | * not be touched by anyone but the initializer for curcons! | |
51 | */ | |
52 | static struct console cons = { | |
53 | .row = 0, | |
54 | .col = 0, | |
55 | .attr = FGND_LGRAY, | |
56 | .base = (unsigned char *)CONSOLE_MEM_BASE, | |
57 | .physbase = (unsigned char *)CONSOLE_MEM_BASE, | |
58 | .showcursor = 1, | |
59 | .initialized = 0, | |
60 | .backbuffer = 0, | |
61 | }; | |
62 | ||
63 | /** @brief The current console that all console.c operations work on. */ | |
64 | static struct console *curcons = &cons; | |
65 | ||
66 | /** @brief Makes sure that the VGA cursor matches with the console.c's idea | |
67 | * of where the cursor should be. | |
68 | * | |
69 | * update_cursor would be trivial, but for a few important checks. In | |
70 | * particular, it won't touch the cursor at all if we're backbuffered | |
71 | * (since that would make the cursor fly around a screen that isn't | |
72 | * actually being updated), and if the cursor is hidden, it sets it to an | |
73 | * out-of-bounds segment to make sure that it's actually hidden. | |
74 | */ | |
75 | static void update_cursor() | |
76 | { | |
77 | if (curcons->backbuffer) | |
78 | return; | |
79 | if (curcons->showcursor) | |
80 | { | |
81 | unsigned short addr = (curcons->row * CONSOLE_WIDTH + curcons->col); | |
82 | outb(CRTC_IDX_REG, CRTC_CURSOR_MSB_IDX); | |
83 | outb(CRTC_DATA_REG, (addr >> 8)); | |
84 | outb(CRTC_IDX_REG, CRTC_CURSOR_LSB_IDX); | |
85 | outb(CRTC_DATA_REG, addr & 0xFF); | |
86 | } else { | |
87 | outb(CRTC_IDX_REG, CRTC_CURSOR_MSB_IDX); | |
88 | outb(CRTC_DATA_REG, 255 /* invalid */); | |
89 | outb(CRTC_IDX_REG, CRTC_CURSOR_LSB_IDX); | |
90 | outb(CRTC_DATA_REG, 255 /* invalid */); | |
91 | } | |
92 | } | |
93 | ||
94 | /** @brief Redirects console writes to a backbuffer. | |
95 | * | |
96 | * Verifies that the console is not already backbuffered. If it's not, it | |
97 | * allocates a backbuffer, copies the current console into the backbuffer, | |
98 | * and sets the backbuffered flag. | |
99 | * | |
100 | * This isn't just theoretical, by the way. The game screen's timer causes | |
101 | * it to repaint every frame (for lack of a better way to do it), which is | |
102 | * fine in qemu (which is fast), but causes severe flicker in simics. This | |
103 | * backbuffering logic seems to have alleviated the flicker. | |
104 | * | |
105 | * @see cons_debackbuffer | |
106 | */ | |
107 | /*void cons_backbuffer() | |
108 | { | |
109 | if (!curcons->initialized) | |
110 | clear_console(); | |
111 | if (curcons->backbuffer) | |
112 | return; | |
113 | curcons->base = malloc(CONSOLE_WIDTH * CONSOLE_HEIGHT * 2); | |
114 | memcpy(curcons->base, curcons->physbase, CONSOLE_WIDTH * CONSOLE_HEIGHT * 2); | |
115 | curcons->backbuffer = 1; | |
116 | }*/ | |
117 | ||
118 | /** @brief Turns off the backbuffer. | |
119 | * | |
120 | * Verifies that we are currently backbuffered. If so, copies the | |
121 | * backbuffer into video memory, frees the backbuffer, sets the pointer | |
122 | * back to video memory, clears the backbuffered flag, and updates the | |
123 | * hardware cursor. | |
124 | * | |
125 | * @see cons_backbuffer | |
126 | */ | |
127 | /*void cons_debackbuffer() | |
128 | { | |
129 | if (!curcons->initialized) | |
130 | clear_console(); | |
131 | if (!curcons->backbuffer) | |
132 | return; | |
133 | memcpy(curcons->physbase, curcons->base, CONSOLE_WIDTH * CONSOLE_HEIGHT * 2); | |
134 | free(curcons->base); | |
135 | curcons->base = curcons->physbase; | |
136 | curcons->backbuffer = 0; | |
137 | update_cursor(); | |
138 | }*/ | |
139 | ||
140 | int putbyte(char ch) | |
141 | { | |
142 | if (!curcons->initialized) | |
143 | clear_console(); | |
144 | ||
145 | /* Make sure to handle special cases nicely.*/ | |
146 | switch(ch) | |
147 | { | |
148 | case '\n': | |
149 | curcons->row++; | |
150 | if (curcons->row >= CONSOLE_HEIGHT) /* Moving off the end? Scroll. */ | |
151 | { | |
152 | int c; | |
153 | memmove(curcons->base, curcons->base + 2*CONSOLE_WIDTH, 2*CONSOLE_WIDTH*(CONSOLE_HEIGHT-1)); | |
154 | curcons->row--; | |
155 | for (c=0; c<CONSOLE_WIDTH; c++) /* Clear the newly blank bottom line. */ | |
156 | { | |
157 | curcons->base[(curcons->row * CONSOLE_WIDTH + c) * 2] = ' '; | |
158 | curcons->base[(curcons->row * CONSOLE_WIDTH + c) * 2 + 1] = curcons->attr; | |
159 | } | |
160 | } | |
161 | // fall through | |
162 | case '\r': | |
163 | curcons->col = 0; | |
164 | update_cursor(); | |
165 | break; | |
166 | case '\b': | |
167 | if (curcons->col) | |
168 | { | |
169 | curcons->col--; | |
170 | curcons->base[(curcons->row*CONSOLE_WIDTH + curcons->col) * 2] = ' '; | |
171 | } | |
172 | update_cursor(); | |
173 | break; | |
174 | default: | |
175 | curcons->base[(curcons->row*CONSOLE_WIDTH + curcons->col) * 2] = ch; | |
176 | curcons->base[(curcons->row*CONSOLE_WIDTH + curcons->col) * 2 + 1] = curcons->attr; | |
177 | curcons->col++; | |
178 | if (curcons->col >= CONSOLE_WIDTH) | |
179 | putbyte('\n'); | |
180 | update_cursor(); | |
181 | } | |
182 | return ch; | |
183 | } | |
184 | ||
185 | void putbytes(const char *s, int len) | |
186 | { | |
187 | if (!curcons->initialized) | |
188 | clear_console(); | |
189 | ||
190 | while (len--) | |
191 | putbyte(*(s++)); | |
192 | } | |
193 | ||
194 | int set_term_color(int color) | |
195 | { | |
196 | if (!curcons->initialized) | |
197 | clear_console(); | |
198 | ||
199 | curcons->attr = (unsigned char)color; | |
200 | return 0; | |
201 | } | |
202 | ||
203 | void get_term_color(int *color) | |
204 | { | |
205 | if (!curcons->initialized) | |
206 | clear_console(); | |
207 | ||
208 | *color = (int)curcons->attr; | |
209 | } | |
210 | ||
211 | int set_cursor(int row, int col) | |
212 | { | |
213 | if (!curcons->initialized) | |
214 | clear_console(); | |
215 | if (!POS_IS_VALID(row, col)) | |
216 | return -1; | |
217 | curcons->row = row; | |
218 | curcons->col = col; | |
219 | update_cursor(); | |
220 | return 0; | |
221 | } | |
222 | ||
223 | void get_cursor(int *row, int *col) | |
224 | { | |
225 | if (!curcons->initialized) | |
226 | clear_console(); | |
227 | *row = curcons->row; | |
228 | *col = curcons->col; | |
229 | } | |
230 | ||
231 | void hide_cursor() | |
232 | { | |
233 | if (!curcons->initialized) | |
234 | clear_console(); | |
235 | curcons->showcursor = 0; | |
236 | update_cursor(); | |
237 | } | |
238 | ||
239 | void show_cursor() | |
240 | { | |
241 | if (!curcons->initialized) | |
242 | clear_console(); | |
243 | curcons->showcursor = 1; | |
244 | update_cursor(); | |
245 | } | |
246 | ||
247 | void clear_console() | |
248 | { | |
249 | int i; | |
250 | curcons->initialized = 1; | |
251 | curcons->row = 0; | |
252 | curcons->col = 0; | |
253 | for (i = 0; i < CONSOLE_WIDTH * CONSOLE_HEIGHT; i++) | |
254 | { | |
255 | curcons->base[i*2] = ' '; | |
256 | curcons->base[i*2+1] = FGND_LGRAY; | |
257 | } | |
258 | update_cursor(); | |
259 | } | |
260 | ||
261 | void draw_char(int row, int col, int ch, int color) | |
262 | { | |
263 | if (!POS_IS_VALID(row, col)) | |
264 | return; | |
265 | curcons->base[2 * (CONSOLE_WIDTH * row + col)] = (unsigned char)ch; | |
266 | curcons->base[2 * (CONSOLE_WIDTH * row + col)+1] = (unsigned char)color; | |
267 | } | |
268 | ||
269 | char get_char(int row, int col) | |
270 | { | |
271 | if (!POS_IS_VALID(row, col)) | |
272 | return 0; | |
273 | return curcons->base[2 * (CONSOLE_WIDTH * row + col)]; | |
274 | } |