You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
4.4 KiB

#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);
}