我的puts函数不能用于16位代码

时间:2016-07-06 09:25:49

标签: gcc x86 bootloader 16-bit real-mode

我正在使用 GCC 和一个小的汇编程序引导程序编写一个引导加载程序。我编写了一个puts例程,使用BIOS中断将字符串打印到显示器上,这些中断似乎没有正确写入字符串。

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

.code16 .section .text

.extern main
.globl start
start:

    mov     $0x7c0,     %ax
    mov     %ax,        %ds
    mov     %ax,        %es
    mov     %ax,        %fs
    mov     %ax,        %gs

    jmp     main

here:
    hlt
    jmp here

main.c中的 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     $512,      %%sp\n" : : "a" (ss), "r" (size)\
        );                                                      \
}

#define set_videomode(mode)                                     \
{                                                               \
    __asm__ __volatile__ (                                      \
            "int $0x10\n" : : "a" (mode)                        \
        );                                                      \
}

void putchar_bios(unsigned char ch)
{
    __asm__ __volatile__ (
            "int $0x10\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标志。我使用了自定义链接描述文件。

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 中编译,链接和运行的脚本是:

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函数正确显示字符串?

1 个答案:

答案 0 :(得分:1)

我意识到我将段寄存器的值设置为某些垃圾(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

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