vga.c (2765B)
1 #include "vga.h" 2 #include "cpufunc.h" 3 4 #define VGA_MEM 0xb8000; 5 #define VGA_COLS 80 6 #define VGA_ROWS 25 7 #define VGA_PUTC(c) (((u_int16_t)vga.color << 8) | (c)) 8 9 #define CURS_CMD 0x3d4 10 #define CURS_DATA 0x3d5 11 12 struct vga { 13 volatile u_int16_t *buf; 14 size_t row; /* XXX: why signed? */ 15 size_t col; 16 u_int8_t color; 17 }; 18 19 static struct vga vga; 20 21 void 22 vga_clear(u_int8_t fg, u_int8_t bg) 23 { 24 size_t y, x; 25 26 vga_curs_enable(0x0e, 0x0f); /* Low cursor shape. */ 27 vga_curs_setpos(0, 0); 28 vga.row = 0; 29 vga.col = 0; 30 vga.buf = (u_int16_t *)VGA_MEM; 31 vga_set_color(fg, bg); 32 for (x = 0; x < VGA_COLS; x++) 33 for (y = 0; y < VGA_ROWS; y++) 34 vga.buf[y * VGA_COLS + x] = VGA_PUTC(' '); 35 } 36 37 void 38 vga_set_color(u_int8_t fg, u_int8_t bg) 39 { 40 vga.color = fg | (bg << 4); 41 } 42 43 /* 44 * TODO: page break 45 * https://en.wikipedia.org/wiki/Page_break 46 * https://en.wikipedia.org/wiki/Escape_sequences_in_C 47 * 48 * handle long lines (backspacing inside them as well) 49 */ 50 void 51 vga_putc(char c) 52 { 53 switch (c) { 54 case '\n': 55 vga.row++; 56 vga.col = 0; 57 break; 58 case '\b': 59 if (vga.col > 0) 60 vga.col--; 61 vga.buf[vga.row * VGA_COLS + vga.col] = VGA_PUTC(' '); 62 break; 63 case '\r': 64 vga.col = 0; 65 break; 66 case '\t': 67 vga.col += 4; 68 break; 69 default: 70 vga.buf[vga.row * VGA_COLS + vga.col] = VGA_PUTC(c); 71 vga.col++; 72 } 73 74 if (vga.row > VGA_ROWS - 1) { 75 /* XXX: change page completely or push everything down one line? */ 76 vga_clear(VGA_BLACK, VGA_WHITE); 77 vga.row = 0; 78 vga.col = 0; 79 } 80 if (vga.col > VGA_COLS - 1) { 81 vga.row++; 82 vga.col = 0; 83 } 84 vga_curs_setpos(vga.col, vga.row); 85 } 86 87 void 88 vga_puts(const char *str) 89 { 90 while (*str != '\0') 91 vga_putc(*str++); 92 } 93 94 /* 95 * The arguments `start` and `end` define the shape of the cursor. 96 * The lowest scaline value is 0x00 and the highest 0x0f. A cursor shape 97 * with `start = 0x00` and `end = 0x0f` is effectively a block cursor. 98 */ 99 void 100 vga_curs_enable(u_int8_t start, u_int8_t end) 101 { 102 /* 0x0a: Low cursor shape */ 103 outb(CURS_CMD, 0x0a); 104 outb(CURS_DATA, (inb(CURS_DATA) & 0xc0) | start); 105 outb(CURS_CMD, 0x0b); 106 outb(CURS_DATA, (inb(CURS_DATA) & 0xe0) | end); 107 } 108 109 void 110 vga_curs_disable(void) 111 { 112 outb(CURS_CMD, 0x0a); 113 /* 114 * Bit representation: 115 * 7 6 | 5 | 4 0 116 * Unused | Disable | Shape 117 */ 118 outb(CURS_DATA, 0x20); 119 } 120 121 /* 122 * Returns the position in `y * VGA_COLS + x` format. 123 * x = pos % VGA_COLS 124 * y = pos / VGA_COLS 125 */ 126 u_int16_t 127 vga_curs_getpos(void) 128 { 129 u_int16_t pos = 0; 130 131 outb(CURS_CMD, 0x0e); 132 pos |= (u_int16_t)inb(CURS_DATA) << 8; 133 outb(CURS_CMD, 0x0f); 134 pos |= inb(CURS_DATA); 135 136 return (pos); 137 } 138 139 void 140 vga_curs_setpos(int x, int y) 141 { 142 u_int16_t pos = y * VGA_COLS + x; 143 144 outb(CURS_CMD, 0x0e); 145 outb(CURS_DATA, (u_int8_t)((pos >> 8) & 0xff)); 146 outb(CURS_CMD, 0x0f); 147 outb(CURS_DATA, (u_int8_t)(pos & 0xff)); 148 }