MonkOS  v0.1
A simple 64-bit operating system (x86_64)
tty.c
Go to the documentation of this file.
1 //============================================================================
2 /// @file tty.c
3 /// @brief Teletype (console) screen text manipulation routines.
4 //
5 // Copyright 2016 Brett Vickers.
6 // Use of this source code is governed by a BSD-style license that can
7 // be found in the MonkOS LICENSE file.
8 //============================================================================
9 
10 #include <core.h>
11 #include <libc/stdio.h>
12 #include <libc/string.h>
13 #include <kernel/x86/cpu.h>
14 #include <kernel/device/tty.h>
15 
16 // CRTC ports
17 #define CRTC_PORT_CMD 0x03d4 ///< Command port for CRT controller.
18 #define CRTC_PORT_DATA 0x03d5 ///< Data port for CRT controller.
19 
20 // CRTC commands
21 #define CRTC_CMD_STARTADDR_HI 0x0c ///< Hi-byte of buffer start address.
22 #define CRTC_CMD_STARTADDR_LO 0x0d ///< Lo-byte of buffer start address.
23 #define CRTC_CMD_CURSORADDR_HI 0x0e ///< Hi-byte of cursor start address.
24 #define CRTC_CMD_CURSORADDR_LO 0x0f ///< Lo-byte of cursor start address.
25 
26 // Visible screen geometry
27 #define SCREEN_ROWS 25
28 #define SCREEN_COLS 80
29 #define SCREEN_SIZE (SCREEN_ROWS * SCREEN_COLS)
30 #define SCREEN_BUFFER 0x000b8000
31 
32 /// Virtual console state.
33 struct tty
34 {
35  uint16_t textcolor; ///< Current fg/bg color (shifted).
36  uint16_t textcolor_orig; ///< Original, non-override text color.
37  screenpos_t pos; ///< Current screen position.
38  uint8_t ybuf; ///< Virtual buffer y position.
39  uint16_t *screen; ///< Virtual screen buffer for 50 rows.
40  uint16_t *tlcorner; ///< Points to char in top-left corner.
41 };
42 
43 typedef struct tty tty_t;
44 
45 static tty_t tty[MAX_TTYS]; ///< All virtual consoles.
46 static tty_t *active_tty; ///< The currently visible console.
47 
48 static inline uint16_t
50 {
51  return (uint16_t)bg << 12 | (uint16_t)fg << 8;
52 }
53 
54 static void
56 {
57  // Calculate top-left corner offset from the start of the first screen
58  // buffer.
59  int offset = (int)(active_tty->tlcorner - (uint16_t *)SCREEN_BUFFER);
60 
61  uint8_t save = io_inb(CRTC_PORT_CMD);
62 
64  io_outb(CRTC_PORT_DATA, (uint8_t)offset);
66  io_outb(CRTC_PORT_DATA, (uint8_t)(offset >> 8));
67 
68  io_outb(CRTC_PORT_CMD, save);
69 }
70 
71 static void
73 {
74  // Calculate cursor offset from the start of the first screen buffer.
75  int offset = active_tty->ybuf * SCREEN_COLS + active_tty->pos.x +
76  (int)(active_tty->screen - (uint16_t *)SCREEN_BUFFER);
77 
78  uint8_t save = io_inb(CRTC_PORT_CMD);
79 
81  io_outb(CRTC_PORT_DATA, (uint8_t)offset);
83  io_outb(CRTC_PORT_DATA, (uint8_t)(offset >> 8));
84 
85  io_outb(CRTC_PORT_CMD, save);
86 }
87 
88 void
90 {
91  uint16_t *screenptr = (uint16_t *)SCREEN_BUFFER;
92 
93  for (int id = 0; id < MAX_TTYS; id++) {
95  tty[id].textcolor_orig = tty[id].textcolor;
96  tty[id].pos.x = 0;
97  tty[id].pos.y = 0;
98  tty[id].ybuf = 0;
99  tty[id].screen = screenptr;
100  tty[id].tlcorner = screenptr;
101  screenptr += 0x1000; // each screen is 4K words.
102  }
103  active_tty = &tty[0];
104 }
105 
106 void
108 {
109  if ((id < 0) || (id >= MAX_TTYS)) {
110  id = 0;
111  }
112  if (&tty[id] == active_tty) {
113  return;
114  }
115 
116  active_tty = &tty[id];
118  update_cursor();
119 }
120 
121 void
123 {
124  if ((id < 0) || (id >= MAX_TTYS)) {
125  id = 0;
126  }
127 
128  tty[id].textcolor = tty[id].textcolor_orig = color(fg, bg);
129 }
130 
131 void
133 {
134  if ((id < 0) || (id >= MAX_TTYS)) {
135  id = 0;
136  }
137 
138  tty[id].textcolor = color(fg, tty_get_textcolor_bg(id));
139  tty[id].textcolor_orig = tty[id].textcolor;
140 }
141 
142 void
144 {
145  if ((id < 0) || (id >= MAX_TTYS)) {
146  id = 0;
147  }
148 
149  tty[id].textcolor = color(tty_get_textcolor_fg(id), bg);
150  tty[id].textcolor_orig = tty[id].textcolor;
151 }
152 
155 {
156  if ((id < 0) || (id >= MAX_TTYS)) {
157  id = 0;
158  }
159 
160  return (textcolor_t)((tty[id].textcolor_orig >> 8) & 0x0f);
161 }
162 
165 {
166  if ((id < 0) || (id >= MAX_TTYS)) {
167  id = 0;
168  }
169 
170  return (textcolor_t)((tty[id].textcolor_orig >> 12) & 0x0f);
171 }
172 
173 void
174 tty_clear(int id)
175 {
176  if ((id < 0) || (id >= MAX_TTYS)) {
177  id = 0;
178  }
179 
180  memsetw(tty[id].screen,
181  tty[id].textcolor | ' ', SCREEN_SIZE * 2);
182  tty[id].pos.x = 0;
183  tty[id].pos.y = 0;
184  tty[id].ybuf = 0;
185  tty[id].tlcorner = tty[id].screen;
186 
187  if (active_tty == &tty[id]) {
189  update_cursor();
190  }
191 }
192 
193 void
194 tty_setpos(int id, screenpos_t pos)
195 {
196  if ((id < 0) || (id >= MAX_TTYS)) {
197  id = 0;
198  }
199 
200  int diff = (int)pos.y - (int)tty[id].pos.y;
201  tty[id].pos = pos;
202  tty[id].ybuf = (uint8_t)((int)tty[id].ybuf + diff);
203  if (active_tty == &tty[id]) {
204  update_cursor();
205  }
206 }
207 
208 void
209 tty_getpos(int id, screenpos_t *pos)
210 {
211  if ((id < 0) || (id >= MAX_TTYS)) {
212  id = 0;
213  }
214  *pos = tty[id].pos;
215 }
216 
217 static int
218 colorcode(char x, int orig)
219 {
220  int code = x;
221 
222  if ((code >= '0') && (code <= '9')) {
223  return code - '0';
224  }
225  else if ((code >= 'a') && (code <= 'f')) {
226  return code - 'a' + 10;
227  }
228  else if ((code >= 'A') && (code <= 'F')) {
229  return code - 'A' + 10;
230  }
231  else if (code == '-') {
232  return orig;
233  }
234  else {
235  return -1;
236  }
237 }
238 
239 static void
240 tty_printchar(tty_t *cons, const char **strptr)
241 {
242  bool linefeed = false;
243 
244  const char *str = *strptr;
245  char ch = *str;
246 
247  // If the newline character is encountered, do a line feed + carriage
248  // return.
249  if (ch == '\n') {
250  cons->pos.x = 0;
251  linefeed = true;
252  }
253  // Handle color codes, e.g. "\033[#]".
254  else if (ch == '\033') {
255  if ((str[1] == '[') && str[2] && (str[3] == ']')) {
256  int code = colorcode(str[2], (cons->textcolor_orig >> 8) & 0x0f);
257  if (code != -1) {
258  cons->textcolor = (cons->textcolor & 0xf000) |
259  (uint16_t)(code << 8);
260  *strptr += 3;
261  }
262  }
263  else if ((str[1] == '{') && str[2] && (str[3] == '}')) {
264  int code = colorcode(str[2], (cons->textcolor_orig >> 12));
265  if (code != -1) {
266  cons->textcolor = (cons->textcolor & 0x0f00) |
267  (uint16_t)(code << 12);
268  *strptr += 3;
269  }
270  }
271  return;
272  }
273  else if (ch == '\b') {
274  if (cons->pos.x > 0) {
275  int offset = cons->ybuf * SCREEN_COLS + --cons->pos.x;
276  cons->screen[offset] = cons->textcolor | ' ';
277  }
278  }
279  else {
280  // Use the current foreground and background color.
281  uint16_t value = cons->textcolor | ch;
282 
283  // Calculate the buffer offset using the virtual row.
284  int offset = cons->ybuf * SCREEN_COLS + cons->pos.x;
285 
286  // Update the visible screen buffer.
287  cons->screen[offset] = value;
288 
289  // If the right side of the screen was reached, we need a linefeed.
290  if (++cons->pos.x == SCREEN_COLS) {
291  cons->pos.x = 0;
292  linefeed = true;
293  }
294  }
295 
296  // A linefeed causes a hardware scroll of one row. If we reach the end of
297  // the virtual buffer, wrap it back one screen.
298  if (linefeed) {
299 
300  // Copy the last line of the screen to a virtual row one screen
301  // away. This way, when we reach the end of the virtual buffer,
302  // we'll have another shifted copy of the screen ready to display.
303  // This is better than copying the entire screen whenever we reach
304  // the end of the virtual buffer, because it amortizes the cost
305  // of copying.
306  memcpy(cons->screen + cons->ybuf * SCREEN_COLS - SCREEN_SIZE,
307  cons->screen + cons->ybuf * SCREEN_COLS,
308  SCREEN_COLS * sizeof(uint16_t));
309 
310  // Increment row (on screen and in the virtual buffer).
311  ++cons->pos.y;
312  ++cons->ybuf;
313 
314  // If we're at the bottom of the screen, we need to scroll a line.
315  if (cons->pos.y == SCREEN_ROWS) {
316 
317  --cons->pos.y;
318 
319  // If we're at the end of the virtual buffer, we need to
320  // wrap back a screen.
321  if (cons->ybuf == SCREEN_ROWS * 2) {
322  cons->ybuf -= SCREEN_ROWS;
323  }
324 
325  // Clear the row at the bottom of the screen.
326  memsetw(cons->screen + cons->ybuf * SCREEN_COLS,
327  cons->textcolor | ' ',
328  SCREEN_COLS * sizeof(uint16_t));
329 
330  // Adjust the offset of the top-left corner of the screen.
331  cons->tlcorner = cons->screen + (cons->ybuf + 1) * SCREEN_COLS -
332  SCREEN_SIZE;
333 
334  // Do a hardware scroll if this console is currently active.
335  if (cons == active_tty) {
337  }
338  }
339  }
340 }
341 
342 void
343 tty_print(int id, const char *str)
344 {
345  if ((id < 0) || (id >= MAX_TTYS))
346  id = 0;
347 
348  tty_t *cons = &tty[id];
349  for (; *str; ++str)
350  tty_printchar(cons, &str);
351 
352  if (cons == active_tty)
353  update_cursor();
354 }
355 
356 void
357 tty_printc(int id, char ch)
358 {
359  if ((id < 0) || (id >= MAX_TTYS))
360  id = 0;
361 
362  const char str[2] = { ch, 0 };
363  const char *ptr = str;
364  tty_t *cons = &tty[id];
365  tty_printchar(cons, &ptr);
366 
367  if (cons == active_tty)
368  update_cursor();
369 }
370 
371 int
372 tty_printf(int id, const char *format, ...)
373 {
374  if ((id < 0) || (id >= MAX_TTYS))
375  id = 0;
376 
377  va_list args;
378  va_start(args, format);
379  char buffer[8 * 1024];
380  int result = vsnprintf(buffer, sizeof(buffer), format, args);
381  va_end(args);
382 
383  tty_print(id, buffer);
384 
385  return result;
386 }
#define MAX_TTYS
The number of available virtual consoles.
Definition: tty.h:20
textcolor
Color values used for tty text.
Definition: tty.h:26
__forceinline uint8_t io_inb(uint16_t port)
Definition: cpu_inl.h:47
__forceinline void io_outb(uint16_t port, uint8_t value)
Definition: cpu_inl.h:59
String and memory operations.
textcolor_t tty_get_textcolor_fg(int id)
Get the foreground color used to display text on the virtual console.
Definition: tty.c:154
void tty_set_textcolor(int id, textcolor_t fg, textcolor_t bg)
Set the foreground and background colors used to display text on the virtual console.
Definition: tty.c:122
static uint16_t color(textcolor_t fg, textcolor_t bg)
Definition: tty.c:49
void tty_getpos(int id, screenpos_t *pos)
Get the current position of the cursor on the virtual console.
Definition: tty.c:209
void tty_set_textcolor_bg(int id, textcolor_t bg)
Set the background color used to display text on the virtual console.
Definition: tty.c:143
Core include file.
#define SCREEN_BUFFER
Definition: tty.c:30
uint16_t textcolor
Current fg/bg color (shifted).
Definition: tty.c:35
int vsnprintf(char *buf, size_t n, const char *format, va_list arg)
Compose a printf-formatted string into the target buffer using a variable argument list...
uint16_t textcolor_orig
Original, non-override text color.
Definition: tty.c:36
uint8_t ybuf
Virtual buffer y position.
Definition: tty.c:38
uint16_t * tlcorner
Points to char in top-left corner.
Definition: tty.c:40
enum textcolor textcolor_t
Definition: tty.h:46
void tty_activate(int id)
Activate the requested virtual console.
Definition: tty.c:107
#define CRTC_PORT_CMD
Command port for CRT controller.
Definition: tty.c:17
void tty_init()
Initialize all virtual consoles.
Definition: tty.c:89
static void tty_printchar(tty_t *cons, const char **strptr)
Definition: tty.c:240
#define CRTC_CMD_STARTADDR_HI
Hi-byte of buffer start address.
Definition: tty.c:21
textcolor_t tty_get_textcolor_bg(int id)
Get the background color used to display text on the virtual console.
Definition: tty.c:164
#define SCREEN_COLS
Definition: tty.c:28
#define CRTC_CMD_CURSORADDR_LO
Lo-byte of cursor start address.
Definition: tty.c:24
#define CRTC_PORT_DATA
Data port for CRT controller.
Definition: tty.c:18
static int colorcode(char x, int orig)
Definition: tty.c:218
static tty_t * active_tty
The currently visible console.
Definition: tty.c:46
x86 CPU-specific function implementations.
void tty_print(int id, const char *str)
Output a null-terminated string to the virtual console using the console&#39;s current text color and scr...
Definition: tty.c:343
static void update_cursor()
Definition: tty.c:72
Standard i/o library.
Virtual console state.
Definition: tty.c:33
screenpos_t pos
Current screen position.
Definition: tty.c:37
int tty_printf(int id, const char *format,...)
Output a printf-formatted string to the virtual console using the console&#39;s current text color and sc...
Definition: tty.c:372
Teletype (console) screen text manipulation routines.
#define CRTC_CMD_CURSORADDR_HI
Hi-byte of cursor start address.
Definition: tty.c:23
void tty_set_textcolor_fg(int id, textcolor_t fg)
Set the foreground color used to display text on the virtual console.
Definition: tty.c:132
static void update_buffer_offset()
Definition: tty.c:55
void * memsetw(void *dst, int w, size_t num)
Fill a region of memory with a single 16-bit word value.
#define SCREEN_SIZE
Definition: tty.c:29
void tty_clear(int id)
Clear the virtual console screen&#39;s contents using the current text background color.
Definition: tty.c:174
void tty_printc(int id, char ch)
Output a single character to the virtual console using the console&#39;s current text color and screen po...
Definition: tty.c:357
#define CRTC_CMD_STARTADDR_LO
Lo-byte of buffer start address.
Definition: tty.c:22
#define SCREEN_ROWS
Definition: tty.c:27
uint16_t * screen
Virtual screen buffer for 50 rows.
Definition: tty.c:39
void tty_setpos(int id, screenpos_t pos)
Set the position of the cursor on the virtual console.
Definition: tty.c:194
void * memcpy(void *dst, const void *src, size_t num)
Copy bytes from one memory region to another.