使用Open Watcom为286 CPU编译Bootloader遇问题求助
问题解决:Open Watcom 286 Bootloader 二进制过大与垃圾输出问题
问题根源分析
你遇到的两个核心问题:
- 二进制文件过大:Watcom汇编器的
org 7c00h会让链接器认为代码需要从0x7c00地址开始,因此在二进制文件开头填充大量0字节到该地址,导致文件接近32KB。 - 垃圾输出:去掉
org后,段寄存器ds=0,但C代码编译出的字符串指针是偏移地址(从0开始),实际字符串加载在物理地址0x7c00+偏移处,导致读取错误内存;同时_print函数的参数获取逻辑错误,直接弹出栈顶的返回地址当作参数,破坏了程序执行流程。
修复方案
1. 修正汇编代码(init.asm)
- 移除
org 7c00h,调整段寄存器设置,让ds指向0x07c0(对应物理地址0x7c00,即BIOS加载bootloader的位置),这样偏移地址直接对应实际物理位置。 - 修复
_print函数的参数获取逻辑,使用bp寄存器安全访问栈参数,避免破坏返回地址。 - 添加bootloader必备的512字节填充与0x55aa签名。
修改后的init.asm:
.code INIT extern kernelMain_ : proc ;export functions PUBLIC initMain PUBLIC _print initMain: ; 初始化段寄存器,ds=0x07c0对应物理地址0x7c00(0x07c0*16=0x7c00) mov ax, 07C0h mov ds, ax mov es, ax mov ss, ax mov sp, 0FFFFh ; 栈指针设为段末尾,向下增长,避免覆盖bootloader mov bp, sp jmp kernelMain_ ;void __cdecl print(char* message, unsigned short length); _print: push bp ; 保存基址指针 mov bp, sp ; 用bp访问栈参数 mov si, [bp+6] ; 获取第一个参数message(__cdecl从右往左压栈,message在bp+6) mov cx, [bp+4] ; 获取第二个参数length printLoop: mov ah, 0Eh ; BIOS TTY输出功能号 mov bh, 0 ; 视频页0 mov bl, 01h ; 前景色(蓝色) lodsb ; 从[si]读取字节到al,si自增1 int 10h ; 调用BIOS视频中断 loop printLoop ; cx减1,非0则循环 pop bp ; 恢复基址指针 ret ; 返回,__cdecl由调用者清理栈 ; 填充到512字节,添加bootloader签名0x55aa REPEAT 510 - ($ - INIT) DB 0 ENDM DW 0AA55h end
2. 保持C代码(test.c)不变
extern void __cdecl print(char* message, unsigned short length); void kernelMain(void) { print("this is a test", 14); }
3. 构建命令不变
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
效果说明
- 生成的
test.bin大小为512字节,符合bootloader要求,不再有多余的0填充。 - 程序运行时,
ds=0x07c0让C代码的字符串指针直接指向加载后的物理地址,_print函数正确读取参数与字符串,输出正常内容。
内容的提问来源于stack exchange,提问作者fredlllll
相关产品推荐
相关产品推荐

