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