Search code examples
c++textbitmapvga

Why won't my code draw characters to screen?


I'm working on a little operating system, and I've gotten to the part where I want to draw characters to screen. I'm having issues getting my code to run (which might just be due to my rusty C++ skills), and when I do, I don't see the requested character on screen. I copied an 8x8 ASCII character bitmap, and tried to use code from OSDev Wiki as a base for my text-displaying function. Here's my C++ code so far:

#define LOCHAR 32
#define HICHAR 127
#define FONT_HEIGHT 8
//bitmap
unsigned char font[HICHAR-LOCHAR+1][FONT_HEIGHT] = {
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x02 },{ 0x00,0x00,0x00,0x00,0x05,0x05,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x06,0x0F,0x0F,0x06 },{ 0x00,0x00,0x00,0x00,0x02,0x07,0x07,0x02 },{ 0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x09 },{ 0x00,0x00,0x00,0x00,0x03,0x0D,0x07,0x0B },{ 0x00,0x00,0x00,0x00,0x04,0x02,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x04,0x02,0x02,0x04 },{ 0x00,0x00,0x00,0x00,0x02,0x04,0x04,0x02 },{ 0x00,0x00,0x00,0x00,0x02,0x07,0x02,0x05 },{ 0x00,0x00,0x00,0x00,0x00,0x02,0x07,0x02 },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x04 },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00 },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 },{ 0x00,0x00,0x00,0x00,0x04,0x04,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x03,0x02,0x02,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x04,0x03,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x06,0x04,0x07 },{ 0x00,0x00,0x00,0x00,0x05,0x05,0x07,0x04 },{ 0x00,0x00,0x00,0x00,0x07,0x03,0x04,0x07 },{ 0x00,0x00,0x00,0x00,0x03,0x01,0x07,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x04,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x06,0x07,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x07,0x04,0x06 },{ 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x02 },{ 0x00,0x00,0x00,0x00,0x02,0x00,0x06,0x04 },{ 0x00,0x00,0x00,0x00,0x00,0x04,0x02,0x04 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x07 },{ 0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x02 },{ 0x00,0x00,0x00,0x00,0x07,0x06,0x00,0x02 },{ 0x00,0x00,0x00,0x00,0x0F,0x09,0x0A,0x0F },{ 0x00,0x00,0x00,0x00,0x06,0x05,0x07,0x05 },{ 0x00,0x00,0x00,0x00,0x03,0x07,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x06,0x01,0x01,0x07 },{ 0x00,0x00,0x00,0x00,0x03,0x05,0x05,0x03 },{ 0x00,0x00,0x00,0x00,0x07,0x03,0x01,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x03,0x01,0x01 },{ 0x00,0x00,0x00,0x00,0x06,0x01,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x05,0x05,0x07,0x05 },{ 0x00,0x00,0x00,0x00,0x07,0x02,0x02,0x07 },{ 0x00,0x00,0x00,0x00,0x06,0x04,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x05,0x03,0x03,0x05 },{ 0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x07,0x07,0x05 },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x05,0x05 },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x07,0x01 },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x07,0x0F },{ 0x00,0x00,0x00,0x00,0x07,0x05,0x03,0x05 },{ 0x00,0x00,0x00,0x00,0x07,0x01,0x06,0x07 },{ 0x00,0x00,0x00,0x00,0x07,0x02,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x05,0x05,0x03,0x01 },{ 0x00,0x00,0x00,0x00,0x05,0x07,0x07,0x07 },{ 0x00,0x00,0x00,0x00,0x05,0x02,0x05,0x05 },{ 0x00,0x00,0x00,0x00,0x05,0x07,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x07,0x04,0x02,0x07 },{ 0x00,0x00,0x00,0x00,0x06,0x02,0x02,0x06 },{ 0x00,0x00,0x00,0x00,0x02,0x02,0x04,0x04 },{ 0x00,0x00,0x00,0x00,0x06,0x04,0x04,0x06 },{ 0x00,0x00,0x00,0x00,0x02,0x05,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F },{ 0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x00 },{ 0x00,0x00,0x00,0x00,0x00,0x06,0x07,0x07 },{ 0x00,0x00,0x00,0x00,0x01,0x07,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x00,0x06,0x01,0x07 },{ 0x00,0x00,0x00,0x00,0x04,0x07,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x03 },{ 0x00,0x00,0x00,0x00,0x06,0x02,0x07,0x02 },{ 0x00,0x00,0x00,0x00,0x06,0x07,0x04,0x03 },{ 0x00,0x00,0x00,0x00,0x01,0x07,0x05,0x05 },{ 0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x02,0x00,0x02,0x03 },{ 0x00,0x00,0x00,0x00,0x01,0x05,0x03,0x05 },{ 0x00,0x00,0x00,0x00,0x03,0x02,0x02,0x06 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x05 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x05,0x05 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x02,0x05,0x03,0x01 },{ 0x00,0x00,0x00,0x00,0x02,0x05,0x06,0x04 },{ 0x00,0x00,0x00,0x00,0x00,0x07,0x01,0x01 },{ 0x00,0x00,0x00,0x00,0x00,0x06,0x02,0x03 },{ 0x00,0x00,0x00,0x00,0x02,0x06,0x02,0x06 },{ 0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x07 },{ 0x00,0x00,0x00,0x00,0x00,0x05,0x07,0x02 },{ 0x00,0x00,0x00,0x00,0x00,0x05,0x07,0x07 },{ 0x00,0x00,0x00,0x00,0x00,0x05,0x02,0x05 },{ 0x00,0x00,0x00,0x00,0x00,0x05,0x06,0x03 },{ 0x00,0x00,0x00,0x00,0x00,0x03,0x02,0x06 },{ 0x00,0x00,0x00,0x00,0x06,0x03,0x02,0x06 },{ 0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02 },{ 0x00,0x00,0x00,0x00,0x03,0x06,0x02,0x03 },{ 0x00,0x00,0x00,0x00,0x04,0x07,0x01,0x00 }};

