Carlos Fenollosa 10 rokov pred
rodič
commit
3530dd700c

+ 1 - 1
14-checkpoint/boot/bootsect.asm

@@ -28,7 +28,7 @@ load_kernel:
     call print_nl
 
     mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
-    mov dh, 2
+    mov dh, 16 ; Our future kernel will be larger, make this big
     mov dl, [BOOT_DRIVE]
     call disk_load
     ret

+ 1 - 0
16-video-driver/Makefile

@@ -0,0 +1 @@
+../14-checkpoint/Makefile

+ 61 - 0
16-video-driver/README.md

@@ -0,0 +1,61 @@
+*Concepts you may want to Google beforehand: VGA character cells, screen offset*
+
+**Goal: Write strings on the screen**
+
+Finally, we are going to be able to output text on the screen. This lesson contains
+a bit more code than usual, so let's go step by step.
+
+Open `drivers/screen.h` and you'll see that we have defined some constants for the VGA
+card driver and three public functions, one to clear the screen and another couple
+to write strings, the famously named `kprint` for "kernel print"
+
+Now open `drivers/screen.c`. It starts with the declaration of private helper functions
+that we will use to aid our `kprint` kernel API.
+
+There are the two I/O port access routines that we learned in the previous lesson,
+`get` and `set_cursor_offset()`.
+
+Then there is the routine that directly manipulates the video memory, `print_char()`
+
+Finally, there are three small helper functions to transform rows and columns into offsets
+and vice versa.
+
+
+kprint_at
+---------
+
+`kprint_at` may be called with a `-1` value for `col` and `row`, which indicates that
+we will print the string at the current cursor position.
+
+It first sets three variables for the col/row and the offset. Then it iterates through
+the `char*` and calls `print_char()` with the current coordinates.
+
+Note that `print_char` itself returns the offset of the next cursor position, and we reuse
+it for the next loop.
+
+`kprint` is basically a wrapper for `kprint_at`
+
+
+
+print_char
+----------
+
+Like `kprint_at`, `print_char` allows cols/rows to be `-1`. In that case it retrieves
+the cursor position from the hardware, using the `ports.c` routines.
+
+`print_char` also handles newlines. In that case, we will position the cursor offset
+to column 0 of the next row. 
+
+Remember that the VGA cells take two bytes, one for the character itself and another one
+for the attribute.
+
+
+kernel.c
+--------
+
+Our new kernel is finally able to print strings.
+
+It tests correct character positioning, spanning through multiple lines, line breaks,
+and finally it tries to write outside of the screen bounds. What happens then?
+
+In the next lesson we will learn how to scroll the screen.

+ 1 - 0
16-video-driver/boot

@@ -0,0 +1 @@
+../14-checkpoint/boot

+ 35 - 0
16-video-driver/drivers/ports.c

@@ -0,0 +1,35 @@
+/**
+ * Read a byte from the specified port
+ */
+unsigned char port_byte_in (unsigned short port) {
+    unsigned char result;
+    /* Inline assembler syntax
+     * !! Notice how the source and destination registers are switched from NASM !!
+     *
+     * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x
+     * '"d" (port)': map the C variable '(port)' into e'd'x register
+     *
+     * Inputs and outputs are separated by colons
+     */
+    __asm__("in %%dx, %%al" : "=a" (result) : "d" (port));
+    return result;
+}
+
+void port_byte_out (unsigned short port, unsigned char data) {
+    /* Notice how here both registers are mapped to C variables and
+     * nothing is returned, thus, no equals '=' in the asm syntax 
+     * However we see a comma since there are two variables in the input area
+     * and none in the 'return' area
+     */
+    __asm__("out %%al, %%dx" : : "a" (data), "d" (port));
+}
+
+unsigned short port_word_in (unsigned short port) {
+    unsigned short result;
+    __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port));
+    return result;
+}
+
+void port_word_out (unsigned short port, unsigned short data) {
+    __asm__("out %%ax, %%dx" : : "a" (data), "d" (port));
+}

+ 4 - 0
16-video-driver/drivers/ports.h

@@ -0,0 +1,4 @@
+unsigned char port_byte_in (unsigned short port);
+void port_byte_out (unsigned short port, unsigned char data);
+unsigned short port_word_in (unsigned short port);
+void port_word_out (unsigned short port, unsigned short data);

+ 122 - 0
16-video-driver/drivers/screen.c

