4

I'm writing a C program under Real Mode. The program will be loaded to 0x2000:0x0000 address and run. DS register equals to CS, which is 0x2000. Also I'm debugging with bochs.

My goal is to print some text on the screen. So that I need inline assembly (for BIOS INT 10h).

Here is my test file:

asm("jmp _main");

void putchar(c) char c;
{
    asm("mov ah, 0x0e");
    asm("mov al, c");
    asm("xor bx, bx");
    asm("int 0x10");
}

void main ()
{
    asm("push cs");
    asm("pop ds");
    putchar('A');
    for(;;);
}

When I compiled it with this command...

bcc -W -0 -c test.c -o test.obj

...it's working. But when I'm trying to link it with...

ld86 -d isimsiz.obj -o kernel.bin

...it gave me this error:

undefined symbol: c

Why is this happening? How can I use C variables under BCC In-Line Assembly?

If you know a good tutorial about BCC, please leave a link. I couldn't find it on the internet :(

Thanks in advance.

PS: Here are the man pages of the respective compiler BCC and linker LD86.

fuz
  • 88,405
  • 25
  • 200
  • 352
B. Kaan
  • 95
  • 1
  • 8
  • Also I forgot the mention. If you know a good tutorial about BCC leave a link. I couldn't find on the internet :( Thanks in advance.. – B. Kaan Sep 17 '16 at 09:26
  • What's with the `char c;` declared on the same line as `putchar`, and with the typeless variable `c`? That should (unless I'm mistaken, and it uses non-standard syntax) be `void putchar(char c)` – enhzflep Sep 17 '16 at 09:33
  • 1
    @enhzlep BCC uses K&R syntax. So parameter is correct. – B. Kaan Sep 17 '16 at 09:36
  • 1
    You can't do that in bcc. Instead, write the whole procedure in assembly. – fuz Sep 17 '16 at 09:39
  • @FUZxxl Do you mean there is no way to use C variables like that? – B. Kaan Sep 17 '16 at 09:41
  • @B.Kaan No, there isn't. – fuz Sep 17 '16 at 09:43
  • Huh, interesting. For what it's worth, you can use gcc when writing kernels. Furthermore, since you're in real mode, you can just write straight to video memory yourself. Just throw bytes at the 80x25x2 bytes found at B800:000 - 80 columns, 25 rows, 2 bytes per char - 1 for attribute, 1 for character. (http://www.shikadi.net/moddingwiki/B800_Text) You dont need ints and can do it all with C ;) I'm a masochist and just use asm. Loading transparent bitmaps was er, 'fun'. :) – enhzflep Sep 17 '16 at 09:44
  • 3
    @enhzflep gcc's support for real mode is, let's say, “limited.” – fuz Sep 17 '16 at 09:46
  • @FUZxxl - yep, I should have said that. I'd intended to convey the idea that a prot-mode kernel written in c can be achieved with gcc. It's also worth leveraging grub instead of writing a 1, 2 or 3 stage boot loader oneself. Notepad++, Nasm and (inter)Net - gimme these 3 and I'm good to go. – enhzflep Sep 17 '16 at 09:49
  • 1
    @enhzflep : _GCC_ doesn't happen to generate _C_ code to workable 16-bit code. It can generate 32-bit code (including stack access and memory addressing) that will run in 16-bit real mode (if you use the .code16gcc directive or compile with `-m16`). Because it generates 32-bit instructions, the code requires a 386+. If you happen to need to target 286/186/86 then the code won't run on those processors. _GCC_ has no concept of segment:offset addressing of 16-bit real mode and things like FAR pointers etc. – Michael Petch Sep 17 '16 at 13:35
  • 1
    I prefer OpenWatcom for true 16-bit _C_ code that needs to work with memory models other than the tiny model. BCC (Bruce's C Compiler) is fine for generating 16-bit real mode code running in 64kb (tiny model). last I looked BCC (unlike Openwatcom) doesn't support FAR pointers. – Michael Petch Sep 17 '16 at 13:36
  • @Michael Petch Well, I don't know how to use Open Watcom's compiler and linker. I mean my tryings end up with undefined references (ie: _cstart_ , _STK etc.). So that I choose BCC. But it's -like you said- works in tiny model. Could you tell me that how can I produce raw binary programs for real mode using Open Watcom? – B. Kaan Sep 17 '16 at 14:18
  • I usually use something like `wcc -s -wx -d0 -ms -zl -ecf bootload.c` where `bootload.c` is the name of your bootloader _C_ file of course. The problem after that is actually linking it in such a way that it can be used as a bootloader. That is where [JLOC linker](http://wiki.osdev.org/JLoc) (proprietary, but freely available) comes in. You need something like DOSEmu to run JLOC on Linux since it is a DOS program. – Michael Petch Sep 17 '16 at 14:42
  • With DOSEmu I usually run JLOC this way `dosemu -dumb JLOC.EXE boot.ld bootload.bin` where `boot.ld` is a simple linker script that looks like `ALL:` `bootload.o` `BOOT: 0 7C00 0` `*` – Michael Petch Sep 17 '16 at 14:48
  • And then to add the boot signature to the `bootload.bin` file you could do something like `printf "\x55\xaa" | dd of=bootload.bin bs=1 seek=510 conv=notrunc` – Michael Petch Sep 17 '16 at 15:05
  • @MichaelPetch Thank you so much. I'll play with JLOC and OpenWatcom then. – B. Kaan Sep 17 '16 at 15:16

1 Answers1

6

bcc doesn't support referring to C variables. You need to write the whole function in assembly:

void putchar(c)
{
#asm
     mov ah, 0x0e
     mov bx, sp
     mov al, [bx+2]
     xor bx, bx
     int 0x10
#endasm
}

You might also want to check if __FIRST_ARG_IN_AX__ is defined:

void putchar(c)
{
#asm
     mov ah, 0x0e
#if !__FIST_ARG_IN_AX__
     mov bx, sp
     mov al, [bx+2]
#endif
     xor bx, bx
     int 0x10
#endasm
}

Note that in K&R-style functions, function parameters can't have types narrower than int, so while void putchar(c) char c; is syntactically correct, you can't do that. Incidentally, this is why the libc function putchar takes an argument of type int.

If you really need to use variables, consider using a global variable:

unsigned equipment;
int has_floppy() {
#asm
    int 0x11 ! get BIOS equipment list
    mov _equipment,ax
#endasm

    return (_equipment & 1);
}
}

You can look at the dev86 libc for examples on inline assembly in bcc.

fuz
  • 88,405
  • 25
  • 200
  • 352