【问题标题】:Problems loading second stage of a bootloader and/or transferring control to it加载引导加载程序的第二阶段和/或将控制权转移给它时出现问题
【发布时间】:2017-06-07 08:11:15
【问题描述】:

我的主引导记录代码:

;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的软盘dick,块大小512字节,共2880字节

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

line3:在dt.bin处将dt.asm编译成二进制

line4:将mbr.bin的内容放入boot.img,block size 512,总共放入1个block

line5:将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开始将另一个扇区加载到内存中 谢谢

【问题讨论】:

  • 您能显示您用于dt.asm 的代码吗?或者您怎么知道问题出在加载扇区而不是扇区本身?
  • 了解dt.asm 中的内容会很有用。如果您实际上使用 0x5678:0x1234 作为加载地址,并且对该地址执行 FAR JMp - 那么无论您在 dt.asm 中执行的操作都需要 0x1234 的 ORG 指令(org 0x1234dt.asm 的顶部) 并且您必须将 DSES 设置为 0x5678。
  • @Michael Petch 我在 dt.asm 中的第一行代码:ORG 0x1234:0x5678 或者应该是org 579b4
  • 谢谢!按照你给我的指示,我在屏幕上正确显示了我想要的字符串!关于听击键我可能还有其他问题,我可以通过您的电子邮件地址与您联系吗?
  • @MichaelPetch BTW,能否解释一下:如果跳出到第一个512byte软盘空间,(bootloader空间),dos不会预设到当前环境,int 20h也不存在。我建议实际上当我单独使用自己的 MBR 并调用中断 20h 时涉及 DOS?既然涉及到了 DOS 还是引导过程吗?

标签: linux assembly x86-16 bootloader bios


【解决方案1】:

足够简单。

您不能只使用 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 这样的模拟器可能会在这里使用一些特定的值!

【讨论】:

  • 只是我忘了跳过跳转开始标签的第一行,我的错
  • 所以磁道和磁头都从 0 开始,除了扇区从 1 开始?意思是没有扇区 0,所以物理扇区 2 是逻辑扇区 2?
  • 更正起始值。逻辑扇区号从 0 开始,一直持续到磁盘末尾。 CHS (0,0,1) 是第一个扇区,也就是逻辑扇区 0。
  • 谢谢,但是,我在用 dosbox 运行 boot.img 时还是出现了黑屏,但还是有进步
  • 我的意思是传递给 BIOS 的 ES:BP 指针,用于在设置扇区加载之前显示消息。
【解决方案2】:

由于其他答案中没有涵盖许多问题,我将提供一个新答案。

我建议在另一个Stackoverflow answer 中查看我的通用引导加载程序提示。特别是前几个技巧在这里适用:

  1. 当 BIOS 跳转到您的代码时,您不能依赖 CSDSESSS,SP 寄存器具有有效或预期值。当您的引导加载程序启动时,它们应该被适当地设置。您只能保证您的引导加载程序将从物理地址 0x00007c00 加载和运行,并且引导驱动器号加载到 DL 寄存器中。

  2. lodsbmovsb 等使用的方向标志可以设置或清除。如果方向标志设置不当SI/DI 寄存器可能会在错误的方向上调整。使用STD/CLD 将其设置为您希望的方向(CLD=forward/STD=backwards)。在这种情况下,代码假定向前移动,因此应该使用CLD。更多信息可以在instruction set reference

    中找到
  3. 当跳转到内核时,FAR JMP 到它通常是一个好主意,以便它正确地将 CS:IP 设置为预期值。这可以避免内核代码可能在同一段内执行absolute near JMPsCALLs 的问题。

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

要解决这些问题,您可以在 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

您的引导加载程序中有int 20,位于jmp 0x5678:0x1234 之后。我相信你的意思是int 20hJMP 将永远不会返回,因此在它之后放置代码将无济于事。但是,int 20h 是一个 DOS 中断,只有在 MS-DOS 系统从磁盘加载到内存后才可用。当然,您的磁盘上没有 MS-DOS。在引导加载程序(裸机)环境中,根本不要使用 DOS 中断。


dt.asm 你有一些问题。您FAR JMP 到 0x5678:0x1234 处新加载的代码。这样做 CS 被设置为 0x5678。您需要手动设置 ESDS,您可以通过将 CS 中的值复制到 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 返回之后。在 CALL 之后,您可以使用以下内容进行无限循环:

    jmp $

一个更可取的可以花费更少处理能力的无限循环是使用CLI关闭中断,使用HLT指令,然后在HLT返回JMP时进行安全测量 返回并再次执行 HLT(如果存在 NMI 不可屏蔽中断,则可能发生)。 HLT 一直等到下一个中​​断发生。这是您经常会看到的:

    cli
endloop:
    hlt
    jmp endloop

其他观察

在你发布的代码的第一个版本中,INT 13h/AH=2磁盘读取函数的参数不正确。扇区编号从 1 开始,磁头和柱面从零开始。中断信息的最佳来源是Ralph Brown's Interrupt List,它涵盖了 BIOS 和 MS-DOS 中断。如果您需要有关中断参数的信息,这是一个很好的参考。


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

【讨论】:

  • 感谢您的帮助,在此代码中: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: 我知道 CS 寄存器已自动更新为 0x5678,DS 需要手动更新,因为 ES 是额外的段,所以更新 ES 寄存器有什么意义?
  • @paradox 设置 ES 不是必需的,但是如果发生这种情况,将 ES 设置为 DS 很有用使用 movs/stos/scas 等字符串指令在与 DS 相同的段中移动/存储/扫描数据。 ES 可以是你想要的任何东西,实际上取决于你的使用模式。
猜你喜欢
  • 2016-03-22
  • 2013-05-05
  • 2019-08-24
  • 2011-12-04
  • 2019-07-08
  • 2016-02-14
  • 1970-01-01
  • 1970-01-01
  • 2020-10-21
相关资源
最近更新 更多