VGA Artefacts & understanding planes
Posted: Thu Mar 02, 2017 1:09 am
Hey,
Recently I've been trying to get VGA drawing working (640x480 16-colours) and have been drawing from the VGA Hardware page on the wiki, Chris Giese's example code and osdever.net for help learning and understanding everything. My first attempts were at drawing single pixels to the screen using the method shown in Chris Giese's demo, but obviously that was hilariously slow and more of a demo of how the memory is addressed. So I began taking what I had learnt there and from other places and began putting together something. It mostly works, but there is an issue:
There are artefacts showing in the image. I've tried inspecting the appropriate memory locations in BOCHS and everything appears to be what I expected, but this is where I'm not sure my understanding of the planes is quite correct.
Plane 0 (0xA0000 - 0xAFFFF) is where bit 4 of the colour goes (brightness)
Plane 1 (also 0xA0000 - 0xAFFFF) is where bit 3 of the colour goes (red)
Plane 2 (also 0xB0000 - 0xB7FFF) is where bit 2 of the colour goes (green)
Plane 3 (also 0xB8000 - 0xBFFFF) is where bit 1 of the colour goes (blue)
The problem is I can quite understand what is going on with Plane 0 and Plane 1? Surely plane 1 should be at 0xa8000. However that just makes the situation worse. I also clear all of VGA memory before re-enabling the display, so I'm pretty certain its my code generating this noise.
What I'm doing is working in an offscreen buffer, where each pixel is 1 byte (wasteful I know, but more interested in getting it functional for now). Currently I'm trying to blit the entire thing to VGA memory at once (operating on runs of 8 pixels at a time.) The code is below:
The display struct is just a structure in which I keep some properties about the current state of the display, dimensions, off screen buffer and such. I'm pretty certain it's probably my understanding of VGA planes that is causing the issue. If anyone could point me in the right direction that would be appreciated.
Edit
I've just tried setting the screen to be red and this is the result that I get
Recently I've been trying to get VGA drawing working (640x480 16-colours) and have been drawing from the VGA Hardware page on the wiki, Chris Giese's example code and osdever.net for help learning and understanding everything. My first attempts were at drawing single pixels to the screen using the method shown in Chris Giese's demo, but obviously that was hilariously slow and more of a demo of how the memory is addressed. So I began taking what I had learnt there and from other places and began putting together something. It mostly works, but there is an issue:
There are artefacts showing in the image. I've tried inspecting the appropriate memory locations in BOCHS and everything appears to be what I expected, but this is where I'm not sure my understanding of the planes is quite correct.
Plane 0 (0xA0000 - 0xAFFFF) is where bit 4 of the colour goes (brightness)
Plane 1 (also 0xA0000 - 0xAFFFF) is where bit 3 of the colour goes (red)
Plane 2 (also 0xB0000 - 0xB7FFF) is where bit 2 of the colour goes (green)
Plane 3 (also 0xB8000 - 0xBFFFF) is where bit 1 of the colour goes (blue)
The problem is I can quite understand what is going on with Plane 0 and Plane 1? Surely plane 1 should be at 0xa8000. However that just makes the situation worse. I also clear all of VGA memory before re-enabling the display, so I'm pretty certain its my code generating this noise.
What I'm doing is working in an offscreen buffer, where each pixel is 1 byte (wasteful I know, but more interested in getting it functional for now). Currently I'm trying to blit the entire thing to VGA memory at once (operating on runs of 8 pixels at a time.) The code is below:
Code: Select all
static inline vga_set_plane(uint8_t p)
{
p &= 3;
uint8_t pmask = 1 << p;
outb(VGA_GC_INDEX, 4);
outb(VGA_GC_DATA, p);
outb(VGA_SEQ_INDEX, 2);
outb(VGA_SEQ_DATA, pmask);
}
static inline vga_blit_buffer_full(struct display *display)
{
static uint32_t plane_offsets[] = {0xa0000, 0xa8000, 0xb0000, 0xb8000};
static uint8_t c_mask[] = {0x1, 0x2, 0x4, 0x8};
static uint8_t c_shift[] = {0, 1, 2, 3};
uint32_t width_in_bytes = display->width / 8;
uint32_t buffer_offset = (uint32_t)display->buffer;
for (uint32_t y = 0; y < display->height; ++y) {
for (uint32_t x = 0; x < display->width; x += 8) {
uint8_t *pixel = (uint8_t *)buffer_offset;
uint32_t vga_offset = (width_in_bytes) * y + x / 8;
for (uint8_t p = 0; p < 4; ++p) {
vga_set_plane(p);
uint8_t chunk = 0;
uint8_t mask = 0x80;
for (uint32_t i = 0; i < 8; ++i) {
uint8_t c = ((*(pixel + i) & c_mask[p]) >> c_shift[p]);
if (c) {
chunk |= mask;
}
mask >>= 1;
}
*((uint8_t *)(plane_offsets[p] + vga_offset)) = chunk & 0xFF;
}
buffer_offset++;
}
}
}
Edit
I've just tried setting the screen to be red and this is the result that I get