首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用int 13h从硬盘读取写入扇区

用int 13h从硬盘读取写入扇区
EN

Stack Overflow用户
提问于 2013-03-19 11:10:01
回答 4查看 16.3K关注 0票数 8

我有一个简单的程序。它必须从硬盘(而不是mbr)读取第一个扇区,并将其写入0扇区(mbr)。但它不起作用。我认为这与错误的DAP有关。谢谢。

代码语言:javascript
运行
复制
    [bits   16]
    [org    0x7c00]

;clear screen
start:
    mov     ax, 0x3
    int     0x10

;reset the hard drive
    xor     ah, ah
    mov     dl, 0x80
    int     0x13
    jnz     error

;read the second sector
    mov     si, DAP
    mov     ah, 0x42
    int     0x13

    mov     si, data
    call    print_string
    jmp     $

DAP:
    db      0x10    ;size of DAP
    db      0x0     ;zero
    db      0x1     ;number of sectors to read
    db      0x0     ;zero
;point to memory
    dw      0x0     ;offset
    dw      0x0     ;segment
    dq      0x1     ;disk address

DAP2:
    db      0x10
    db      0x0
    db      0x1
    db      0x0
    dw      0x0
    dw      0x0
    dd      0x0
    dd      0x0            

print_string:
    mov     ax, 0xb800
    mov     es, ax
    xor     di, di
    mov     cx, 8
    rep     movsw
    ret
data: db 'H',2,'e',2,'l',2,'l',2
error:db 'E',2,'r',2,'r',2
    times   510 - ($ - $$) db 0
    dw      0xaa55   

UPD:新代码

代码语言:javascript
运行
复制
    [bits   16]
    [org    0x7c00]

;clear screen
start:
;    mov     ah, 0
;    push    ax
;    pop     ds
    mov     ax, 0x3
    int     0x10

;reset the hard drive
    xor     ah, ah
    mov     dl, 0x80
    int     0x13
    jc      error

;read the second sector
    mov     si, DAP
    mov     ah, 0x42
    int     0x13

    mov     si, data
    call    print_string
    jmp     $

DAP:
    db      0x10    ;size of DAP
    db      0x0     ;zero
    db      0x1     ;number of sectors to read
    db      0x0     ;zero
;point to memory
    dw      0x0     ;offset
    dw      0x8c00  ;segment
    dq      0x1     ;disk address

DAP2:
    db      0x10
    db      0x0
    db      0x1
    db      0x0
    dw      0x0
    dw      0x8c00
    dq      0x2            

print_string:
    mov     ax, 0xb800
    mov     es, ax
    xor     di, di
    mov     si, 0x8c00
    mov     cx, 8
    rep     movsw
    ret

data: db 'H',2,'e',2,'l',2,'l',2
error:db 'E',2,'r',2,'r',2
endp:
    times   510 - ($ - $$) db 0
    dw      0xaa55 

P.S.我在用Bochs

EN

回答 4

Stack Overflow用户

发布于 2014-07-23 10:03:50

有点恋尸癖,希望你的组装技巧在这段时间里有所提高。但以防万一..。

引用@Alexey的话来说,“你需要注意你正在做的事情”。除了在其他答案中详述的错误之外,以下是我的一些观察:

你的仿真器太好了

  • 您的代码似乎是引导加载程序。假设BIOS将在0x0000:0x7C00加载您的代码,但您不能确定它实际上没有在0x07C0:0000或任何其他等效地址加载它。读一读分割
  • 您无法初始化任何段寄存器。您可能不会使用它,因为您的仿真器很好,并且正确地将csdses初始化为0x0000

您可以这样解决这两个问题:

代码语言:javascript
运行
复制
[bits 16]
[org 0x7C00]

    jmp 0x0000:start_16 ; ensure cs == 0x0000

start_16:
    ; initialise essential segment registers
    xor ax, ax
    mov ds, ax
    mov es, ax

根本误解

  • 如果发生错误,直接跳转到字符串,而不是可执行代码。只有上帝知道,如果发生这种情况,电脑会做什么。
  • 您检查驱动器重置的返回值(CF),但不检查读取本身。如果读取失败,则应重置驱动器并再次尝试读取。在循环中这样做几次(例如,3次),以防驱动器打嗝。如果驱动器复位失败,很可能是更严重的错误,你应该保释。

更安全的方法

我建议使用int 0x13, ah = 0x02。您正在使用的是一个扩展的BIOS函数,它可能并不是所有系统都支持的(仿真器支持可能是不稳定的,更不用说在一些现代硬件上发现的延迟BIOS实现)。你在真正的模式中-你不需要做任何花哨的事情。最好进入保护模式,长期目标是编写一个PM驱动程序来处理磁盘I/O。

只要您保持真实模式,这里就有一个独立的函数,它将使用简单的BIOS函数从磁盘读取一个或多个扇区。如果您事先不知道您需要哪个扇区,您将不得不添加额外的检查来处理多声道读取

代码语言:javascript
运行
复制
; read_sectors_16
;
; Reads sectors from disk into memory using BIOS services
;
; input:    dl      = drive
;           ch      = cylinder[7:0]
;           cl[7:6] = cylinder[9:8]
;           dh      = head
;           cl[5:0] = sector (1-63)
;           es:bx  -> destination
;           al      = number of sectors
;
; output:   cf (0 = success, 1 = failure)

