As a piece of advice before I address your question, I would recommend giving a link to your online repo, so we can go over any code you haven't posted here. I would even go so far as to suggest putting the link in your .sig for the forum, so it will be available whenever you post here.
If you don't have a repo yet, then I strongly urge you to drop everything else and set one up
immediately, for this project
and any other project you might be working on, no matter how small. I cannot stress enough how important version control is, for a variety of reasons including having an offsite backup of your code, as an audit trail for your changes, and as a way to make it easier to experiment with various changes to your code while ensuring that the main branch is untouched.
If nothing else, it makes it easy to let other see your code without having to post large code sections to the forum.
It doesn't really matter which VCS you use (whether it is Subversion, GIT, Bazaar, Mercurial, Team Foundation Server, whatever) or host (GitHub, Gitlab, CloudForge, etc.). What is important is to have
some system in place. Most of the major project hosts will serve FOSS projects for free.
Sorry for being so strident about this, but it is something which is too valuable a tool and a resource not to use.
Second, note that the forum software (phpBB, and sadly, a rather ancient version at that) does not retain text/code formatting by default - something which you will find is the case for nearly all message board software, though how they work around it to allow code samples to be posted varies. This is a common misunderstanding for newcomers to fora in general, so no harm, no foul.
In the case of phpBB, it uses what are called 'BBcode tags', which resemble HTML tags but use square brackets instead of angle brackets, and accept a different set of tags from base HTML. in this case, the [ code ] tag (without the spaces) opens a code section, and the [ /code ] tag (again, with the spaces removed) closes it. There is a 'code' button at the top over the editing window which you can use to add these automatically.
Unfortunately, the current forum doesn't have the modules to support color highlighting installed. Also, the forum sometimes has problems handling tabs in a helpful fashion, so it is often better to use bare spaces instead. That having been said, here is your code suitably formatted (though not, as I said, highlighted):
Code: Select all
void rect_filled1(int x, int y, int w, int h, uint32_t col, uint32_t *data) {
VideoTerminal terminal = GetTerminal();
uint32_t* lfb = (uint32_t*)data;
for(uint32_t k = 0; k < h; k++)
for(uint32_t j = 0; j < w; j++)
lfb[(w+x) + (h+y) *terminal.height] = col;
}
void Update(uint32_t *data) {
uint32_t *lfb = (uint32_t*)0x1000000;
for(int i = 0; i < 1024*720; i++) {
lfb[i] = data[i];
}
}
void InitializeBackBuffer() {
VideoTerminal vt = GetTerminal();
uintptr_t *e = (uintptr_t*)PmmngrAllocBlock();
size_t BytesToMap = vt.BytesPerScanLine * vt.height/4096;
for(int i = 0; i <= BytesToMap; i++) {
VmmngrMap((void*)(e + (i*4096)),(void*)(0xF00000000 + (i*4096)));
}
back = (uint32_t*)0xF00000000;
}
void CreateFrame(int x, int y, int width, int height) {
uint32_t buf = (uint32_t)0xF00000000;
rect_filled1(x,y,width,height,rgb(223,228,230), (uint32_t*)buf);
Update((uint32_t*)buf);
//VideoDrawLine(x,y,x-width,y,rgb(12,22,32));
}
void Main(Multiboot_info *info) {
InitializeVideo(info);
InitializeBackBuffer();
CreateFrame(10,20,300,100);
}
Given this now, we can see the structure of the code clearly, and I notice a few things right away. First, in the code as given, you are jumping into initializing the video immediately, rather than doing all of the other housekeeping one would normally tackle at this point (setting up the OS's own GDT, LDTs, and IDT to replace the default one from the bootloader, setting up a scheduler, etc.). Now, it is likely that all of this was done in the multiboot entry code, so I doubt it is relevant - especially since you stated that it was working correctly with double buffering disabled - but it does stand out. While we don't actually need to the full startup code in front of us, knowing where we could view it would help. This is part of why I was so adamant about having an online repo which you could link to us.
What we
do need, or at least what would be a significant help for us, is the working code, so we can compare them and try to work out why the double-buffering version fails. If you could please post the relevant functions from the working version (the ones which different from that above), or even just explain how they differ, it would help a great deal.
Some more things that I note are:
- You have both the address of the linear frame buffer and the back buffer in the code as 'magic numbers', that is to say, numeric constants. While you do assign both to local variables, this is one place where a named global constant may be better, even if you just end up assigning to the local variable in the same manner.
- On a related note, I am not clear what the three uses of '4096' in InitializeBackBuffer() are in reference to, or even if they are for the same or related purposes or not. In the first instance, you divide the terminal height (presumably 720 in this case, though you clearly intend it to work with any height of terminal), which (assuming that vt.height directly corresponds to the screen resolution, which I suppose it might not) is going to present a significant problem for an integer division. Also, why 0x1000 in the first place? I don't know enough about your particular terminal structure and representation to see the purpose of this, yet. Similar questions arise where you use the same constant in the virtual memory mapping call, though I get the impression that these do not actually have the same meaning as in the previous line. Could you explain these to us a bit, so we could see what the intent was?
- In CreateFrame(), was there any specific reason why you declared buf as a uint32_t, when both of the subsequent uses for it are cast to uint32_t*? It just seems to me that simply declaring it as uint32_t* would have been easier, but I don't know if there was some reason not to that I am missing.
I know that this is a relatively shallow analysis, but I really would need to see more of the code to say more, especially the difference between this and the working single-buffer code.
EDIT: Two quick questions, by any chance was the line covering terminal height meant to read:
Code: Select all
size_t BytesToMap = (vt.BytesPerScanLine * vt.height)/4096;
that is to say, dividing the product of the bytes per scan line and the height by 0x1000, rather than just the height? Also, is the 0x1000 in the later line
Code: Select all
VmmngrMap((void*)(e + (i*4096)),(void*)(0xF00000000 + (i*4096)));
a memory page size, and if so, are you certain that you are using the 32-bit protected mode default 4KiB pages, as opposed to the larger 4MiB pages (which can be used in p-mode depending on the setting for the page tables, and IIUC are pretty much standard for Long Mode)?