mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-08-26 21:27:15 -05:00
Initial commit.
This commit is contained in:
+369
@@ -0,0 +1,369 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020 Martin Whitaker
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
#include "font.h"
|
||||
#include "vmem.h"
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// screen_info.orig_video_isVGA values.
|
||||
|
||||
#define VIDEO_TYPE_VLFB 0x23 // VESA VGA in graphic mode
|
||||
#define VIDEO_TYPE_EFI 0x70 // EFI graphic mode
|
||||
|
||||
// screen_info.capabilities values.
|
||||
|
||||
#define LFB_CAPABILITY_64BIT_BASE (1 << 1)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// The following definition must match the Linux screen_info struct.
|
||||
|
||||
typedef struct {
|
||||
uint8_t orig_x;
|
||||
uint8_t orig_y;
|
||||
uint16_t ext_mem_k;
|
||||
uint16_t orig_video_page;
|
||||
uint8_t orig_video_mode;
|
||||
uint8_t orig_video_cols;
|
||||
uint8_t flags;
|
||||
uint8_t unused2;
|
||||
uint16_t orig_video_ega_bx;
|
||||
uint16_t unused3;
|
||||
uint8_t orig_video_lines;
|
||||
uint8_t orig_video_isVGA;
|
||||
uint16_t orig_video_points;
|
||||
|
||||
uint16_t lfb_width;
|
||||
uint16_t lfb_height;
|
||||
uint16_t lfb_depth;
|
||||
uint32_t lfb_base;
|
||||
uint32_t lfb_size;
|
||||
uint16_t cl_magic, cl_offset;
|
||||
uint16_t lfb_linelength;
|
||||
uint8_t red_size;
|
||||
uint8_t red_pos;
|
||||
uint8_t green_size;
|
||||
uint8_t green_pos;
|
||||
uint8_t blue_size;
|
||||
uint8_t blue_pos;
|
||||
uint8_t rsvd_size;
|
||||
uint8_t rsvd_pos;
|
||||
uint16_t vesapm_seg;
|
||||
uint16_t vesapm_off;
|
||||
uint16_t pages;
|
||||
uint16_t vesa_attributes;
|
||||
uint32_t capabilities;
|
||||
uint32_t ext_lfb_base;
|
||||
uint8_t _reserved[2];
|
||||
} __attribute__((packed)) screen_info_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} __attribute__((packed)) rgb_value_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t ch;
|
||||
uint8_t attr;
|
||||
};
|
||||
struct {
|
||||
uint16_t value;
|
||||
};
|
||||
} vga_char_t;
|
||||
|
||||
typedef vga_char_t vga_buffer_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const rgb_value_t vga_pallete[16] = {
|
||||
// R G B
|
||||
{ 0, 0, 0 }, // BLACK
|
||||
{ 0, 0, 170 }, // BLUE
|
||||
{ 0, 170, 0 }, // GREEN
|
||||
{ 0, 170, 170 }, // CYAN
|
||||
{ 170, 0, 0 }, // RED
|
||||
{ 170, 0, 170 }, // MAUVE
|
||||
{ 170, 85, 0 }, // YELLOW (brown really)
|
||||
{ 170, 170, 170 }, // WHITE
|
||||
{ 85, 85, 85 }, // BOLD+BLACK
|
||||
{ 85, 85, 255 }, // BOLD+BLUE
|
||||
{ 85, 255, 85 }, // BOLD+GREEN
|
||||
{ 85, 255, 255 }, // BOLD+CYAN
|
||||
{ 255, 85, 85 }, // BOLD+RED
|
||||
{ 255, 85, 255 }, // BOLD+MAUVE
|
||||
{ 255, 255, 85 }, // BOLD+YELLOW
|
||||
{ 255, 255, 255 } // BOLD+WHITE
|
||||
};
|
||||
|
||||
static vga_buffer_t *vga_buffer = (vga_buffer_t *)(0xb8000);
|
||||
|
||||
static vga_buffer_t shadow_buffer;
|
||||
|
||||
static int lfb_bytes_per_pixel = 0;
|
||||
|
||||
static uintptr_t lfb_base;
|
||||
static uintptr_t lfb_stride;
|
||||
|
||||
static uint32_t lfb_pallete[16];
|
||||
|
||||
static uint8_t current_attr = WHITE | BLUE << 4;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void vga_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
||||
{
|
||||
shadow_buffer[row][col].ch = ch;
|
||||
shadow_buffer[row][col].attr = attr;
|
||||
|
||||
(*vga_buffer)[row][col].value = shadow_buffer[row][col].value;
|
||||
}
|
||||
|
||||
static void lfb8_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
||||
{
|
||||
shadow_buffer[row][col].ch = ch;
|
||||
shadow_buffer[row][col].attr = attr;
|
||||
|
||||
uint8_t fg_colour = attr % 16;
|
||||
uint8_t bg_colour = attr / 16;
|
||||
|
||||
uint8_t *pixel_row = (uint8_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
||||
for (int y = 0; y < FONT_HEIGHT; y++) {
|
||||
uint8_t font_row = font_data[ch][y];
|
||||
for (int x = 0; x < FONT_WIDTH; x++) {
|
||||
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
||||
font_row <<= 1;
|
||||
}
|
||||
pixel_row += lfb_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void lfb16_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
||||
{
|
||||
shadow_buffer[row][col].ch = ch;
|
||||
shadow_buffer[row][col].attr = attr;
|
||||
|
||||
uint16_t fg_colour = lfb_pallete[attr % 16];
|
||||
uint16_t bg_colour = lfb_pallete[attr / 16];
|
||||
|
||||
uint16_t *pixel_row = (uint16_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
||||
for (int y = 0; y < FONT_HEIGHT; y++) {
|
||||
uint8_t font_row = font_data[ch][y];
|
||||
for (int x = 0; x < FONT_WIDTH; x++) {
|
||||
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
||||
font_row <<= 1;
|
||||
}
|
||||
pixel_row += lfb_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void lfb32_put_char(int row, int col, uint8_t ch, uint8_t attr)
|
||||
{
|
||||
shadow_buffer[row][col].ch = ch;
|
||||
shadow_buffer[row][col].attr = attr;
|
||||
|
||||
uint32_t fg_colour = lfb_pallete[attr % 16];
|
||||
uint32_t bg_colour = lfb_pallete[attr / 16];
|
||||
|
||||
uint32_t *pixel_row = (uint32_t *)lfb_base + row * FONT_HEIGHT * lfb_stride + col * FONT_WIDTH;
|
||||
for (int y = 0; y < FONT_HEIGHT; y++) {
|
||||
uint8_t font_row = font_data[ch][y];
|
||||
for (int x = 0; x < FONT_WIDTH; x++) {
|
||||
pixel_row[x] = font_row & 0x80 ? fg_colour : bg_colour;
|
||||
font_row <<= 1;
|
||||
}
|
||||
pixel_row += lfb_stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void (*put_char)(int, int, uint8_t, uint8_t) = vga_put_char;
|
||||
|
||||
static void put_value(int row, int col, uint16_t value)
|
||||
{
|
||||
put_char(row, col, value % 256, value / 256);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void screen_init(void)
|
||||
{
|
||||
const screen_info_t *screen_info = (screen_info_t *)boot_params_addr;
|
||||
|
||||
bool use_lfb = screen_info->orig_video_isVGA == VIDEO_TYPE_VLFB
|
||||
|| screen_info->orig_video_isVGA == VIDEO_TYPE_EFI;
|
||||
|
||||
if (use_lfb) {
|
||||
int lfb_width = screen_info->lfb_width;
|
||||
int lfb_height = screen_info->lfb_height;
|
||||
int lfb_depth = screen_info->lfb_depth;
|
||||
|
||||
if (lfb_depth <= 8) {
|
||||
lfb_bytes_per_pixel = 1;
|
||||
put_char = lfb8_put_char;
|
||||
} else if (lfb_depth <= 16) {
|
||||
lfb_bytes_per_pixel = 2;
|
||||
put_char = lfb16_put_char;
|
||||
} else {
|
||||
lfb_bytes_per_pixel = 4;
|
||||
put_char = lfb32_put_char;
|
||||
}
|
||||
|
||||
lfb_base = screen_info->lfb_base;
|
||||
#ifdef __x86_64__
|
||||
if (LFB_CAPABILITY_64BIT_BASE & screen_info->capabilities) {
|
||||
lfb_base |= (uintptr_t)screen_info->ext_lfb_base << 32;
|
||||
}
|
||||
#endif
|
||||
lfb_stride = screen_info->lfb_linelength;
|
||||
|
||||
lfb_base = map_framebuffer(lfb_base, lfb_height * lfb_width * lfb_bytes_per_pixel);
|
||||
|
||||
// Blank the whole framebuffer.
|
||||
int pixels_per_word = sizeof(uint32_t) / lfb_bytes_per_pixel;
|
||||
uint32_t *line = (uint32_t *)lfb_base;
|
||||
for (int y = 0; y < lfb_height; y++) {
|
||||
for (int x = 0; x < (lfb_width / pixels_per_word); x++) {
|
||||
line[x] = 0;
|
||||
}
|
||||
line += lfb_stride / sizeof(uint32_t);
|
||||
}
|
||||
|
||||
int excess_width = lfb_width - (SCREEN_WIDTH * FONT_WIDTH);
|
||||
if (excess_width > 0) {
|
||||
lfb_base += (excess_width / 2) * lfb_bytes_per_pixel;
|
||||
}
|
||||
int excess_height = lfb_height - (SCREEN_HEIGHT * FONT_HEIGHT);
|
||||
if (excess_height > 0) {
|
||||
lfb_base += (excess_height / 2) * lfb_stride;
|
||||
}
|
||||
|
||||
lfb_stride /= lfb_bytes_per_pixel;
|
||||
|
||||
// Initialise the pallete.
|
||||
uint32_t r_max = (1 << screen_info->red_size ) - 1;
|
||||
uint32_t g_max = (1 << screen_info->green_size) - 1;
|
||||
uint32_t b_max = (1 << screen_info->blue_size ) - 1;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
uint32_t r = ((vga_pallete[i].r * r_max) / 255) << screen_info->red_pos;
|
||||
uint32_t g = ((vga_pallete[i].g * g_max) / 255) << screen_info->green_pos;
|
||||
uint32_t b = ((vga_pallete[i].b * b_max) / 255) << screen_info->blue_pos;
|
||||
lfb_pallete[i] = r | g | b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_foreground_colour(screen_colour_t colour)
|
||||
{
|
||||
current_attr = (current_attr & 0xf0) | (colour & 0x0f);
|
||||
}
|
||||
|
||||
void set_background_colour(screen_colour_t colour)
|
||||
{
|
||||
current_attr = (current_attr & 0x8f) | ((colour << 4) & 0x70);
|
||||
}
|
||||
|
||||
void clear_screen(void)
|
||||
{
|
||||
for (int row = 0; row < SCREEN_HEIGHT; row++) {
|
||||
for (int col = 0; col < SCREEN_WIDTH; col++) {
|
||||
put_char(row, col, ' ', current_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clear_screen_region(int start_row, int start_col, int end_row, int end_col)
|
||||
{
|
||||
if (start_row < 0) start_row = 0;
|
||||
if (start_col < 0) start_col = 0;
|
||||
|
||||
if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1;
|
||||
if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1;
|
||||
|
||||
if (start_row > end_row) return;
|
||||
if (start_col > end_col) return;
|
||||
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
put_char(row, col, ' ', current_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scroll_screen_region(int start_row, int start_col, int end_row, int end_col)
|
||||
{
|
||||
if (start_row < 0) start_row = 0;
|
||||
if (start_col < 0) start_col = 0;
|
||||
|
||||
if (end_row >= SCREEN_HEIGHT) end_row = SCREEN_HEIGHT - 1;
|
||||
if (end_col >= SCREEN_WIDTH) end_col = SCREEN_WIDTH - 1;
|
||||
|
||||
if (start_row > end_row) return;
|
||||
if (start_col > end_col) return;
|
||||
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
if (row < end_row) {
|
||||
put_value(row, col, shadow_buffer[row + 1][col].value);
|
||||
} else {
|
||||
put_char(row, col, ' ', current_attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void save_screen_region(int start_row, int start_col, int end_row, int end_col, uint16_t buffer[])
|
||||
{
|
||||
if (start_row < 0) start_row = 0;
|
||||
if (start_col < 0) start_col = 0;
|
||||
|
||||
uint16_t *dst = &buffer[0];
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
if (row >= SCREEN_HEIGHT) break;
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
if (col >= SCREEN_WIDTH) break;
|
||||
*dst++ = shadow_buffer[row][col].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void restore_screen_region(int start_row, int start_col, int end_row, int end_col, const uint16_t buffer[])
|
||||
{
|
||||
if (start_row < 0) start_row = 0;
|
||||
if (start_col < 0) start_col = 0;
|
||||
|
||||
const uint16_t *src = &buffer[0];
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
if (row >= SCREEN_HEIGHT) break;
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
if (col >= SCREEN_WIDTH) break;
|
||||
put_value(row, col, *src++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_char(int row, int col, char ch)
|
||||
{
|
||||
if (row < 0 || row >= SCREEN_HEIGHT) return;
|
||||
if (col < 0 || col >= SCREEN_WIDTH) return;
|
||||
|
||||
put_char(row, col, ch, (current_attr & 0x0f) | (shadow_buffer[row][col].attr & 0xf0));
|
||||
}
|
||||
Reference in New Issue
Block a user