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