Procházet zdrojové kódy

lesson 17, video scroll

Carlos Fenollosa před 10 roky
rodič
revize
f8369ec919

+ 1 - 0
17-video-scroll/Makefile

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

+ 22 - 0
17-video-scroll/README.md

@@ -0,0 +1,22 @@
+*Concepts you may want to Google beforehand: scroll*
+
+**Goal: Scroll the screen when the text reaches the bottom**
+
+For this short lesson, open `drivers/screen.c` and note that at the
+bottom of `print_char` there is a new section (line 84) which checks
+if the current offset is over the screen size and scrolls the text.
+
+The actual scrolling is handled by a new function, `memory_copy`. It is
+a simpler version of the standard `memcpy` but we named it differently
+to avoid namespace collisions, at least for now. Open `kernel/util.c` to
+see its implementation.
+
+To help visualize scrolling, we will also implement a function to
+convert integers to text, `int_to_ascii`. Again, it is a quick implementation
+of the standard `itoa`. Notice that for integers which have double digits
+or more, they are printed in reverse. This is intended. On future lessons
+we will extend our helper functions, but that is not the point for now.
+
+Finally, open `kernel/kernel.c`. Initially, each line displays its line
+number. You can set a breakpoint on line 14 to confirm this. Then,
+the following `kprint`s force the kernel to scroll down.

+ 1 - 0
17-video-scroll/boot

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

+ 1 - 0
17-video-scroll/drivers/ports.c

@@ -0,0 +1 @@
+../../16-video-driver/drivers/ports.c

+ 1 - 0
17-video-scroll/drivers/ports.h

@@ -0,0 +1 @@
+../../16-video-driver/drivers/ports.h

+ 139 - 0
17-video-scroll/drivers/screen.c

@@ -0,0 +1,139 @@
+#include "screen.h"
+#include "ports.h"
+#include "../kernel/util.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;
+    }
+
+    /* Check if the offset is over screen size and scroll */
+    if (offset >= MAX_ROWS * MAX_COLS * 2) {
+        int i;
+        for (i = 1; i < MAX_ROWS; i++) 
+            memory_copy(get_offset(0, i) + VIDEO_ADDRESS,
+                        get_offset(0, i-1) + VIDEO_ADDRESS,
+                        MAX_COLS * 2);
+
+        /* Blank last line */
+        char *last_line = get_offset(0, MAX_ROWS-1) + VIDEO_ADDRESS;
+        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;
+
+        offset -= 2 * MAX_COLS;
+    }
+
+    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; }

+ 1 - 0
17-video-scroll/drivers/screen.h

@@ -0,0 +1 @@
+../../16-video-driver/drivers/screen.h

+ 17 - 0
17-video-scroll/kernel/kernel.c

@@ -0,0 +1,17 @@
+#include "../drivers/screen.h"
+#include "util.h"
+
+void main() {
+    clear_screen();
+
+    /* Fill up the screen */
+    int i = 0;
+    for (i = 0; i < 24; i++) {
+        char str[255];
+        int_to_ascii(i, str);
+        kprint_at(str, 0, i);
+    }
+
+    kprint_at("This text forces the kernel to scroll. Row 0 will disappear. ", 60, 24);
+    kprint("And with this text, the kernel will scroll again, and row 1 will disappear too!");
+}

+ 23 - 0
17-video-scroll/kernel/util.c

@@ -0,0 +1,23 @@
+void memory_copy(char *source, char *dest, int nbytes) {
+    int i;
+    for (i = 0; i < nbytes; i++) {
+        *(dest + i) = *(source + i);
+    }
+}
+
+/**
+ * K&R implementation
+ */
+void int_to_ascii(int n, char str[]) {
+    int i, sign;
+    if ((sign = n) < 0) n = -n;
+    i = 0;
+    do {
+        str[i++] = n % 10 + '0';
+    } while ((n /= 10) > 0);
+
+    if (sign < 0) str[i++] = '-';
+    str[i] = '\0';
+
+    /* TODO: implement "reverse" */
+}

+ 2 - 0
17-video-scroll/kernel/util.h

@@ -0,0 +1,2 @@
+void memory_copy(char *source, char *dest, int nbytes);
+void int_to_ascii(int n, char str[]);