read_sectors_16:
    pusha
    mov si, 0x02    ; maximum attempts - 1
.top:
    mov ah, 0x02    ; read sectors into memory (int 0x13, ah = 0x02)
    int 0x13
    jnc .end        ; exit if read succeeded
    dec si          ; decrement remaining attempts
    jc  .end        ; exit if maximum attempts exceeded
    xor ah, ah      ; reset disk system (int 0x13, ah = 0x00)
    int 0x13
    jnc .top        ; retry if reset succeeded, otherwise exit
.end:
    popa
    retn

您的打印功能假设一个彩色监视器(通过将0xB 8000写入视频内存)。再说一次,你在真实的模式中。保持简单。使用BIOS服务:

代码语言:javascript
运行
复制
; print_string_16
;
; Prints a string using BIOS services
;
; input:    ds:si -> string

print_string_16:
    pusha
    mov  ah, 0x0E    ; teletype output (int 0x10, ah = 0x0E)
    mov  bx, 0x0007  ; bh = page number (0), bl = foreground colour (light grey)
.print_char:
    lodsb            ; al = [ds:si]++
    test al, al
    jz   .end        ; exit if null-terminator found
    int  0x10        ; print character
    jmp  .print_char ; repeat for next character
.end:
    popa
    retn

示例用法

代码语言:javascript
运行
复制
load_sector_2:
    mov  al, 0x01           ; load 1 sector
    mov  bx, 0x7E00         ; destination (might as well load it right after your bootloader)
    mov  cx, 0x0002         ; cylinder 0, sector 2
    mov  dl, [BootDrv]      ; boot drive
    xor  dh, dh             ; head 0
    call read_sectors_16
    jnc  .success           ; if carry flag is set, either the disk system wouldn't reset, or we exceeded our maximum attempts and the disk is probably shagged
    mov  si, read_failure_str
    call print_string_16
    jmp halt                ; jump to a hang routine to prevent further execution
.success:
    ; do whatever (maybe jmp 0x7E00?)


read_failure_str db 'Boot disk read failure!', 13, 10, 0

halt:
    cli
    hlt
    jmp halt

最后但同样重要的是..。

您的引导加载程序没有设置堆栈。我提供的代码使用堆栈来防止寄存器崩溃。在引导加载程序(< 0x7C00)之前有可用的近30,因此您只需在引导加载程序开始时的某个地方执行此操作:

代码语言:javascript
运行
复制
xor ax, ax
cli         ; disable interrupts to update ss:sp atomically (AFAICT, only required for <= 286)
mov ss, ax
mov sp, 0x7C00
sti

呼!有很多要消化的。请注意,我已经尝试保持独立功能的灵活性,这样您就可以在其他16位真正的模式程序中重用它们。我建议您尝试编写更多模块化代码,并坚持这种方法,直到您更有经验。

例如,如果您在使用扩展的read函数时死掉了,那么也许您应该在堆栈上编写一个接受DAP的函数,或者一个指向DAP的指针。当然,首先,您将浪费代码空间将数据推送到那里,但是一旦数据在那里,您就可以简单地为后续读取调整必要的字段,而不是让大量的DAP占用内存。堆栈空间可以稍后回收。

不要灰心,集会需要时间,对细节的极度关注.在工作中猛击这些东西并不容易,所以我的代码中可能会有错误!:)

票数 25
EN

Stack Overflow用户

发布于 2013-03-19 11:37:52

首先,您需要检查cf,而不是zf,以查看BIOS调用是否成功。纠正你的jnz error

其次,您似乎依赖于ds等于0。不能保证是0。把它设为0。

同样,对于flags.df,它也不能保证为0。把它设为0。查看关于repmovs*cld的文档。

第三,您要求BIOS读取扇区并将其写入内存中的物理地址0。通过这样做,您可以覆盖中断向量表(从那里开始,占用1KB)并损坏系统,需要重新启动。选择一个更好的地址。最好是在内存引导区结束之后。但是,您还需要确保堆栈不存在,所以还需要将堆栈设置为已知位置。

你需要注意你在做什么。

票数 8
EN

Stack Overflow用户

发布于 2015-10-05 07:45:07

使用代码加载扇区并跳转到扇区的最小NASM BIOS示例,没有错误检查和DAP:

代码语言:javascript
运行
复制
use16
org 0x7C00

    ; For greater portability you should
    ; do further initializations here like setup the stack and segments. 

    ; Load stage 2 to memory.
    mov ah, 0x02
    mov al, 1
    ; This may not be necessary as many BIOS setup is as an initial state.
    mov dl, 0x80
    mov ch, 0
    mov dh, 0
    mov cl, 2
    mov bx, stage2
    int 0x13

    jmp stage2

    ; Magic bytes.    
    times ((0x200 - 2) - ($ - $$)) db 0x00
    dw 0xAA55

stage2:

    ; Print 'a'.
    mov ax, 0x0E61
    int 0x10

    cli
    hlt

    ; Pad image to multiple of 512 bytes.
    times ((0x400) - ($ - $$)) db 0x00

编译和运行:

代码语言:javascript
运行
复制
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img

预期结果:a被打印到屏幕上,然后程序停止。

在Ubuntu 14.04上测试。

使用链接器脚本和更正确的初始化(段寄存器,堆栈) 在我的GitHub上的更好的气体示例。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15497842

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档