加载引导加载程序的第二阶段时出现问题 and/or 将控制权转移给它

Problems loading second stage of a bootloader and/or transferring control to it

我的主引导记录代码:

;bit16                  ; 16bit by default
    org 0x7c00
    jmp short start
    nop
bsOEM   db "OS423 v.0.1"               ; OEM String

start:


;;cls
    mov ah,06h      ;Function 06h (scroll screen)
    mov al,0        ;Scroll all lines
    mov bh,0x0f     ;Attribute (lightgreen on blue) 
    mov ch,0        ;Upper left row is zero
    mov cl,0        ;Upper left column is zero
    mov dh,24       ;Lower left row is 24
    mov dl,79       ;Lower left column is 79
    int 10h         ;BIOS Interrupt 10h (video services)

;;print welcome msg
    mov ah,13h      ;Function 13h (display string), XT machine only
    mov al,1        ;Write mode is zero: cursor stay after last char
    mov bh,0        ;Use video page of zero
    mov bl,0x0f     ;Attribute (lightgreen on blue)
    mov cx,mlen     ;Character string length
    mov dh,0        ;Position on row 0
    mov dl,0        ;And column 0
    lea bp,[msg]    ;Load the offset address of string into BP, es:bp
                    ;Same as mov bp, msg 
    int 10h

;;load sector into memory & 5678h:1234h
    mov bx, 0x5678  ;segmented address
    mov es, bx      ;move segemented address to es
    mov bx,0x1234       ;base address to bx

    mov ah, 02      ;function read sectors
    mov al, 01      ;# of sectors to load
    mov ch, 00      ;track to read
    mov cl, 02      ;sector to read
    mov dh, 00      ;head to read
    mov dl, 00          ;drive number

    int 0x13            ;call interrupt 13 

    jmp 0x5678:0x1234       ;jump to memory address 


    int 20


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;variables;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg:  db 'Welcome to Pradox OS 0.1! Authored by Jiansong he', 10, 13, '$' 
mlen equ $-msg

padding times 510-($-$$) db 0       ;to make MBR 512 bytes
bootSig db 0x55, 0xaa       ;signature (optional)

我的 nasm 编译二进制文件并将其放入我的 .img 软盘的终端命令:

line1: 定义一个软盘,名称为boot.img,块大小512字节,一共2880字节

line2: 使用nasm编译器,将mbr.asm文件编译成二进制格式,存放在名为mbr.bin的文件中(这是我的主引导记录)

第 3 行:在 dt.bin

将 dt.asm 编译成二进制文件

line4:将mbr.bin的内容放入boot.img,块大小512,共放入1个块

第 5 行:将 dt.bin 的内容放入 boot.img,块大小 512,物理扇区 #2(逻辑扇区 #1)

dd if=/dev/zero of=boot.img bs=512 count=2880 

nasm -f bin mbr.asm -o mbr.bin

nasm -f bin dt.asm -o dt.bin 

dd if=mbr.bin of=boot.img bs=512 count=1 conv=notrunc

dd if=dt.bin of=boot.img bs=512 seek=1 count=1 conv=notrunc 

dt.asm中的代码:

[BITS 16]               ;Set code generation to 16 bit mode

ORG 0x5647:0x1234       ;set addressing to begin at 579b4H

startdt:
  ;call cls ;call routine to clear screen
  ;call dspmsg  ;call routine to display message

  call date
  call cvtmo
  call cvtday
  call cvtcent
  call cvtyear
  call dspdate

  call time
  call cvthrs
  call cvtmin
  call cvtsec
  call dsptime

  int 20h ;halt operation (VERY IMPORTANT!!!)


cls:             
  mov ah,06h    ;function 06h (Scroll Screen)
  mov al,0  ;scroll all lines
  mov bh,0x0f   ;Attribute (bright white on blue)
  mov ch,0  ;Upper left row is zero
  mov cl,0  ;Upper left column is zero
  mov dh,24 ;Lower left row is 24
  mov dl,79 ;Lower left column is 79
  int 10H   ;BIOS Interrupt 10h (video services)
  ret


