0

以下源文件分别组装(成原始二进制文件)并分别加载到虚拟软盘的扇区 1 和 2。然后,这张软盘用作 qemu-system-i386 VM 的引导介质。

“引导加载程序”从软盘的第 2 扇区读取“第一个程序”,然后跳转到包含刚刚读取的代码的内存。以下代码按需要工作(即打印“第一个程序”欢迎消息),但我必须ORG 0x001E在“第一个程序”的源中指定(通过在十六进制编辑器中检查引导加载程序的代码获得)。0x001E 是temp缓冲区的偏移量,它保存从软盘读取的代码。

“引导程序”:

BITS 16

bootloader_main:
    mov bx, 0x07C0      ; Set data segment to bootloader's default segment
    mov ds, bx


    mov ah, 0x02        ; BIOS int13h "read sector" function
    mov al, 1           ; Number of sectors to read
    mov cl, 2           ; Sector to read
    mov ch, 0           ; Cylinder/track
    mov dh, 0           ; Head
    mov dl, 0           ; Disk number (here, the floppy disk)
    mov bx, 0x07C0      ; Segment containing the destination buffer
    mov es, bx
    mov bx, temp        ; Destination buffer offset
    int 0x13

    jmp temp

    ret
;end bootloader_main




temp: times 60 db 17

times 510-($-$$) db 0       ; Pad rest of sector and add bootloader   
dw 0xAA55                      signature

“第一个程序”:

BITS 16
ORG 0x001E       ; Assume that this code will be located 0x001E bytes     
                   after start of bootloader (in RAM)

mov bx, string      ; Print a welcome string
mov ah, 0x0E
print_loop:
    mov al, byte [bx]
    int 0x10
    inc bx
    cmp byte [bx], 0
    jne print_loop
;end print_loop


string: db "This is the first program.", 0

或者,我可以使用ORG 0x200and0x200代替缓冲区temp(即在引导加载程序之后将程序加载到 RAM 中),但是在创建有用的操作系统时,这些黑客似乎都不可持续。如何避免这种地址硬编码?

4

2 回答 2

2

您可以通过使用段来避免对地址进行硬编码。在一个 16 的倍数的地址加载“第一个程序”,并用相应的段(地址/16)加载 DS,然后远跳转到加载程序的位置segment:0。在加载的程序中segment使用。ORG 0

例如:

BITS 16

bootloader_main:
    mov ax, 0x07C0      ; Set data segment to bootloader's default segment
    mov ds, ax

    mov ah, 0x02        ; BIOS int13h "read sector" function
    mov al, 1           ; Number of sectors to read
    mov cl, 2           ; Sector to read
    mov ch, 0           ; Cylinder/track
    mov dh, 0           ; Head
    mov bx, program_seg ; load program at program_seg:0
    mov es, bx
    xor bx, bx
    int 0x13

    mov ax, program_seg
    mov ds, ax
    mov ss, ax          ; set stack to end of program_seg
    mov sp, 0
    jmp program_seg:0

bootloader_end:
program_seg equ (bootloader_end - bootloader_main + 0x7c00 + 15) / 16

times 510-($-$$) db 0       ; Pad rest of sector and add bootloader   
dw 0xAA55                   ;   signature
BITS 16
ORG 0

mov bx, string      ; Print a welcome string
mov ah, 0x0E
print_loop:
    mov al, byte [bx]
    int 0x10
    inc bx
    cmp byte [bx], 0
    jne print_loop
;end print_loop


string: db "This is the first program.", 0

我删除了该mov dl, 0指令,因为您不应该硬编码这个值。BIOS 会在 DL 中传递引导设备的驱动器号,因此您无需更改它。

于 2016-08-08T23:41:00.510 回答
0

此示例允许您在 jmp Load_Buffer 和 Load_Buffer 之间输入任意数量的代码,只要它不会超出引导扇区的最后 4 个字节。

引导程序

    BOOTSEG     equ 0x7c0
    LOW_MEM     equ 18
    DISKIO      equ 19

确定有多少 4k 页可用并将其转换为段地址。就我而言,它是 0x8FC0。

xor     cx, cx
mov     cl, 64
int     LOW_MEM             ; Get # of 4k segments to top of memory
sub     ax, cx
shl     ax, 6                       ; 

修改堆栈指针时始终禁用中断。这将堆栈放在一个非常安全的位置,内存小于 64k

cli
mov     ss, ax
xor     sp, sp
sti

此时 CS 可能为 0 或可能为 0x7c0,具体取决于 bios 供应商,但这并不重要,因为此时只有 ES:BX 很重要

mov     ax, Load_Buffer
shr     ax, 4
add     ax, BOOTSEG
mov     es, ax  
mov     ax, 0x201                   ; Read one sector
mov     cx, 2                       ; Starting @ cylinder 0, sector 2
xor     dh, dh                  ; Head 0 and BIOS has already passed device.

如您的示例所示,此处的替代方法是使用 0x7c0 加载 ES,使用 Load_Buffer 加载 BX,但是在我们的示例中,ES = BOOTSEG + Load_Buffer / 16。

xor     bx, bx
int     DISKIO                  ; Read sector 2
jmp     Load_Buffer

页面边界对齐将保证加载器始终工作,无论在 Load_Buffer 上方添加多少,除非它碰巧超出了引导扇区的最后 4 个字节。对齐只需要在页面边界上,所以 16 也可以。

align   32
Load_Buffer:

用 NOP 的代码填充扇区可能是个好主意,以防代码跑掉,不会出现段错误。

times   508 - ($-$$)    db  144     ; NOP's 0x90
int     25                      ; Incase codes runs away we'll reboot
dw      0xAA55                  ; Boot sig (no required by some emulators)

第一个程序

打算使用 LODSB,这并不比您的方式更正确,而是使用更少的代码。

mov     si, string
push    es
pop     ds                      ; DS:SI now points to string
mov     ah, 14                  ; TTY output

Loop:
lodsb                           ; Read from DS:SI
test    al, 255             ; Test if these any of these bits are on.
jz      .Done
int     16
jmp     Loop

label 之前的时间段只是用于声明本地标签的 NASM 功能

.Done:
hlt
jmp     $

string  db  'This is the first program', 0
于 2016-08-31T06:23:27.730 回答