Hello I'm trying to make a simple os, and I'm currently trying to do double buffering.
I have two arrays in the size of the screen and a function that copy the array that currently is being used to the memory of the screen and then swap the being used array to the second one.
And there is a function that clear the screen and set it to a specific color.
screen.c
static u8 *BUFFER = (u8*) 0xA0000;
u8 buffers[2][SCREEN_SIZE];
u8 current_buffer = 0;
#define CURRENT (buffers[current_buffer])
#define SWAP() (current_buffer = 1 - current_buffer)
void screen_swap(){
memcpy(BUFFER, CURRENT, SCREEN_SIZE);
SWAP();
}
void clear_screen(u8 color){ // set all the memory of the screen to one color
memset(&CURRENT, color, SCREEN_SIZE);
}
memory.c
void memset(void* src, u8 val, u32 len){
u8* ptr = (u8*)src;
while(len--){
*ptr++ = val;
}
}
void* memcpy(void* dst, void* src, u32 len){
u8 *d = (u8*)dst;
const u8 *s = (const u8*)src;
while (len-- > 0){
*d++ = *s++;
}
return dst;
}
When I try to run these functions the system keeps rebooting. For example:
clear_screen(COLOR(0,0,255));
screen_swap();
A link to my github repo for more context
The TL;DR is that you are blasting your program stack.
I added some debug
printfcalls using a macro:dbgprtmain)&buffers[0][0]is 0x5f60SCREEN_SIZEis 0xfa00buffersis 0x5f60-0x1f960memsetonbuffers, we write well beyond the address of the stack.The stack address in question (i.e. 0x7be8) is the stack address used during the boot process. In
main.asm, this is set with:At
jmpToKernel, we need to set theespregister to a value that doesn't conflict with anything (e.g.):Here is the [redacted] output of
make run:Here is a
git diffof what I changed:Edit: Removed this for space. See update below.
UPDATE:
Of course. I just grabbed that from my other "build your own OS" answer: How can I fix my VBE implementation for my OS?
That was a bit of a hack [and what the original author was doing].
At first, I was going to use the
makeprocess to define the address forboot.asm. Then, I realized a better way is to have the_startinkernel_entry.asmset it.kernel_entry.asmis the simplest way to go.While in the process of doing that, I was still having some trouble, so I changed some unrelated things to improve debugging:
printf-like function to allow (e.g.%d,%ld, and%lld) to supportint/long/long longnumbers (so I could print the timer value).printfto usestdarg.hmainso that it only changed the background color on initial key press (e.g. rising edge).Here is the "no crash" version of the code:
UPDATE #2:
Yes, you are quite correct. You don't need two
buffers. But, it's your code, so why did you do this? ;-)Just one is needed because filling
buffersand then "blitting" to the frame buffer (i.e. thememcpy) is [already] "double buffering"It would actually be faster with just one
buffers. After sending the first frame to the frame buffer, generating the second frame could be just a small incremental change tobuffersThis is similar to using a graphics library like
SDL2.buffers), draw into that surface. Or, just draw into the offscreen buffer with (e.g.)SDL_RenderDrawRectSDL_RenderPresent(like yourmemcpy).I've seen this in some other similar SO questions for this like the linked answer. The github repo linked to on that page is [AFAICT] no longer available. But, it had a buffer that was just text chars, it would change those, then use those to index into a font to produce the raster lines in the frame buffer.
Really, I don't know why, so the following is just speculation ...
Although not really true on modern systems, IIRC [and I could easily be wrong about this] access to the frame buffer could be slower than regular RAM on older systems. So, with two offscreen
buffers, we could domemcpyonly for the pixels that change frame-to-frame.Or, with multiple buffers, it could help with animation (loosely):
.giffile intobuffers[0]buffers[1]memcpyofbuffers[0]to H/W frame bufferSo, reading data from a file for frame
Ncould be overlapped with thememcpyfor frameN-1. This could be more efficient.