dspmsg: 
  mov ah,13h    ;function 13h (Display String)
  mov al,1  ;Write mode is zero
  mov bh,0  ;Use video page of zero
  mov bl,0x0a   ;Attribute (bright white on bright blue)
  mov cx,mlen2  ;Character length
  mov dh,0  ;position on row 0
  mov dl,0  ;and column 0
  lea bp,[welcom]   ;load the offset address of string into BP

  int 10H
  ret

welcom: db 'jiansong Hes first Operating System :D',10,13,'$'
mlen2 equ $-welcom;

date:
;Get date from the system
mov ah,04h   ;function 04h (get RTC date)
int 1Ah     ;BIOS Interrupt 1Ah (Read Real Time Clock)
ret

;CH - Century
;CL - Year
;DH - Month
;DL - Day

cvtmo:
;Converts the system date from BCD to ASCII
mov bh,dh ;copy contents of month (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [dtfld + 1],bh
ret

cvtday:
mov bh,dl ;copy contents of day (dl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 3],bh
mov bh,dl
and bh,0fh
add bh,30h
mov [dtfld + 4],bh
ret

cvtcent:
mov bh,ch ;copy contents of century (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 6],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [dtfld + 7],bh
ret

cvtyear:
mov bh,cl ;copy contents of year (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [dtfld + 8],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [dtfld + 9],bh
ret

dtfld: db '00/00/0000'

dspdate:
;Display the system date
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0f ;Attribute
mov cx,10 ;Character string is 10 long
mov dh,4 ;position on row 4
mov dl,0 ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[dtfld] ;load the offset address of string into BP
int 10H
ret

time:
;Get time from the system
mov ah,02h
int 1Ah
ret

;CH - Hours
;CL - Minutes
;DH - Seconds

cvthrs:
;Converts the system time from BCD to ASCII
mov bh,ch ;copy contents of hours (ch) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld],bh
mov bh,ch
and bh,0fh
add bh,30h
mov [tmfld + 1],bh
ret

cvtmin:
mov bh,cl ;copy contents of minutes (cl) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 3],bh
mov bh,cl
and bh,0fh
add bh,30h
mov [tmfld + 4],bh
ret

cvtsec:
mov bh,dh ;copy contents of seconds (dh) to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmfld + 6],bh
mov bh,dh
and bh,0fh
add bh,30h
mov [tmfld + 7],bh
ret

tmfld: db '00:00:00'

dsptime:
;Display the system time
mov ah,13h ;function 13h (Display String)
mov al,0 ;Write mode is zero
mov bh,0 ;Use video page of zero
mov bl,0x0f;Attribute
mov cx,8 ;Character string is 8 long
mov dh,5 ;position on row 5
mov dl,0;and column 0
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,[tmfld] ;load the offset address of string into BP
int 10H
ret

int 20H

我的测试环境是 dosbox,我可以在屏幕上成功显示欢迎消息,但无法将另一个扇区加载到从 0x5647:0x1234 开始的内存中 谢谢

足够简单。

您不能仅使用 OEM 字符串启动引导扇区!
你必须跳过这个文本到真正的起点。
要么使用:

  • 3 字节近跳转
  • 一个 2 字节的短跳转后跟一个 nop 指令。

mov ch, 01      ;track to read
mov cl, 02      ;sector to read
mov dh, 01      ;head to read

如果要读取驱动器的第 2 个扇区,需要指定 Cylinder=0、Head=0 和 Sector=2

mov ch, 0      ;track to read
mov cl, 2      ;sector to read
mov dh, 0      ;head to read

您的引导消息可能不会显示,因为您没有设置 ES 段寄存器。鉴于 org 0x7C00 您需要设置 ES=0。你不能相信 BIOS 会为你做这件事。


您可能希望从程序中删除 int 20 指令。帮不了你。


与其使用 mov dl, 0 指定要加载的磁盘,不如使用 BIOS 首次调用引导扇区时获得的 DL 寄存器的内容。 像 DOSBOX 这样的模拟器可能会在这里使用一些特定的值!

由于其他答案中有很多问题没有涉及,我会提供一个新的答案。

