os

Toy OS
git clone git://git.margiolis.net/os.git
Log | Files | Refs | README | LICENSE

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 }