]> Joshua Wise's Git repositories - netwatch.git/blob - lib/console.c
Have text.c take scrolling into account.
[netwatch.git] / lib / console.c
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 }
This page took 0.042271 seconds and 4 git commands to generate.