我建议在另一个 中查看我的一般引导加载程序提示。特别是前几个技巧适用于此:

  1. When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.

  2. The direction flag used by lodsb, movsb etc could be either set or cleared. If the direction flag is set improperly SI/DI registers may be adjusted in the wrong direction. Use STD/CLD to set it to the direction you wish (CLD=forward/STD=backwards). In this case the code assumes forward movement so one should use CLD. More on this can be found in an instruction set reference

  3. When jumping to a kernel it is generally a good idea to FAR JMP to it so that it properly sets CS:IP to expected values. This can avoid problems with kernel code that may do absolute near JMPs and CALLs within the same segment.

在您的代码中,您的引导加载程序应该在 SS:SP 中设置一个堆栈指针。将它放在引导加载程序下方是合理的 0x0000:0x7c00。你应该在销毁它之前保存 DL 寄存器,因为它包含引导驱动器号。您可以在开始时将其压入堆栈,并在设置访问的磁盘相关例程时恢复它 int 13h。您不应该假设 ESDS(特别是 DS)被设置为值0. 由于您使用 0x7c00 的 ORG,因此段需要 0x0000。 (0x0000<<4)+0x7c00 = 物理地址 0x07c00.

要解决这些问题,您可以在 start 标签后添加这些行:

start:
    mov [bootdrv],dl;Save the boot drive passed in via DL to the bootloader
    xor ax,ax       ;Set ES and DS to zero since we use ORG 0x7c00
    mov es,ax
    mov ds,ax
    mov ss,ax       ;Set SS:SP to 0x0000:0x7c00 below bootloader
    mov sp,0x7c00
    cld             ;Set direction flag forward

您需要在 msg

之后添加一个 bootdrv 变量
bootdrv: db 0

在使用 int 13h 磁盘读取功能之前,您现在可以使用 bootdrv 中的值并在发出中断调用之前将其放入 DL 中。此行应替换为:

mov dl, 00      ;drive number

有:

mov dl,[bootdrv];Get the boot drive saved at start of bootloader

jmp 0x5678:0x1234 之后,您的引导加载程序中有 int 20。我相信你的意思是 int 20hJMP 永远不会 return 所以在它后面放置代码什么也做不了。但是,int 20h 是 DOS 中断,只有在 MS-DOS 系统从磁盘加载到内存后才可用。当然,您的磁盘上没有 MS-DOS。在引导加载程序(裸机)环境中根本不要使用 DOS 中断。


dt.asm 中,您遇到了一些问题。您 FAR JMP 到 0x5678:0x1234 处新加载的代码。在这样做时 CS 被设置为 0x5678。您需要手动设置 ESDS,您可以通过复制 CS[=98= 中的值来完成此操作] 到 DSES。您还需要设置适当的 ORG。在这种情况下,原点是从段 (0x5678) 开始的 0x1234,因此您必须使用 org 0x1234dt.asm 的顶部可以修改为:

BITS 16               ;Set code generation to 16 bit mode

ORG 0x1234            ;set origin point to 0x1234
mov ax, cs            ;copy CS to DS and ES
                      ;alternatively could have used mov ax, 0x5678
mov ds, ax
mov es, ax

startdt:

之前讨论的 int 20h 问题是 dt.asm 中的问题。删除它的所有匹配项。相反,您可以将处理器置于无限循环中。 dt.asm 中最后执行的代码是在 call dsptime return 之后执行的。在那之后 CALL 你可以用这样的东西来无限循环:

    jmp $

一个更可取的可以占用更少处理能力的无限循环是使用CLI, use a HLT指令关闭中断然后为了安全措施如果HLTreturns JMP 返回并再次执行 HLT(如果有 NMI Non-Maskable 中断可能会发生)。 HLT等待下一个中断发生。这是你经常看到的:

    cli
endloop:
    hlt
    jmp endloop

其他观察结果

它出现在您将参数发布到 INT 13h/AH=2 disk read function were incorrect. Sector numbers start at 1 and heads and cylinders are zero based. The best source of interrupt information is Ralph Brown's Interrupt List 的代码的第一个版本中,其中涵盖了 BIOS 和 MS-DOS 中断。如果您需要有关中断参数的信息,这是一个很好的参考。


我建议 BOCHS 用于调试引导加载程序。它有一个命令行调试器,可以理解实模式寻址,可以用来观察指令的执行、设置断点、显示寄存器、检查内存等。