mirror of https://gitlab.com/nakst/essence
268 lines
8.4 KiB
C++
268 lines
8.4 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
#include <module.h>
|
|
#include <arch/x86_pc.h>
|
|
|
|
struct VideoModeInformation {
|
|
uint8_t valid : 1, edidValid : 1;
|
|
uint8_t bitsPerPixel;
|
|
uint16_t widthPixels, heightPixels;
|
|
uint16_t bytesPerScanlineLinear;
|
|
uint64_t bufferPhysical;
|
|
uint8_t edid[128];
|
|
};
|
|
|
|
VideoModeInformation *vbeMode;
|
|
uint32_t screenWidth, screenHeight, strideX, strideY;
|
|
volatile uint8_t *linearBuffer;
|
|
|
|
#if 0
|
|
float colorBlindnessMatrix[3][9] = {
|
|
{
|
|
// Protanopia.
|
|
0.171, 0.829, 0,
|
|
0.171, 0.829, 0,
|
|
-0.005, 0.005, 1,
|
|
},
|
|
|
|
{
|
|
// Deuteranopia.
|
|
0.330, 0.670, 0,
|
|
0.330, 0.670, 0,
|
|
-0.028, 0.028, 1,
|
|
},
|
|
|
|
{
|
|
// Tritanopia.
|
|
1, 0.127, -0.127,
|
|
0, 0.874, 0.126,
|
|
0, 0.874, 0.126,
|
|
},
|
|
};
|
|
|
|
// #define SIMULATE_COLOR_BLINDNESS (1)
|
|
#endif
|
|
|
|
void UpdateScreen_32_XRGB(K_USER_BUFFER const uint8_t *source, uint32_t sourceWidth, uint32_t sourceHeight, uint32_t sourceStride,
|
|
uint32_t destinationX, uint32_t destinationY) {
|
|
GraphicsUpdateScreen32(source, sourceWidth, sourceHeight, sourceStride,
|
|
destinationX, destinationY, screenWidth, screenHeight, strideY, linearBuffer);
|
|
}
|
|
|
|
void DebugPutBlock_32_XRGB(uintptr_t x, uintptr_t y, bool toggle) {
|
|
GraphicsDebugPutBlock32(x, y, toggle, screenWidth, screenHeight, strideY, linearBuffer);
|
|
}
|
|
|
|
void DebugClearScreen_32_XRGB() {
|
|
GraphicsDebugClearScreen32(screenWidth, screenHeight, strideY, linearBuffer);
|
|
}
|
|
|
|
void UpdateScreen_24_RGB(K_USER_BUFFER const uint8_t *source, uint32_t sourceWidth, uint32_t sourceHeight, uint32_t sourceStride,
|
|
uint32_t destinationX, uint32_t destinationY) {
|
|
GraphicsUpdateScreen24(source, sourceWidth, sourceHeight, sourceStride,
|
|
destinationX, destinationY, screenWidth, screenHeight, strideY, linearBuffer);
|
|
}
|
|
|
|
void DebugPutBlock_24_RGB(uintptr_t x, uintptr_t y, bool toggle) {
|
|
if (toggle) {
|
|
linearBuffer[y * strideY + x * 3 + 0] += 0x4C;
|
|
linearBuffer[y * strideY + x * 3 + 1] += 0x4C;
|
|
linearBuffer[y * strideY + x * 3 + 2] += 0x4C;
|
|
} else {
|
|
linearBuffer[y * strideY + x * 3 + 0] = 0xFF;
|
|
linearBuffer[y * strideY + x * 3 + 1] = 0xFF;
|
|
linearBuffer[y * strideY + x * 3 + 2] = 0xFF;
|
|
}
|
|
|
|
linearBuffer[(y + 1) * strideY + (x + 1) * 3 + 0] = 0;
|
|
linearBuffer[(y + 1) * strideY + (x + 1) * 3 + 1] = 0;
|
|
linearBuffer[(y + 1) * strideY + (x + 1) * 3 + 2] = 0;
|
|
}
|
|
|
|
void DebugClearScreen_24_RGB() {
|
|
for (uintptr_t i = 0; i < screenWidth * screenHeight * 3; i += 3) {
|
|
linearBuffer[i + 2] = 0x18;
|
|
linearBuffer[i + 1] = 0x7E;
|
|
linearBuffer[i + 0] = 0xCF;
|
|
}
|
|
}
|
|
|
|
void InitialiseVBE(KDevice *parent) {
|
|
if (KGraphicsIsTargetRegistered()) {
|
|
return;
|
|
}
|
|
|
|
vbeMode = (VideoModeInformation *) MMMapPhysical(MMGetKernelSpace(), 0x7000 + GetBootloaderInformationOffset(),
|
|
sizeof(VideoModeInformation), ES_FLAGS_DEFAULT);
|
|
|
|
if (!vbeMode->valid) {
|
|
return;
|
|
}
|
|
|
|
if (vbeMode->edidValid) {
|
|
for (uintptr_t i = 0; i < 128; i++) {
|
|
EsPrint("EDID byte %d: %X.\n", i, vbeMode->edid[i]);
|
|
}
|
|
}
|
|
|
|
KGraphicsTarget *target = (KGraphicsTarget *) KDeviceCreate("VBE", parent, sizeof(KGraphicsTarget));
|
|
|
|
linearBuffer = (uint8_t *) MMMapPhysical(MMGetKernelSpace(), vbeMode->bufferPhysical,
|
|
vbeMode->bytesPerScanlineLinear * vbeMode->heightPixels, MM_REGION_WRITE_COMBINING);
|
|
screenWidth = target->screenWidth = vbeMode->widthPixels;
|
|
screenHeight = target->screenHeight = vbeMode->heightPixels;
|
|
strideX = vbeMode->bitsPerPixel >> 3;
|
|
strideY = vbeMode->bytesPerScanlineLinear;
|
|
|
|
if (vbeMode->bitsPerPixel == 32) {
|
|
target->updateScreen = UpdateScreen_32_XRGB;
|
|
target->debugPutBlock = DebugPutBlock_32_XRGB;
|
|
target->debugClearScreen = DebugClearScreen_32_XRGB;
|
|
} else {
|
|
target->updateScreen = UpdateScreen_24_RGB;
|
|
target->debugPutBlock = DebugPutBlock_24_RGB;
|
|
target->debugClearScreen = DebugClearScreen_24_RGB;
|
|
}
|
|
|
|
// TODO Other color modes.
|
|
|
|
KRegisterGraphicsTarget(target);
|
|
}
|
|
|
|
#if 0
|
|
|
|
uint8_t vgaMode18[] = {
|
|
0xE3, 0x03, 0x01, 0x08, 0x00, 0x06, 0x5F, 0x4F,
|
|
0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C,
|
|
0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3, 0xFF, 0x00,
|
|
0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF,
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
|
|
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
|
|
0x01, 0x00, 0x0F, 0x00, 0x00,
|
|
};
|
|
|
|
volatile uint8_t *vgaAddress;
|
|
|
|
#define VGA_SCREEN_WIDTH (640)
|
|
#define VGA_SCREEN_HEIGHT (480)
|
|
|
|
uint8_t egaPaletteConverter[4][64] = {
|
|
{ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1,
|
|
0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, },
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1,
|
|
0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, },
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, },
|
|
{ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1,
|
|
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, },
|
|
};
|
|
|
|
void VGAUpdateScreen(uint8_t *_source, uint8_t *modifiedScanlineBitset, KModifiedScanline *modifiedScanlines) {
|
|
for (int plane = 0; plane < 4; plane++) {
|
|
uint8_t *source = _source;
|
|
|
|
ProcessorOut8(IO_VGA_SEQ_INDEX, 2);
|
|
ProcessorOut8(IO_VGA_SEQ_DATA, 1 << plane);
|
|
|
|
for (uintptr_t y_ = 0; y_ < VGA_SCREEN_HEIGHT / 8; y_++) {
|
|
if (modifiedScanlineBitset[y_] == 0) {
|
|
source += VGA_SCREEN_WIDTH * 32;
|
|
continue;
|
|
}
|
|
|
|
for (uintptr_t y = 0; y < 8; y++) {
|
|
uint8_t *sourceStart = source;
|
|
|
|
if ((modifiedScanlineBitset[y_] & (1 << y)) == 0) {
|
|
source += VGA_SCREEN_WIDTH * 4;
|
|
continue;
|
|
}
|
|
|
|
KModifiedScanline *scanline = modifiedScanlines + y + (y_ << 3);
|
|
|
|
uintptr_t x = scanline->minimumX & ~7;
|
|
source += 4 * x;
|
|
|
|
while (x < scanline->maximumX) {
|
|
uint8_t v = 0;
|
|
|
|
for (int i = 7; i >= 0; i--) {
|
|
if (egaPaletteConverter[plane][((source[0] >> 6) & 3) | ((source[1] >> 4) & 12) | ((source[2] >> 2) & 48)]) {
|
|
v |= 1 << i;
|
|
}
|
|
|
|
source += 4;
|
|
}
|
|
|
|
vgaAddress[(y + y_ * 8) * 80 + (x >> 3)] = v;
|
|
x += 8;
|
|
}
|
|
|
|
source = sourceStart + VGA_SCREEN_WIDTH * 4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGAPutBlock(uintptr_t x, uintptr_t y, bool toggle) {
|
|
for (int plane = 0; plane < 4; plane++) {
|
|
ProcessorOut8(IO_VGA_SEQ_INDEX, 2);
|
|
ProcessorOut8(IO_VGA_SEQ_DATA, 1 << plane);
|
|
|
|
if (toggle) {
|
|
vgaAddress[y * 80 + x / 8] ^= 1 << (7 - (x & 7));
|
|
} else {
|
|
vgaAddress[y * 80 + x / 8] |= 1 << (7 - (x & 7));
|
|
}
|
|
}
|
|
}
|
|
|
|
void VGAClearScreen() {
|
|
for (int plane = 0; plane < 4; plane++) {
|
|
ProcessorOut8(IO_VGA_SEQ_INDEX, 2);
|
|
ProcessorOut8(IO_VGA_SEQ_DATA, 1 << plane);
|
|
EsMemoryZero((void *) vgaAddress, VGA_SCREEN_WIDTH / 8 * VGA_SCREEN_HEIGHT);
|
|
}
|
|
}
|
|
|
|
void InitialiseVGA(KDevice *parent) {
|
|
if (KGraphicsIsTargetRegistered()) {
|
|
return;
|
|
}
|
|
|
|
vgaAddress = (uint8_t *) MMMapPhysical(MMGetKernelSpace(), 0xA0000, 0x10000, MM_REGION_WRITE_COMBINING);
|
|
uint8_t *registers = vgaMode18;
|
|
ProcessorOut8(IO_VGA_MISC_WRITE, *registers++);
|
|
for (int i = 0; i < 5; i++) { ProcessorOut8(IO_VGA_SEQ_INDEX, i); ProcessorOut8(IO_VGA_SEQ_DATA, *registers++); }
|
|
ProcessorOut8(IO_VGA_CRTC_INDEX, 0x03);
|
|
ProcessorOut8(IO_VGA_CRTC_DATA, ProcessorIn8(IO_VGA_CRTC_DATA) | 0x80);
|
|
ProcessorOut8(IO_VGA_CRTC_INDEX, 0x11);
|
|
ProcessorOut8(IO_VGA_CRTC_DATA, ProcessorIn8(IO_VGA_CRTC_DATA) & ~0x80);
|
|
registers[0x03] |= 0x80;
|
|
registers[0x11] &= ~0x80;
|
|
for (int i = 0; i < 25; i++) { ProcessorOut8(IO_VGA_CRTC_INDEX, i); ProcessorOut8(IO_VGA_CRTC_DATA, *registers++); }
|
|
for (int i = 0; i < 9; i++) { ProcessorOut8(IO_VGA_GC_INDEX, i); ProcessorOut8(IO_VGA_GC_DATA, *registers++); }
|
|
for (int i = 0; i < 21; i++) { ProcessorIn8(IO_VGA_INSTAT_READ); ProcessorOut8(IO_VGA_AC_INDEX, i); ProcessorOut8(IO_VGA_AC_WRITE, *registers++); }
|
|
ProcessorIn8(IO_VGA_INSTAT_READ);
|
|
ProcessorOut8(IO_VGA_AC_INDEX, 0x20);
|
|
|
|
KGraphicsTarget *target = (KGraphicsTarget *) KDeviceCreate("VGA", parent, sizeof(KGraphicsTarget));
|
|
target->screenWidth = VGA_SCREEN_WIDTH;
|
|
target->screenHeight = VGA_SCREEN_HEIGHT;
|
|
target->updateScreen = VGAUpdateScreen;
|
|
target->debugPutBlock = VGAPutBlock;
|
|
target->debugClearScreen = VGAClearScreen;
|
|
target->reducedColors = true;
|
|
// TODO Debug callbacks.
|
|
KRegisterGraphicsTarget(target);
|
|
}
|
|
|
|
#endif
|
|
|
|
KDriver driverSVGA = {
|
|
.attach = InitialiseVBE,
|
|
};
|