@@ -0,0 +1,122 @@
+#include "screen.h"
+#include "ports.h"
+
+/* Declaration of private functions */
+int get_cursor_offset();
+void set_cursor_offset(int offset);
+int print_char(char c, int col, int row, char attr);
+int get_offset(int col, int row);
+int get_offset_row(int offset);
+int get_offset_col(int offset);
+
+/**********************************************************
+ * Public Kernel API functions                            *
+ **********************************************************/
+
+/**
+ * Print a message on the specified location
+ * If col, row, are negative, we will use the current offset
+ */
+void kprint_at(char *message, int col, int row) {
+    /* Set cursor if col/row are negative */
+    int offset;
+    if (col >= 0 && row >= 0)
+        offset = get_offset(col, row);
+    else {
+        offset = get_cursor_offset();
+        row = get_offset_row(offset);
+        col = get_offset_col(offset);
+    }
+
+    /* Loop through message and print it */
+    int i = 0;
+    while (message[i] != 0) {
+        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);
+        /* Compute row/col for next iteration */
+        row = get_offset_row(offset);
+        col = get_offset_col(offset);
+    }
+}
+
+void kprint(char *message) {
+    kprint_at(message, -1, -1);
+}
+
+
+/**********************************************************
+ * Private kernel functions                               *
+ **********************************************************/
+
+
+/**
+ * Innermost print function for our kernel, directly accesses the video memory 
+ *
+ * If 'col' and 'row' are negative, we will print at current cursor location
+ * If 'attr' is zero it will use 'white on black' as default
+ * Returns the offset of the next character
+ * Sets the video cursor to the returned offset
+ */
+int print_char(char c, int col, int row, char attr) {
+    unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;
+    if (!attr) attr = WHITE_ON_BLACK;
+
+    /* Error control: print a red 'E' if the coords aren't right */
+    if (col >= MAX_COLS || row >= MAX_ROWS) {
+        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';
+        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;
+        return get_offset(col, row);
+    }
+
+    int offset;
+    if (col >= 0 && row >= 0) offset = get_offset(col, row);
+    else offset = get_cursor_offset();
+
+    if (c == '\n') {
+        row = get_offset_row(offset);
+        offset = get_offset(0, row+1);
+    } else {
+        vidmem[offset] = c;
+        vidmem[offset+1] = attr;
+        offset += 2;
+    }
+    set_cursor_offset(offset);
+    return offset;
+}
+
+int get_cursor_offset() {
+    /* Use the VGA ports to get the current cursor position
+     * 1. Ask for high byte of the cursor offset (data 14)
+     * 2. Ask for low byte (data 15)
+     */
+    port_byte_out(REG_SCREEN_CTRL, 14);
+    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */
+    port_byte_out(REG_SCREEN_CTRL, 15);
+    offset += port_byte_in(REG_SCREEN_DATA);
+    return offset * 2; /* Position * size of character cell */
+}
+
+void set_cursor_offset(int offset) {
+    /* Similar to get_cursor_offset, but instead of reading we write data */
+    offset /= 2;
+    port_byte_out(REG_SCREEN_CTRL, 14);
+    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));
+    port_byte_out(REG_SCREEN_CTRL, 15);
+    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));
+}
+
+void clear_screen() {
+    int screen_size = MAX_COLS * MAX_ROWS;
+    int i;
+    char *screen = VIDEO_ADDRESS;
+
+    for (i = 0; i < screen_size; i++) {
+        screen[i*2] = ' ';
+        screen[i*2+1] = WHITE_ON_BLACK;
+    }
+    set_cursor_offset(get_offset(0, 0));
+}
+
+
+int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }
+int get_offset_row(int offset) { return offset / (2 * MAX_COLS); }
+int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }

+ 14 - 0
16-video-driver/drivers/screen.h

@@ -0,0 +1,14 @@
+#define VIDEO_ADDRESS 0xb8000
+#define MAX_ROWS 25
+#define MAX_COLS 80
+#define WHITE_ON_BLACK 0x0f
+#define RED_ON_WHITE 0xf4
+
+/* Screen i/o ports */
+#define REG_SCREEN_CTRL 0x3d4
+#define REG_SCREEN_DATA 0x3d5
+
+/* Public kernel API */
+void clear_screen();
+void kprint_at(char *message, int col, int row);
+void kprint(char *message);

+ 10 - 0
16-video-driver/kernel/kernel.c

@@ -0,0 +1,10 @@
+#include "../drivers/screen.h"
+
+void main() {
+    clear_screen();
+    kprint_at("X", 1, 6);
+    kprint_at("This text spans multiple lines", 75, 10);
+    kprint_at("There is a line\nbreak", 0, 20);
+    kprint("There is a line\nbreak");
+    kprint_at("What happens when we run out of space?", 45, 24);
+}