diff --git a/15-screen-ports/Makefile b/15-screen-ports/Makefile new file mode 120000 index 0000000..3fbcd60 --- /dev/null +++ b/15-screen-ports/Makefile @@ -0,0 +1 @@ +../14-checkpoint/Makefile \ No newline at end of file diff --git a/15-screen-ports/README.md b/15-screen-ports/README.md new file mode 100644 index 0000000..d8718e1 --- /dev/null +++ b/15-screen-ports/README.md @@ -0,0 +1,27 @@ +*Concepts you may want to Google beforehand: I/O ports* + +**Goal: Learn how to use the VGA card data ports** + +We will use C to communicate with devices via I/O registers and ports. + +Open `drivers/ports.c` and examine the inline C assembler syntax. It has +some differences, like the order of the source and destination operands, +and the funny syntax to assign variables to operands. + +When you understand the concepts, open `kernel/kernel.c` for an example +of use. + +In this example we will examine the I/O ports which map the screen cursor +position. Specifically, we will query port `0x3d4` with value `14` to request +the cursor position high byte, and the same port with `15` for the low byte. + +When this port is queried, it saves the result in port `0x3d5` + +Don't miss the opportunity to use `gdb` to inspect the value of C variables, +since we still can't print them on the screen. To do so, set a breakpoint +for a specific line, `breakpoint kernel.c:21` and use the `print` command +to examine variables. Aren't you glad now that we invested some time in +compiling the cross-compiled gdb? ;) + +Finally, we will use the queried cursor position to write a character +at that location. diff --git a/15-screen-ports/boot b/15-screen-ports/boot new file mode 120000 index 0000000..7ce5a3d --- /dev/null +++ b/15-screen-ports/boot @@ -0,0 +1 @@ +../14-checkpoint/boot \ No newline at end of file diff --git a/15-screen-ports/drivers/ports.c b/15-screen-ports/drivers/ports.c new file mode 100644 index 0000000..60c9b9a --- /dev/null +++ b/15-screen-ports/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)); +} diff --git a/15-screen-ports/drivers/ports.h b/15-screen-ports/drivers/ports.h new file mode 100644 index 0000000..db9253b --- /dev/null +++ b/15-screen-ports/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); diff --git a/15-screen-ports/kernel/kernel.c b/15-screen-ports/kernel/kernel.c new file mode 100644 index 0000000..dcc2d9f --- /dev/null +++ b/15-screen-ports/kernel/kernel.c @@ -0,0 +1,32 @@ +#include "../drivers/ports.h" + +void main() { + /* Screen cursor position: ask VGA control register (0x3d4) for bytes + * 14 = high byte of cursor and 15 = low byte of cursor. */ + port_byte_out(0x3d4, 14); /* Requesting byte 14: high byte of cursor pos */ + /* Data is returned in VGA data register (0x3d5) */ + int position = port_byte_in(0x3d5); + position = position << 8; /* high byte */ + + port_byte_out(0x3d4, 15); /* requesting low byte */ + position += port_byte_in(0x3d5); + + /* VGA 'cells' consist of the character and its control data + * e.g. 'white on black background', 'red text on white bg', etc */ + int offset_from_vga = position * 2; + + /* Now you can examine both variables using gdb, since we still + * don't know how to print strings on screen. Run 'make debug' and + * on the gdb console: + * breakpoint kernel.c:21 + * continue + * print position + * print offset_from_vga + */ + + /* Let's write on the current cursor position, we already know how + * to do that */ + char *vga = 0xb8000; + vga[offset_from_vga] = 'X'; + vga[offset_from_vga+1] = 0x0f; /* White text on black background */ +}