如何在汇编中迭代所有磁盘扇区?
How are all disk sectors iterated in assembly?
在学习汇编的过程中,正在写一篇OS。我已经成功编写了将第二个 512 字节扇区附加到初始 512 字节引导加载程序所需的代码:
%define KBDINT 0x16
%define VIDINT 0x10
%define DISKINT 0x13
%define TTYOUT 0x0E
%define VIDMODE 0x0000
%define NUL 0x00
%define CR 0x0D
%define LF 0x0A
%define START 0x7C00
%macro PRINT 1
mov si, %1
call print
%endmacro
bits 16 ; 16 bit real mode
org START ; loader start in memory
start: jmp main
print: jmp .init
.loop: mov bx, VIDMODE
mov ah, TTYOUT
int VIDINT
inc si
.init: mov al, [si]
cmp al, NUL
jne .loop
ret
main: cli
xor ax, ax
mov ds, ax
mov es, ax
sti
PRINT welcome
mov ah, NUL
int DISKINT
mov al, 0x01 ; sector count
mov ah, 0x02 ; read function
mov bx, kernel
mov cl, 0x02
mov ch, 0x00 ; cylinder number
mov dh, 0x00 ; head number
int DISKINT
jc fail
jmp kernel
fail: PRINT failure
; jmp halt
halt: PRINT halting
cli
hlt
PRINT imprbbl
jmp halt
welcome db "moose os", CR, LF, NUL
failure db "failed disk load", CR, LF, NUL
halting db "halting", CR, LF, NUL
imprbbl db "but that's impossible!", CR, LF, NUL
times 0x0200 - ($ - $$) - 2 db 0x00
end dw 0xAA55
kernel: PRINT yay
yay db "kernel", CR, LF, NUL
jmp halt
times 0xFFFF db 0x00
我用以下代码编译文件:nasm -f bin -o boot.bin boot.asm && qemu boot.bin
:
很好奇磁头和柱面是怎么用的:
- 扇区是如何迭代的?
- 模拟和直接执行之间的迭代有何不同?
•How are the sectors iterated through?
要使用 CHS CylinderHeadSector 表示法遍历许多扇区,我们
首先必须检索这些参数的实际限制。 BIOS有功能
int 13h
上的 08h 为我们提供了 maximum 值以及一些额外的值
我们暂时不需要的信息。
CL中扇区号的范围是1到63。
DH中的head number范围是0到255,虽然255很少用到。
CL 中的柱面号范围为 0 到 1023。因为这不能保存在
单个字节,这个 10 位数的最高 2 位存储在第 6 位中
和 CL 寄存器的 7!
迭代如何工作
将 CHS 符号想象成某种数字,其中 C 是
最重要的部分,S 是最不重要的部分。
为了到达磁盘上的下一个扇区,我们开始增加 this
number 在最不重要的一端。
如果通过递增 S 部分我们溢出了它的范围,我们将它重置为它的
最小值 (1) 并开始递增下一个更重要的部分
在这种情况下是 H。
如果通过递增 H 部分我们溢出了它的范围,我们将它重置为它的
最小值 (0) 并开始递增最重要的部分
在这种情况下是 C。
如果通过递增 C 部分我们溢出了它的范围,我们将它重置为它的
最小值 (0)。这将在磁盘上形成环绕。如果在输入
给出了正确的 SectorCount,此时通常读数将有
停止了。
; INPUT: DL=Drive
; CH=Cylinder
; DH=Head
; CL=Sector
; AX=SectorCount
; ES:BX=Buffer
; OUTPUT: CF=0 AH = 0
; CH,DH,CL = CHS of following sector
; CF=1 AH = Error status
; CH,DH,CL = CHS of problem sector
ReadDiskSectors:
push es
push di
push bp
mov bp,sp ;Local variables:
push ax ;[bp-2]=SectorCount
push cx ;[bp-4]=MaxSector
push dx ;[bp-6]=MaxHead
push bx ;[bp-8]=MaxCylinder
push es
mov ah,08h
int 13h ;ReturnDiskDriveParameters
pop es
jc NOK
mov bx,cx ;10-bit cylinder info -> BX
xchg bl,bh
shr bh,6
xchg [bp-8],bx ;Store MaxCylinder and get input BX back
movzx dx,dh ;8-bit head info -> DX
xchg [bp-6],dx ;Store MaxHead and get input DX back
and cx,003Fh ;6-bit sector info -> CX
xchg [bp-4],cx ;Store MaxSector and get input CX back
ReadNext:
mov di,5 ;Max 5 tries per sector
ReadAgain:
mov ax,0201h ;Read 1 sector
int 13h ;ReadDiskSectors
jnc OK
push ax ;Save error status byte in AH
mov ah,00h
int 13h ;ResetDiskSystem
pop ax
dec di
jnz ReadAgain
stc
jmp NOK
OK:
dec word ptr [bp-2] ;SectorCount
jz Ready
call NextCHS
mov ax,es ;Move buffer 512 bytes up
add ax,512/16
mov es,ax
jmp ReadNext
Ready:
call NextCHS ;Return useful CHS values to support reads
xor ah,ah ; -> CF=0 ... that are longer than memory
NOK:
mov sp,bp
pop bp
pop di
pop es
ret
NextCHS:
mov al,cl ;Calculate the 6-bit sector number
and al,00111111b
cmp al,[bp-4] ;MaxSector
jb NextSector
cmp dh,[bp-6] ;MaxHead
jb NextHead
mov ax,cx ;Calculate the 10-bit cylinder number
xchg al,ah
shr ah,6
cmp ax,[bp-8] ;MaxCylinder
jb NextCylinder
DiskWrap:
mov cx,1 ;Wraparound to very first sector on disk
mov dh,0
ret
NextCylinder:
inc ax
shl ah,6 ;Split 10-bit cylinder number over CL and CH
xchg al,ah
mov cx,ax
mov dh,0
inc cl
ret
NextHead:
inc dh
and cl,11000000b
NextSector:
inc cl
ret
关于扇区大小的注释
虽然拥有非 512 字节的扇区是完全可以的
长度,这是一个保存假设,它们将是那个尺寸。
经过几十年的编程,我从未见过没有 512 字节的磁盘
部门。
如果你硬要支持不同的大小,可以看第4个字节
您从 ES:DI 中收到指针的 DisketteParameterTable 的
BIOS 函数 ReturnDiskDriveParameters。
•How does iteration differ between emulation and direct execution?
我想通过直接执行你了解真正的硬件。
对于真正的硬件 BIOS 将 return 您以 CHS 表示法表示的几何形状和扇区存在......好吧只是因为它们是真实的!
在仿真下,仿真器将尽最大努力为您提供这些几何值,但您需要确保相关驱动器上存在足够的扇区。这正是 @Jester 问你时所说的:"Do you even have that sector in the image file?" 你通过使用 times 0xFFFF db 0x00
放大图像文件解决了这个问题
一些额外的建议
你没有设置堆栈。由于您在 7C00h 处将内核加载到引导扇区之上,我建议您将 SS:SP 初始化为 0000h:7C00h,以便在引导扇区下方形成一个堆栈。
main:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 7C00h
sti
PRINT welcome
正如@Fifoernik 评论的那样,您最好将 jmp halt
放在 yay db "kernel", CR, LF, NUL
之前,以防止执行此数据!
kernel:
PRINT yay
jmp halt
yay db "kernel", CR, LF, NUL
在学习汇编的过程中,正在写一篇OS。我已经成功编写了将第二个 512 字节扇区附加到初始 512 字节引导加载程序所需的代码:
%define KBDINT 0x16
%define VIDINT 0x10
%define DISKINT 0x13
%define TTYOUT 0x0E
%define VIDMODE 0x0000
%define NUL 0x00
%define CR 0x0D
%define LF 0x0A
%define START 0x7C00
%macro PRINT 1
mov si, %1
call print
%endmacro
bits 16 ; 16 bit real mode
org START ; loader start in memory
start: jmp main
print: jmp .init
.loop: mov bx, VIDMODE
mov ah, TTYOUT
int VIDINT
inc si
.init: mov al, [si]
cmp al, NUL
jne .loop
ret
main: cli
xor ax, ax
mov ds, ax
mov es, ax
sti
PRINT welcome
mov ah, NUL
int DISKINT
mov al, 0x01 ; sector count
mov ah, 0x02 ; read function
mov bx, kernel
mov cl, 0x02
mov ch, 0x00 ; cylinder number
mov dh, 0x00 ; head number
int DISKINT
jc fail
jmp kernel
fail: PRINT failure
; jmp halt
halt: PRINT halting
cli
hlt
PRINT imprbbl
jmp halt
welcome db "moose os", CR, LF, NUL
failure db "failed disk load", CR, LF, NUL
halting db "halting", CR, LF, NUL
imprbbl db "but that's impossible!", CR, LF, NUL
times 0x0200 - ($ - $$) - 2 db 0x00
end dw 0xAA55
kernel: PRINT yay
yay db "kernel", CR, LF, NUL
jmp halt
times 0xFFFF db 0x00
我用以下代码编译文件:nasm -f bin -o boot.bin boot.asm && qemu boot.bin
:
很好奇磁头和柱面是怎么用的:
- 扇区是如何迭代的?
- 模拟和直接执行之间的迭代有何不同?
•How are the sectors iterated through?
要使用 CHS CylinderHeadSector 表示法遍历许多扇区,我们
首先必须检索这些参数的实际限制。 BIOS有功能
int 13h
上的 08h 为我们提供了 maximum 值以及一些额外的值
我们暂时不需要的信息。
CL中扇区号的范围是1到63。
DH中的head number范围是0到255,虽然255很少用到。
CL 中的柱面号范围为 0 到 1023。因为这不能保存在
单个字节,这个 10 位数的最高 2 位存储在第 6 位中
和 CL 寄存器的 7!
迭代如何工作
将 CHS 符号想象成某种数字,其中 C 是
最重要的部分,S 是最不重要的部分。
为了到达磁盘上的下一个扇区,我们开始增加 this
number 在最不重要的一端。
如果通过递增 S 部分我们溢出了它的范围,我们将它重置为它的
最小值 (1) 并开始递增下一个更重要的部分
在这种情况下是 H。
如果通过递增 H 部分我们溢出了它的范围,我们将它重置为它的
最小值 (0) 并开始递增最重要的部分
在这种情况下是 C。
如果通过递增 C 部分我们溢出了它的范围,我们将它重置为它的
最小值 (0)。这将在磁盘上形成环绕。如果在输入
给出了正确的 SectorCount,此时通常读数将有
停止了。
; INPUT: DL=Drive
; CH=Cylinder
; DH=Head
; CL=Sector
; AX=SectorCount
; ES:BX=Buffer
; OUTPUT: CF=0 AH = 0
; CH,DH,CL = CHS of following sector
; CF=1 AH = Error status
; CH,DH,CL = CHS of problem sector
ReadDiskSectors:
push es
push di
push bp
mov bp,sp ;Local variables:
push ax ;[bp-2]=SectorCount
push cx ;[bp-4]=MaxSector
push dx ;[bp-6]=MaxHead
push bx ;[bp-8]=MaxCylinder
push es
mov ah,08h
int 13h ;ReturnDiskDriveParameters
pop es
jc NOK
mov bx,cx ;10-bit cylinder info -> BX
xchg bl,bh
shr bh,6
xchg [bp-8],bx ;Store MaxCylinder and get input BX back
movzx dx,dh ;8-bit head info -> DX
xchg [bp-6],dx ;Store MaxHead and get input DX back
and cx,003Fh ;6-bit sector info -> CX
xchg [bp-4],cx ;Store MaxSector and get input CX back
ReadNext:
mov di,5 ;Max 5 tries per sector
ReadAgain:
mov ax,0201h ;Read 1 sector
int 13h ;ReadDiskSectors
jnc OK
push ax ;Save error status byte in AH
mov ah,00h
int 13h ;ResetDiskSystem
pop ax
dec di
jnz ReadAgain
stc
jmp NOK
OK:
dec word ptr [bp-2] ;SectorCount
jz Ready
call NextCHS
mov ax,es ;Move buffer 512 bytes up
add ax,512/16
mov es,ax
jmp ReadNext
Ready:
call NextCHS ;Return useful CHS values to support reads
xor ah,ah ; -> CF=0 ... that are longer than memory
NOK:
mov sp,bp
pop bp
pop di
pop es
ret
NextCHS:
mov al,cl ;Calculate the 6-bit sector number
and al,00111111b
cmp al,[bp-4] ;MaxSector
jb NextSector
cmp dh,[bp-6] ;MaxHead
jb NextHead
mov ax,cx ;Calculate the 10-bit cylinder number
xchg al,ah
shr ah,6
cmp ax,[bp-8] ;MaxCylinder
jb NextCylinder
DiskWrap:
mov cx,1 ;Wraparound to very first sector on disk
mov dh,0
ret
NextCylinder:
inc ax
shl ah,6 ;Split 10-bit cylinder number over CL and CH
xchg al,ah
mov cx,ax
mov dh,0
inc cl
ret
NextHead:
inc dh
and cl,11000000b
NextSector:
inc cl
ret
关于扇区大小的注释
虽然拥有非 512 字节的扇区是完全可以的
长度,这是一个保存假设,它们将是那个尺寸。
经过几十年的编程,我从未见过没有 512 字节的磁盘
部门。
如果你硬要支持不同的大小,可以看第4个字节
您从 ES:DI 中收到指针的 DisketteParameterTable 的
BIOS 函数 ReturnDiskDriveParameters。
•How does iteration differ between emulation and direct execution?
我想通过直接执行你了解真正的硬件。
对于真正的硬件 BIOS 将 return 您以 CHS 表示法表示的几何形状和扇区存在......好吧只是因为它们是真实的!
在仿真下,仿真器将尽最大努力为您提供这些几何值,但您需要确保相关驱动器上存在足够的扇区。这正是 @Jester 问你时所说的:"Do you even have that sector in the image file?" 你通过使用 times 0xFFFF db 0x00
一些额外的建议
你没有设置堆栈。由于您在 7C00h 处将内核加载到引导扇区之上,我建议您将 SS:SP 初始化为 0000h:7C00h,以便在引导扇区下方形成一个堆栈。
main:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 7C00h
sti
PRINT welcome
正如@Fifoernik 评论的那样,您最好将 jmp halt
放在 yay db "kernel", CR, LF, NUL
之前,以防止执行此数据!
kernel:
PRINT yay
jmp halt
yay db "kernel", CR, LF, NUL