我的 puts 函数在 16 位代码中不起作用

My puts function is not working in 16-bit code

我正在使用 GCC 和一个小型汇编程序 bootstrap 例程编写引导加载程序。我编写了一个 puts 例程,该例程使用 BIOS 中断将字符串打印到显示器上,但似乎无法正确写入字符串。

我的 bootstrap 汇编文件 boot.s 包含:

.code16 .section .text

.extern main
.globl start
start:

    mov     [=10=]x7c0,     %ax
    mov     %ax,        %ds
    mov     %ax,        %es
    mov     %ax,        %fs
    mov     %ax,        %gs

    jmp     main

here:
    hlt
    jmp here

我的 Cmain.c 中的代码是:

/*
 * A 16 bit bootloader.
 */

void putchar_bios(unsigned char ch);
void set_videomode(unsigned short mode);
void puts(char str[]);

#define set_stack(ss, size)                                     \
{                                                               \
    __asm__ __volatile__ (                                      \
            "mov     %%ax,      %%ss\n"                         \
            "mov     2,      %%sp\n" : : "a" (ss), "r" (size)\
        );                                                      \
}

#define set_videomode(mode)                                     \
{                                                               \
    __asm__ __volatile__ (                                      \
            "int [=11=]x10\n" : : "a" (mode)                        \
        );                                                      \
}

void putchar_bios(unsigned char ch)
{
    __asm__ __volatile__ (
            "int [=11=]x10\n" : : "a" (0x0E | ch)
        );
}

void puts(char *str)
{
    while(*str)
        putchar_bios(*str++);
}

void main()
{
    set_stack(0x07C0, 512);
    set_videomode(0x03);

    char name[] = "0001234567890";
    puts(name);

    //This works fine.
    //  for(i=0; i<15; i++)
    //      putchar_bios(name[i]);
    while(1);
}

我已经完全在汇编中成功地做到了这一点,但现在我正试图将它迁移到 GCC 。我正在使用交叉编译器 (i386-gcc) 并且还使用了 -m16 标志。我使用了自定义 linker 脚本。

OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
    . = 0x7C00;
    .text : AT(0x7C00) {
        *(.text);
    }
    .data : SUBALIGN(0) {
        *(.data);
        *(.rodata);
    }
    .bss : SUBALIGN(4) {
        __bss_start = .;
        *(.COMMON);
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
    }
    __bss_sizel = SIZEOF(.bss)>>2;
    __bss_sizeb = SIZEOF(.bss);

    /* Boot signature */
    .sig : AT(0x7DFE) {
        SHORT(0xaa55);
    }
}

我用来编译的脚本,QEMU中的link和运行是:

i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/boot.s
i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/main.c
i386-elf-ld -T link.ld -o b.bin -nostdlib --nmagic boot.o main.o
dd if=b.bin of=HD.img conv=notrunc
#add some noticable garbage to second sector since I also try to read it next
echo "This is the second sector..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................." | dd seek=512 bs=1 of=HD.img

qemu-system-i386 -hda HD.img # -boot a -s -S

为什么我的程序无法通过 puts 函数正确显示字符串?

我意识到我将段寄存器的值设置为一些垃圾 (0x7c0),这导致了这种情况的发生。我修改了我的汇编文件以将段寄存器清零。代码现在看起来像:

.code16
.section .text

.extern main
.globl start
start:

    xor     %ax,        %ax
    mov     %ax,        %ds
    mov     %ax,        %es
    mov     %ax,        %fs
    mov     %ax,        %gs

    jmp     main

here:
    hlt
    jmp here

我原以为编译器会自动初始化段寄存器,但事实并非如此。