Generic types; start VGA driver
This commit is contained in:
parent
4ee62fab50
commit
9e4aaa56ba
2
Makefile
2
Makefile
@ -9,7 +9,7 @@ DEPS := $(OBJS:.o=.d)
|
||||
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
|
||||
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
||||
|
||||
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP -g -std=c++11 -Wall
|
||||
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP -g -std=c++17 -Wall
|
||||
|
||||
# Default makefile target
|
||||
all: os-image
|
||||
|
15
src/cpu/types.h
Normal file
15
src/cpu/types.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef KERNEL_CPU_TYPES_H
|
||||
#define KERNEL_CPU_TYPES_H
|
||||
|
||||
// Non-semantic types specific to 32-bit Intel x86
|
||||
typedef unsigned int u32_t;
|
||||
typedef int i32_t;
|
||||
typedef unsigned short u16_t;
|
||||
typedef short i16_t;
|
||||
typedef unsigned char u8_t;
|
||||
typedef char i8_t;
|
||||
|
||||
#define LOW16(address) (u16)((address) & 0xffff)
|
||||
#define HIGH16(address) (u16)(((address) >> 16) & 0xffff)
|
||||
|
||||
#endif //KERNEL_CPU_TYPES_H
|
0
src/drivers/.gitkeep
Normal file
0
src/drivers/.gitkeep
Normal file
149
src/drivers/vga.cpp
Normal file
149
src/drivers/vga.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "../kernel/io.h"
|
||||
#include "vga.h"
|
||||
|
||||
int VGA::getOffset(int col, int row) {
|
||||
return 2 * (row * VGA_MAX_COLS + col);
|
||||
}
|
||||
|
||||
int VGA::getOffsetRow(int offset) {
|
||||
return offset / (2 * VGA_MAX_COLS);
|
||||
}
|
||||
|
||||
int VGA::getOffsetCol(int offset) {
|
||||
return (offset - (getOffsetRow(offset) * 2 * VGA_MAX_COLS)) / 2;
|
||||
}
|
||||
|
||||
int VGA::getCursorOffset() {
|
||||
io_byte_out(REG_VGA_CTL, 14);
|
||||
int offset = io_byte_in(REG_VGA_DATA) << 8; // High byte
|
||||
|
||||
io_byte_out(REG_VGA_CTL, 15);
|
||||
offset += io_byte_in(REG_VGA_DATA); // Low byte
|
||||
|
||||
return offset * 2; // Character cell is 2 bytes, so * position by 2
|
||||
}
|
||||
|
||||
void VGA::setCursorOffset(int offset) {
|
||||
offset /= 2; // Char cell is 2 bytes, so divide the memory offset by 2 to get position
|
||||
io_byte_out(REG_VGA_CTL, 14); // write the high-byte
|
||||
io_byte_out(REG_VGA_DATA, (unsigned char)(offset >> 8)); // get only the high-byte
|
||||
io_byte_out(REG_VGA_CTL, 15); // write the low-byte
|
||||
io_byte_out(REG_VGA_DATA, (unsigned char)(offset & 0xff)); // get only the low-byte
|
||||
}
|
||||
|
||||
int VGA::printChar(char character, int col, int row, char attrs) {
|
||||
// Create a char pointer to the start of video memory
|
||||
unsigned char* vidmem = (unsigned char*) VGA_ADDRESS;
|
||||
|
||||
// If the attributes are nullish, then assume the default
|
||||
if ( !attrs ) {
|
||||
attrs = VGA_WHITE_ON_BLACK;
|
||||
}
|
||||
|
||||
// Calculate the video memory offset for the current screen location
|
||||
int offset;
|
||||
|
||||
// If col & row are non-negative, use those
|
||||
if ( col >= 0 && row >= 0 ) {
|
||||
offset = getOffset(col, row);
|
||||
} else {
|
||||
// Otherwise, use the current location of the cursor.
|
||||
offset = getCursorOffset();
|
||||
}
|
||||
|
||||
// If we see a newline character, advance offset to the end of the current row,
|
||||
// so it will be advanced to the first column of the next row.
|
||||
if ( character == '\n' ) {
|
||||
int current_row = getOffsetRow(offset);
|
||||
offset = getOffset(VGA_MAX_COLS - 1, current_row);
|
||||
} else {
|
||||
// Otherwise, write the char and its attr byte to the specified vidmem offset
|
||||
vidmem[offset] = character;
|
||||
vidmem[offset + 1] = attrs;
|
||||
}
|
||||
|
||||
// Handle scrolling, when we reach the bottom of the screen
|
||||
offset = adjustScrolling(offset);
|
||||
|
||||
// Update the offset to the next cell, 2 bytes ahead of the current one
|
||||
offset += 2;
|
||||
|
||||
// Update the cursor position on the screen device
|
||||
setCursorOffset(offset);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void VGA::clearScreen() {
|
||||
int screen_size = VGA_MAX_COLS * VGA_MAX_ROWS;
|
||||
unsigned char* vidmem = (unsigned char*) VGA_ADDRESS;
|
||||
|
||||
for ( int i = 0; i < screen_size; i += 1 ) {
|
||||
vidmem[i * 2] = ' ';
|
||||
vidmem[(i * 2) + 1] = VGA_WHITE_ON_BLACK;
|
||||
}
|
||||
|
||||
setCursorOffset(getOffset(0, 0));
|
||||
}
|
||||
|
||||
int VGA::adjustScrolling(int offset) {
|
||||
int row = getOffsetRow(offset);
|
||||
int col = getOffsetCol(offset);
|
||||
|
||||
if ( row >= (VGA_MAX_ROWS - 1) && col >= (VGA_MAX_COLS - 2) ) {
|
||||
advanceScrolling();
|
||||
return getOffset(col, row - 1);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void VGA::advanceScrolling() {
|
||||
int screen_size = VGA_MAX_COLS * VGA_MAX_ROWS;
|
||||
unsigned char* vidmem = (unsigned char*) VGA_ADDRESS;
|
||||
|
||||
// Loop over every character cell on the screen
|
||||
for ( int i = 0; i < screen_size; i += 1 ) {
|
||||
// Compute the existing offset & row/column
|
||||
int offset = i * 2;
|
||||
int col = getOffsetCol(offset);
|
||||
int row = getOffsetRow(offset);
|
||||
|
||||
if ( row > 0 ) {
|
||||
// If this is not the top (scrolled-off) row, copy the char
|
||||
// to the same column in the row above it.
|
||||
int new_offset = getOffset(col, row - 1);
|
||||
vidmem[new_offset] = vidmem[offset];
|
||||
vidmem[new_offset + 1] = vidmem[offset + 1];
|
||||
}
|
||||
|
||||
if ( row == (VGA_MAX_ROWS - 1) ) {
|
||||
// If this is the bottom row, clear the cells.
|
||||
vidmem[offset] = ' ';
|
||||
vidmem[offset + 1] = VGA_WHITE_ON_BLACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VGA::printAt(char* string, int col, int row) {
|
||||
// Update the cursor if col & row nonnegative
|
||||
if ( col >= 0 && row >= 0 ) {
|
||||
setCursorOffset(getOffset(col, row));
|
||||
}
|
||||
|
||||
// Loop through each char of the string and print it
|
||||
int i = 0;
|
||||
int offset;
|
||||
while ( string[i] != 0 ) {
|
||||
offset = printChar(string[i], col, row, VGA_WHITE_ON_BLACK);
|
||||
|
||||
// Compute row/col for next iteration
|
||||
i += 1;
|
||||
col = getOffsetCol(offset);
|
||||
row = getOffsetRow(offset);
|
||||
}
|
||||
}
|
||||
|
||||
void VGA::print(char* string) {
|
||||
printAt(string, -1, -1);
|
||||
}
|
83
src/drivers/vga.h
Normal file
83
src/drivers/vga.h
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef KERNEL_VGA_H
|
||||
#define KERNEL_VGA_H
|
||||
|
||||
// Base memory address for MM VGA
|
||||
#define VGA_ADDRESS 0xb8000
|
||||
|
||||
// Max character cell dimensions
|
||||
#define VGA_MAX_ROWS 25
|
||||
#define VGA_MAX_COLS 80
|
||||
|
||||
// Default color scheme
|
||||
#define VGA_WHITE_ON_BLACK 0x0f
|
||||
|
||||
// Screen device I/O ports
|
||||
#define REG_VGA_CTL 0x3d4
|
||||
#define REG_VGA_DATA 0x3d5
|
||||
|
||||
class VGA {
|
||||
protected:
|
||||
/**
|
||||
* Given a column and row of a VGA character cell, compute the memory
|
||||
* offset from the beginning of the VGA video memory of its char and
|
||||
* attr bytes.
|
||||
* @param col
|
||||
* @param row
|
||||
* @return the computed offset
|
||||
*/
|
||||
int getOffset(int col, int row);
|
||||
|
||||
int getOffsetRow(int offset);
|
||||
|
||||
int getOffsetCol(int offset);
|
||||
|
||||
/**
|
||||
* Get the memory offset of the character cell where the cursor is.
|
||||
*
|
||||
* 1. Ask for the high-byte of the offset (data 14)
|
||||
* 2. Ask for the low-byte (data 15)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getCursorOffset();
|
||||
|
||||
/**
|
||||
* Given a VGA memory offset, set the cursor to that offset's character cell.
|
||||
* @param offset
|
||||
*/
|
||||
void setCursorOffset(int offset);
|
||||
|
||||
/**
|
||||
* Print a character to the specified position.
|
||||
*
|
||||
* If col/row are negative, print to the current cursor position.
|
||||
* If attribute_byte is 0, default white on black.
|
||||
*
|
||||
* @param character
|
||||
* @param col
|
||||
* @param row
|
||||
* @param attribute_byte
|
||||
*/
|
||||
int printChar(char character, int col, int row, char attrs);
|
||||
|
||||
int adjustScrolling(int offset);
|
||||
|
||||
/**
|
||||
* Scrolls all the character cells up one row, replacing the top
|
||||
* row and creating a new empty row on the bottom.
|
||||
*/
|
||||
void advanceScrolling();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Resets all video memory characters to empty space and all formatting
|
||||
* to the default white on black. Then, reset the cursor to the first cell.
|
||||
*/
|
||||
void clearScreen();
|
||||
|
||||
void printAt(char* string, int col, int row);
|
||||
|
||||
void print(char* string);
|
||||
};
|
||||
|
||||
#endif //KERNEL_VGA_H
|
@ -1,8 +0,0 @@
|
||||
// Called by boot.asm
|
||||
void main() {
|
||||
// Create pointer to a char and point to the first text cell of VGA
|
||||
char* video_memory = (char*) 0xb8000;
|
||||
|
||||
// Write the char X to the video memory to display it on the top-left of screen
|
||||
*video_memory = 'X';
|
||||
}
|
57
src/kernel/io.cpp
Normal file
57
src/kernel/io.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* Wrapper function that reads a byte from the specified I/O port.
|
||||
*
|
||||
* "=a" (result) - put al register in the `result` var when finished
|
||||
* "d" (port) - load edx with the specified `port`
|
||||
*
|
||||
* @param port
|
||||
* @return the read byte
|
||||
*/
|
||||
u8_t io_byte_in(u16_t port) {
|
||||
u8_t result;
|
||||
__asm__("in %%dx, %%al" : "=a" (result) : "d" (port));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function that writes a byte to the specified I/O port.
|
||||
*
|
||||
* "a" (data) - load eal with `data`
|
||||
* "d" (port) - load edx with `port`
|
||||
*
|
||||
* @param port
|
||||
* @param data
|
||||
*/
|
||||
void io_byte_out(u16_t port, u8_t data) {
|
||||
__asm__("out %%al, %%dx" : : "a" (data), "d" (port));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function that reads a word from the specified I/O port.
|
||||
*
|
||||
* "=a" (result) - put eax register in the `result` var when finished
|
||||
* "d" (port) - load edx with the specified `port`
|
||||
*
|
||||
* @param port
|
||||
* @return the read word
|
||||
*/
|
||||
u16_t io_word_in(u16_t port) {
|
||||
u16_t result;
|
||||
__asm__("in %%dx, %%ax" : "=a" (result) : "d" (port));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function that writes a word to the specified I/O port.
|
||||
*
|
||||
* "a" (data) - load eax with `data`
|
||||
* "d" (port) - load edx with `port`
|
||||
*
|
||||
* @param port
|
||||
* @param data
|
||||
*/
|
||||
void io_word_out(u16_t port, u16_t data) {
|
||||
__asm__("out %%ax, %%dx" : : "a" (data), "d" (port));
|
||||
}
|
48
src/kernel/io.h
Normal file
48
src/kernel/io.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef KERNEL_IO_H
|
||||
#define KERNEL_IO_H
|
||||
|
||||
/**
|
||||
* Wrapper function that reads a byte from the specified I/O port.
|
||||
*
|
||||
* "=a" (result) - put al register in the `result` var when finished
|
||||
* "d" (port) - load edx with the specified `port`
|
||||
*
|
||||
* @param port
|
||||
* @return the read byte
|
||||
*/
|
||||
u8_t io_byte_in(u16_t port);
|
||||
|
||||
/**
|
||||
* Wrapper function that writes a byte to the specified I/O port.
|
||||
*
|
||||
* "a" (data) - load eal with `data`
|
||||
* "d" (port) - load edx with `port`
|
||||
*
|
||||
* @param port
|
||||
* @param data
|
||||
*/
|
||||
void io_byte_out(u16_t port, u8_t data);
|
||||
|
||||
/**
|
||||
* Wrapper function that reads a word from the specified I/O port.
|
||||
*
|
||||
* "=a" (result) - put eax register in the `result` var when finished
|
||||
* "d" (port) - load edx with the specified `port`
|
||||
*
|
||||
* @param port
|
||||
* @return the read word
|
||||
*/
|
||||
u16_t io_word_in(u16_t port);
|
||||
|
||||
/**
|
||||
* Wrapper function that writes a word to the specified I/O port.
|
||||
*
|
||||
* "a" (data) - load eax with `data`
|
||||
* "d" (port) - load edx with `port`
|
||||
*
|
||||
* @param port
|
||||
* @param data
|
||||
*/
|
||||
void io_word_out(u16_t port, u16_t data);
|
||||
|
||||
#endif //KERNEL_IO_H
|
9
src/kernel/main.cpp
Normal file
9
src/kernel/main.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "../drivers/vga.h"
|
||||
|
||||
// Called by boot.asm
|
||||
void main() {
|
||||
VGA vga;
|
||||
|
||||
vga.clearScreen();
|
||||
vga.print("Kernel booted.");
|
||||
}
|
14
src/kernel/mem.cpp
Normal file
14
src/kernel/mem.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
/**
|
||||
* Copy `num_bytes` many bytes from the `source` memory to the
|
||||
* `destination` memory.
|
||||
*
|
||||
* @param source
|
||||
* @param destination
|
||||
* @param num_bytes
|
||||
*/
|
||||
void mem_copy(char* source, char* destination, int num_bytes) {
|
||||
for ( int i = 0; i < num_bytes; i += 1 ) {
|
||||
*(destination + i) = *(source + i);
|
||||
}
|
||||
}
|
6
src/kernel/mem.h
Normal file
6
src/kernel/mem.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef KERNEL_MEM_H
|
||||
#define KERNEL_MEM_H
|
||||
|
||||
void mem_copy(char* source, char* destination, int num_bytes);
|
||||
|
||||
#endif //KERNEL_MEM_H
|
6
src/kernel/util.h
Normal file
6
src/kernel/util.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef KERNEL_UTIL_H
|
||||
#define KERNEL_UTIL_H
|
||||
|
||||
#include "../cpu/types.h"
|
||||
|
||||
#endif //KERNEL_UTIL_H
|
Loading…
Reference in New Issue
Block a user