From f8369ec919a43dd4de36ffdaf77ae698946563ca Mon Sep 17 00:00:00 2001 From: Carlos Fenollosa Date: Tue, 28 Oct 2014 10:36:53 +0100 Subject: [PATCH] lesson 17, video scroll --- 17-video-scroll/Makefile | 1 + 17-video-scroll/README.md | 22 +++++ 17-video-scroll/boot | 1 + 17-video-scroll/drivers/ports.c | 1 + 17-video-scroll/drivers/ports.h | 1 + 17-video-scroll/drivers/screen.c | 139 +++++++++++++++++++++++++++++++ 17-video-scroll/drivers/screen.h | 1 + 17-video-scroll/kernel/kernel.c | 17 ++++ 17-video-scroll/kernel/util.c | 23 +++++ 17-video-scroll/kernel/util.h | 2 + 10 files changed, 208 insertions(+) create mode 120000 17-video-scroll/Makefile create mode 100644 17-video-scroll/README.md create mode 120000 17-video-scroll/boot create mode 120000 17-video-scroll/drivers/ports.c create mode 120000 17-video-scroll/drivers/ports.h create mode 100644 17-video-scroll/drivers/screen.c create mode 120000 17-video-scroll/drivers/screen.h create mode 100644 17-video-scroll/kernel/kernel.c create mode 100644 17-video-scroll/kernel/util.c create mode 100644 17-video-scroll/kernel/util.h diff --git a/17-video-scroll/Makefile b/17-video-scroll/Makefile new file mode 120000 index 0000000..3fbcd60 --- /dev/null +++ b/17-video-scroll/Makefile @@ -0,0 +1 @@ +../14-checkpoint/Makefile \ No newline at end of file diff --git a/17-video-scroll/README.md b/17-video-scroll/README.md new file mode 100644 index 0000000..6416c1d --- /dev/null +++ b/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. diff --git a/17-video-scroll/boot b/17-video-scroll/boot new file mode 120000 index 0000000..85eedf8 --- /dev/null +++ b/17-video-scroll/boot @@ -0,0 +1 @@ +../14-checkpoint/boot/ \ No newline at end of file diff --git a/17-video-scroll/drivers/ports.c b/17-video-scroll/drivers/ports.c new file mode 120000 index 0000000..82c662a --- /dev/null +++ b/17-video-scroll/drivers/ports.c @@ -0,0 +1 @@ +../../16-video-driver/drivers/ports.c \ No newline at end of file diff --git a/17-video-scroll/drivers/ports.h b/17-video-scroll/drivers/ports.h new file mode 120000 index 0000000..1de03ef --- /dev/null +++ b/17-video-scroll/drivers/ports.h @@ -0,0 +1 @@ +../../16-video-driver/drivers/ports.h \ No newline at end of file diff --git a/17-video-scroll/drivers/screen.c b/17-video-scroll/drivers/screen.c new file mode 100644 index 0000000..02d4dd7 --- /dev/null +++ b/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; } diff --git a/17-video-scroll/drivers/screen.h b/17-video-scroll/drivers/screen.h new file mode 120000 index 0000000..f304669 --- /dev/null +++ b/17-video-scroll/drivers/screen.h @@ -0,0 +1 @@ +../../16-video-driver/drivers/screen.h \ No newline at end of file diff --git a/17-video-scroll/kernel/kernel.c b/17-video-scroll/kernel/kernel.c new file mode 100644 index 0000000..647236c --- /dev/null +++ b/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!"); +} diff --git a/17-video-scroll/kernel/util.c b/17-video-scroll/kernel/util.c new file mode 100644 index 0000000..0049301 --- /dev/null +++ b/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" */ +} diff --git a/17-video-scroll/kernel/util.h b/17-video-scroll/kernel/util.h new file mode 100644 index 0000000..00eb506 --- /dev/null +++ b/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[]);