aboutsummaryrefslogtreecommitdiff
path: root/devices/vga.c
diff options
context:
space:
mode:
authoraqua <aqua@iserlohn-fortress.net>2023-03-12 18:39:39 +0200
committeraqua <aqua@iserlohn-fortress.net>2023-03-12 18:39:39 +0200
commitfbc736463f2ca2f5dbf1b7c412f408245e61df97 (patch)
tree84a66eabc5e3be2191ae0d5d1e0ff33d05515b4a /devices/vga.c
parentAdd unit tests for C drivers (diff)
downloadkernel-fbc736463f2ca2f5dbf1b7c412f408245e61df97.tar.xz
Revert VGA C driver
Diffstat (limited to 'devices/vga.c')
-rw-r--r--devices/vga.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/devices/vga.c b/devices/vga.c
new file mode 100644
index 0000000..e4831b8
--- /dev/null
+++ b/devices/vga.c
@@ -0,0 +1,152 @@
+#include "vga.h"
+#include <stdint.h>
+#include <sys/io.h>
+
+#define cga_idx_port 0x3d4
+#define cga_dat_port 0x3d5
+
+#define cursor_start 0x0a
+#define cursor_end 0x0b
+#define cursor_addr_h 0x0e
+#define cursor_addr_l 0x0f
+#define cursor_hide 0x20
+
+struct __attribute__((packed)) VGAEntry {
+ unsigned char text;
+ uint8_t foreground : 4;
+ uint8_t background : 4;
+};
+_Static_assert(sizeof(struct VGAEntry) == 2, "sizeof VGAEntry");
+
+const int width = 80;
+const int height = 25;
+
+struct VGAEntry *buffer;
+int col = 0;
+int row = 0;
+
+// *** Cursor ***
+void
+vga_enable_cursor(unsigned char start, unsigned char end)
+{
+ outb(cursor_start, cga_idx_port);
+ outb((inb(cga_dat_port) & 0xc0) | start, cga_dat_port);
+
+ outb(cursor_end, cga_idx_port);
+ outb((inb(cga_dat_port) & 0xe0) | end, cga_dat_port);
+}
+
+void
+vga_disable_cursor()
+{
+ outb(cursor_start, cga_idx_port);
+ outb(cursor_hide, cga_dat_port);
+}
+
+void
+vga_update_cursor(void)
+{
+ const uint16_t pos = row * width + col;
+
+ outb(cursor_addr_l, cga_idx_port);
+ outb(pos & 0xff, cga_dat_port);
+
+ outb(cursor_addr_h, cga_idx_port);
+ outb((pos >> 8) & 0xff, cga_dat_port);
+}
+
+// *** Text Mode Output ***
+void
+vga_putc(__attribute__((unused)) const FILE *self, char a)
+{
+ switch (a) {
+ case '\n':
+ col = 0;
+ ++row;
+ break;
+ case '\r':
+ col = 0;
+ break;
+ case '\b':
+ --col;
+ if (col < 0) col = 0;
+ break;
+ default:
+ buffer[row * width + col].text = a;
+ ++col;
+ }
+
+ if (col == width) {
+ col = 0;
+ ++row;
+ }
+
+ if (row == height) {
+ // scroll up
+ for (int y = 1; y < height; ++y)
+ for (int x = 0; x < width; ++x) {
+ const int prev = (y - 1) * width + x;
+ const int curr = y * width + x;
+ buffer[prev] = buffer[curr];
+ }
+ // blank out last row
+ for (int i = (height - 1) * width; i < height * width; ++i) buffer[i].text = ' ';
+ --row;
+ }
+}
+
+int
+vga_puts(const FILE *self, const char *string, int len)
+{
+ int written = 0;
+ if (len == -1)
+ while (*string != '\0') {
+ vga_putc(self, *string);
+ ++string;
+ ++written;
+ }
+
+ else
+ for (int i = 0; i < len; ++i) {
+ vga_putc(self, string[i]);
+ ++written;
+ }
+ return written;
+}
+
+void
+vga_flush(__attribute__((unused)) const FILE *self)
+{
+ vga_update_cursor();
+}
+
+// *** Text Mode ***
+FILE vga_stream;
+
+FILE *
+vga_init(void *addr)
+{
+ buffer = (struct VGAEntry *)addr;
+ vga_enable_cursor(14, 15);
+ vga_clear(VGA_COLOR_LIGHT_BLUE, VGA_COLOR_LIGHT_GREY);
+
+ vga_stream.id = 0;
+ vga_stream.putc = &vga_putc;
+ vga_stream.puts = &vga_puts;
+ vga_stream.flush = &vga_flush;
+ return &vga_stream;
+}
+
+void
+vga_clear(enum vga_color foreground, enum vga_color background)
+{
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x) {
+ const int index = y * width + x;
+ buffer[index].text = ' ';
+ buffer[index].foreground = foreground;
+ buffer[index].background = background;
+ }
+ col = row = 0;
+ vga_update_cursor();
+}