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