compile bootloader with open watcom for 286 cpu

157 Views Asked by At

im trying to build myself a small bootloader/program for my old 286. i started doing this with fasm and quickly grew tired of writing assembly. so i figured i want to write it in c instead, but the choice of c toolchains targeting 286 seems to be slim. i opted for open watcom 1.9. so far i managed to get something to compile, but the output is broken, and i dont know why.

init.asm, written to be compatible with watcoms assembler, initializes registers, jumps into the c function kernelMain and provides a simple print function:

.code INIT
extern kernelMain_ : proc

org 7c00h

;export functions
PUBLIC initMain
PUBLIC _print
initMain:
;init stack to use the 30kb before this boot sector
;init other segment registers
mov bp,7C00h ; stack base pointer
xor ax,ax ; ax = 0
mov ds,ax ; data segment = 0
mov es,ax ; extra segment
mov ss,ax ; stack segment = 0
mov sp,bp ; stack pointer = 7C00h

jmp kernelMain_

;void __cdecl print(char* message, unsigned char length);
_print:
  pop si ; message
  pop cx ; length

printLoop: ; print string which is at adress si with length cx
  mov ah,0Eh ; TTY output function number
  mov bh,0 ; page 0
  mov bl,01h ; foreground color
  lodsb ; loads byte from [si] into al and advances pointer in si by 1
  int 10h ; BIOS video interrupt, write byte to screen
  loop printLoop ; loop till message is printed
  ret

end

test.c, contains the kernelMain function:

extern void __cdecl print(char* message, unsigned short length);


void kernelMain(void)
{
  print("this is a test", 14);
}

and this is what i use to build it

wasm -2 -ms init.asm
wcc -2 -d0 -wx -ms -s -zl test.c
wlink file init.obj file test.obj format raw bin name test.bin option NODEFAULTLIBS,verbose,start=initMain ORDER clname CODE segment INIT

the resulting binary is ~32KB instead of the expected few bytes because the linker interprets the org instruction to actually fill everything with 0 bytes before the code. removing the 0 padding before the code breaks the code completely. it doesnt print anything. removing the org instruction gives me code that executes and prints, but it also prints out some random garbage before the test message

can someone help me?

1

There are 1 best solutions below

6
On

thanks to the commenters i now got what i wanted.

fixes:

  1. abandon the asm seperate from c code idea and only use c with inline assembly
  2. upgrade to open watcom 2.0 beta from here https://github.com/open-watcom/open-watcom-v2/releases (current build contains builds and installers)
  3. add offset=0x7c00 option

test.c:

void kernelMain(void);
void initasm(void);
#pragma aux initasm = \
"mov bp,7C00h", \
"xor ax,ax", \
"mov ds,ax", \
"mov es,ax", \
"mov ss,ax", \
"mov sp,bp", \
"jmp kernelMain" \
modify [ AX ];

void __pascal print(char* message, unsigned short length);
#pragma aux print = \
"pop cx", \
"pop si", \
"printLoop:", \
"mov ah,0Eh", \
"mov bh,0", \
"mov bl,01h",\
"lodsb", \
"int 10h", \
"loop printLoop" \
modify [ SI CX AH BH BL];

void init(){
  initasm(); //jumps to kernelMain after initializing registers. seperate init was necessary as adding any actual function calls (like print) would add push instructions before the inline assembly which would be called before initializing the stack and registers
}

void kernelMain(void)
{
  print("this is a test", 14);
}

buildtest.bat:

wcc -2 -d0 -wx -ms -s -zl test.c
wlink file test.obj format raw bin name test.bin option NODEFAULTLIBS,verbose,start=init_,OFFSET=0x7C00

this gives me a raw binary linked at adress 0x7c00.

to make a bootable sector that can be written to a floppy(or be used by a virtual machine as floppy) you have to 0 pad the rest of the file to 510 bytes length, and add 0x55 0xAA as the last 2 bytes,(for a total of 512 bytes) which mark it as a bootable sector.

remarks: the seperate asm and c thing would probably still work as intended if the author (me) could actually write proper assembly