我有一个简单的程序。 它必须读取硬盘驱动器(MBR不)第一个扇区,并将其写入0扇区(MBR)。 但它不工作。 我认为这是错误的与DAP相连。 谢谢。
[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:新代码
[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
PS我使用Bochs的。
奸尸的位; 希望您的装配技能,在此期间有所改善。 但是为了以防万一......
引述@Alexey伏龙芝“你需要注意你在做什么”。 除了在其他答案详细的错误,这里是我的一些意见:
你的模拟器是太不厚道
您可以解决这两个问题是这样的:
[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
基本的误解
更安全的方法
我会建议使用int 0x13, ah = 0x02
。 您使用的可能不是在所有系统上支持扩展BIOS功能(模拟器支持可能是古怪,更不用说一些现代硬件中的懒惰BIOS实现)。 你在实模式 - 你不需要做任何幻想。 这将是最好的进入保护模式写一个PM驱动程序来处理磁盘I / O的长期目标。
只要你留在实模式,这里是一个独立的函数,将读取使用简单的BIOS功能,从磁盘中的一个或多个部门。 如果你不提前知道哪个部门()你需要,你将不得不增加额外的检查,以照顾多轨读取 。
; 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
您的打印功能呈现在彩色显示器(通过0xB8000写入显存)。 同样,你在实模式。 把事情简单化。 使用BIOS服务:
; 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
用法示例
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
最后但并非最不重要的...
引导程序不设置栈。 我提供的代码使用了堆栈来防止寄存器捣毁。 有几乎30KiB引导程序(<0x7C00)之前,所以你可以简单地做这个地方你的引导附近开始:
xor ax, ax
cli ; disable interrupts to update ss:sp atomically (AFAICT, only required for <= 286)
mov ss, ax
mov sp, 0x7C00
sti
唷! 大量消化。 请注意,我试图保持独立的功能灵活,所以在其他16位实模式程序可以重新使用它们。 我建议你尝试写更多模块化的代码,并坚持这种做法,直到你更有经验。
例如,如果你在使用扩展读取功能死心塌地,也许你应该写一个接受DAP,或指向一个,堆栈上的功能。 当然,你会浪费代码空间推数据有摆在首位,但一旦它的存在,你可以简单地调整必要的字段为后续读取,而不是大量的DAP的占用内存。 堆栈空间可以稍后收回。
不要气馁,组装需要时间,而可怕的对细节的关注......在工作中扑这个东西出来不容易的时候,所以有可能是在我的代码中的错误! :)
首先,你需要检查cf
而不是zf
,看是否BIOS调用成功。 纠正你的jnz error
。
其次,你似乎是依靠ds
等于0。它不能保证为0将其设置为0。
对于同上flags.df
,它不能保证为0它设置为0检查的文件rep
, movs*
和cld
。
第三,你问BIOS读取部门和它在内存中写入到物理地址0。 通过这样做,你覆盖中断向量表(即从那里开始,占用1KB)和破坏系统,需要重新启动。 选择一个更好的地址。 最好是在内存中的启动扇区结束之后。 但是,你想也需要确保堆栈不存在,所以你需要堆栈设置为一个已知的位置为好。
你需要注意你在做什么。
加载一个扇区的代码并跳转到它,没有错误检查和DAP最小NASM BIOS例如:
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
编译并运行:
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img
预期成果: a
被打印到屏幕上,然后程序停止。
经测试在Ubuntu 14.04。
使用链接脚本和更正确的初始化(段寄存器,堆栈)理智GAS例如在我的GitHub 。
load:
; Load sectors routine : bootdrv-drive , snum-sectors to load
;ES:BX where to load ex: mov ax,0000h mov es,ax mov bx, 7c00h
push bx
push ds
mov verify,0h
.reset:
cmp verify,5h
je Err1
add verify,1h
mov ax, 0 ; Reset Disk
mov dl, [BootDrv] ; Drive to reset
int 13h ;
jc .reset ; Failed -> Try again
pop ds
pop bx
mov verify,0h
.read:
cmp verify,5h
je Err1
add verify,1h
mov ah, 2 ; Interrupt 13h,2 => Read disk sectors
mov al, snum ; how many sectors to read
mov cx, fsect ; cl-first sector to be r/w ch-track containing sector
mov dh, head ; head=0
mov dl, [BootDrv] ; Drive=boot drive
int 13h ; Read! ES:BX = data from disk
jc .read ; failed -> Try again
retn