Search code examples
cdosx86-16real-modewatcom

Segmented far pointer allocation in 16bit x86 MS-DOS real mode


I'm trying to get my head around programming real mode MS-DOS in C. Using some old books on game programming as a starting point. The source code in the book is written for Microsoft C, but I'm trying to get it to compile under OpenWatcom v2. I've run into a problem early on, when trying to access a pointer to the start of VGA video memory.

#include <stdio.h>
#include <dos.h>

void Set_Video_Mode(int mode) {
    
    union REGS inregs, outregs;

    inregs.h.ah = 0; 
    inregs.h.al = (unsigned char) mode;

    int86(0x10, &inregs, &outregs);
}


int main(void)
{
    Set_Video_Mode(0x13);

    //the following line throws an error, without it the code compiles and runs
    char far *video_buffer = (char far *)0xA0000000L;

    while (!kbhit()) { };

    Set_Video_Mode(0x03);

    return 0;
} 

It's the far pointer assignment that throws the following errors:

VGA.C(33): Error! E1077: Missing '}'
VGA.C(33): Warning! W107: Missing return value for function 'main'
VGA.C(36): Error! E1099: Statement must be inside function. Probable cause: missing {

Which is kind of baffling, and seems like a macro definition gone wrong, or something...

When I try the code from the Wikipedia article on far pointers, with the same compiler:

#include <stdio.h>
int main() {
    char far *p = (char far *)0x55550005;
    char far *q = (char far *)0x53332225;
    *p = 80;
    (*p)++;
    printf("%d", *q);
    return 0;
}

It compiles. The compile command is wcl -bcl=dos source.c in both cases.

So I'm kind of stumped now, and can't seem to pinpoint the problem. I'm on the verge of throwing a few asterisks and brackets at random places to see if it sticks somewhere...


Solution

  • It appears your OpenWatcom C compiler is defaulting to using C89. In C89 variable declarations must be at the beginning of a block scope. In your case all your code and data is at function scope, so the variable has to be declared at the beginning of main before the code.

    Moving the variable declaration this way should be C89 compatible:

    int main(void)
    {
        char far *video_buffer = (char far *)0xA0000000L;
    
        Set_Video_Mode(0x13);
        while (!kbhit()) { };
        Set_Video_Mode(0x03);
    
        return 0;
    } 
    

    If using OpenWatcom 2.0 as you suggest you are, you should be able to compile with C99 mode by adding the option -za99 to the wcl options. In C99 you can place variable declarations in places other than top of block scope.


    When compiling as C89, Watcom extended C89 to allow C++ style comments the same way C99 supports it. This behaviour seem to be documented as:

    The Open Watcom C/16 and C/32 compilers support an extension for comments. The symbol // can be used at any point in a physical source line (except inside a character constant or string literal). Any characters from the // to the end of the line are treated as comment characters. The comment is terminated by the end of the line.

    I agree with the assessment that had the C++ style comments not been allowed, the compiler would have given a much better picture of what the real problem was. I too was fooled at first and it didn't occur to me right away that it was being compiled as C89 code. I had assumed since the // was accepted that it must have been C99.