void draw_pixel(int pos_x, int pos_y, unsigned char colour)
{
    unsigned char* location = (unsigned char*)0xA0000 + 320 * pos_y + pos_x;
    *location = colour;
}
 
void draw_char(unsigned char c, int x, int y, int color)
{
    int cx,cy;
    int mask[8]={1,2,4,8,16,32,64,128};
    unsigned char *glyph[8]=font[(int)c-LOCHAR];
 
    for(cy=0;cy<8;cy++){
        for(cx=0;cx<8;cx++){
            if((int)glyph[cy]&mask[cx]==true){
                draw_pixel(color,x+cx,y+cy-12);
            }
        }
    }
}

extern "C" void main(){

    for(int h = 0; h<200; h++)
    {
        for(int w = 0; w<320; w++)
        {    
            draw_pixel(w,h,h);
        }
    }
    draw_char(97, 100, 100, 4);
    
    return;
}

As for troubleshooting I've done so far, I've modified the original 'draw_char' function to suit my needs as best I can, but with my little experience with C++, I'm sure I just made it worse.


Solution

  • You are misunderstanding operator precedence. The following statement is not what you expect:

    if((int)glyph[cy] & mask[cx] == true) ...
    

    Aggressively isolating all operators with parentheses, this translates to:

    ((int)(glyph[cy])) & ( (mask[cx]) == true )
    

    In other words, the value mask[cx] == true is being calculated first and then used for the bitwise-AND. That's because the operator == has a higher precedence than &.

    You could do this if you must (but don't, and read on):

    if (((int)glyph[cy] & mask[cx]) == true) ...
    

    The problem here is now you're directly comparing an integer to a boolean, relying on the compiler to explicitly cast. Not only that, but you're comparing to a true value which is something we tend to avoid (historically, any non-zero value is considered true). You will find that most production code either omits boolean comparisons altogether, or will always favor comparison phrased with respect to false values.

    That might sound pedantic, and if so then that's unfortunate but also fine. Here's the upshot though... All you actually need is this:

    if (glyph[cy] & mask[cx]) ...
    

    Now, let's look at glyph. I really don't know why your compiler isn't screaming at you here. It's wrong, and is likely also responsible for your program behaving weird. You've defined it as follows:

    // incorrect: you didn't want an array of 8 pointers!!
    unsigned char *glyph[8] = font[(int)c - LOCHAR];
    

    The correct definition is:

    const unsigned char *glyph = font[(int)c - LOCHAR];
    

    A final note on your mask table. It's not really necessary, unless you plan on doing something fancy with it. You can just as easily implement it with shifts:

    if (glyph[cy] & ((unsigned char)1U << cx)) ...