1

我正在尝试在 x86 程序集(16 位实模式)中创建一个简单的第二阶段命令系统。函数com_dir假设读取 720KB 软盘并在扇区以字母“F”开头时打印字符串。

这是第二阶段:

[BITS 16]
[ORG 0x0000]      

mov ax, cs
mov ds, ax   
mov [drive_num], dl
mov [root_drive], dl
mov bx, load_msg
call str_prt
call new_line
call new_line
xor cx, cx  
call new_line
mov bx, art1
call str_prt
call new_line
mov bx, art2
call str_prt
call new_line
mov bx, art3
call str_prt
call new_line
mov bx, art4
call str_prt
call new_line
mov bx, art5
call str_prt
call new_line
mov bx, art6
call str_prt
call new_line
mov bx, art7
call str_prt
call new_line
mov bx, welcome_msg
call str_prt
call new_line
mov bx, creator_msg
call str_prt
call new_line
mov bx, boot_msg
call str_prt
call new_line

mov bx, [buffer]

call new_line

mov ah, 0x0e
mov al, 0x0a
int 0x10
mov al, 0x0d
int 0x10
mov al, '>'
int 0x10

loop:
xor ah, ah
int 0x16
call key_scan
jmp loop

%include "util.inc"

pro_com:
push bx
push ax
mov bx, buffer
mov al, [bx]
cmp al, 'h'
jne help_end
inc bx
mov al, [bx]
cmp al, 'e'
jne help_end
inc bx
mov al, [bx]
cmp al, 'l'
jne help_end
inc bx
mov al, [bx]
cmp al, 'p'
jne help_end
call com_help
jmp pro_end
help_end:
mov bx, buffer
mov al, [bx]
cmp al, 'd'
jne dir_end
inc bx
mov al, [bx]
cmp al, 'i'
jne dir_end
inc bx
mov al, [bx]
cmp al, 'r'
jne dir_end
call com_dir
jmp pro_end
dir_end:
mov bx, buffer
mov al, [bx]
cmp al, 'o'
jne open_end
inc bx
mov al, [bx]
cmp al, 'p'
jne open_end
inc bx
mov al, [bx]
cmp al, 'e'
jne open_end
inc bx
mov al, [bx]
cmp al, 'n'
jne open_end
call com_open
jmp pro_end
open_end:
mov bx, not_found
call str_prt
call new_line
pro_end:
pop ax
pop bx
ret

com_help:
push bx
call new_line
call draw_line
mov bx, dir_help
call str_prt
call new_line
call draw_line
call new_line
pop bx
ret

com_dir:
push ax
push bx
push cx
push dx
call new_line
call draw_line
mov bx, drive_num
mov dl, [bx]
mov al, 0x01
mov ch, 0x00
mov cl, 0x01
mov dh, 0x00
com_dir_loop:
call read_dir
cmp cl, 0x12
je false1
inc cl
jmp com_dir_loop
false1:
cmp ch, 0x4f
je false2
inc ch
xor cl, cl
add cl, 0x01
jmp com_dir_loop
false2:
call draw_line
call new_line
pop dx
pop cx
pop bx
pop ax 
ret

read_dir:
push ax
push bx
mov bx, 0x1000
mov es, bx
mov bx, 0xe00
call read_disc
clc
mov bx, 0x0e00
mov al, [bx]
cmp al, 'F'
jne read_dir_end
mov bx, 0x0e01
call str_prt
call new_line
read_dir_end:
pop bx
pop ax 
mov bx, 0x1000
mov es, bx
ret

com_open:
push ax
push bx
push cx
push dx
mov bx, drive_num
mov dl, [bx]
mov cl, 0x00
mov al, 0x01
mov dh, 0x00
mov bx, 0x1000
mov es, bx
mov ch, 0x00
mov cl, 0x09
mov bx, 0xe00
;----------
com_open_loop:
call read_disc
call next_load
jc com_open_loop_end
jmp com_open_loop
;----------
com_open_loop_end:
clc
call 0x0e0d
clc
call new_line
mov bx, 0x1000
mov es, bx
mov ds, bx
pop dx
pop cx
pop bx
pop ax 
ret

next_load:

clc
ret

buffer times 20 db 0

drive_num:
db 0
root_drive:
db 0

welcome_msg:
db 'Welcome to matriXos$'
creator_msg:
db 'Created by Vishnu Shankar.B$'
boot_msg:
db 'Booting command line interface...$'
not_found:
db '<Error>Command cannot be resolved! Use command "help"$'
dir_help:
db '    dir - Lists files in a drive$'
line:
db '==============================$'
art1:
db '            ==       ==$'
art2:
db '            #  X   X  #$'
art3:
db '            #   X X   #$'
art4:
db '            #    X    #$'
art5:
db '            #   X X   #$'
art6:
db '            #  X   X  #$'
art7:
db '            ==       ==$'
load_msg:
db 'Successfully loaded operating system$'

jmp $
times 3584 - ($ - $$) db 0

我的文件util.inc

key_scan:
cmp al, 0x08
je back_space
cmp al, 0x0d
je enter
cmp cx, 0x0015 
je end
mov ah, 0x0e
int 0x10
mov bx, buffer
add bx, cx
mov [bx], al
inc cx
jmp end
back_space:
cmp cx, 0x00
je end
dec cx
mov ah, 0x0e
mov al, 0x08
int 0x10
mov al, 0x20
int 0x10
mov al, 0x08
int 0x10
jmp end
enter:
xor cx, cx
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov al, 0x0d
int 0x10
call pro_com
call clear_buffer
mov ah, 0x0e
mov al, '>'
int 0x10
end:
ret

clear_buffer:
push ax
push bx
push cx
mov bx, buffer
xor cx, cx
xor ax, ax
start:
cmp cx, 0x41
je end_buff
mov [bx], ax
inc bx
inc cx
jmp start
end_buff:
pop cx
pop bx
pop ax
ret

str_prt:
pusha
str:
mov ah, 0x0e
mov al, [bx]
cmp al, '$'
je str_end
int 0x10
add bx, 1
jmp str
str_end:
popa
ret

draw_line:
push ax
push bx
line_str:
mov bx, line
call str_prt
call new_line
pop bx
pop ax
ret

new_line:
push ax
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov al, 0x0d
int 0x10
pop ax
ret

read_disc:
mov ah, 0x02   
int 0x13   
ret

hex_print:
push ax
push cx
mov ah, 0x0e
mov al, '0'
int 0x10
mov al, 'x'
int 0x10
hex_print_start:
mov al, ch
and al, 0xf0
call hex_map
int 0x10
shl cx, 0x04
mov al, ch
and al, 0xf0
call hex_map
int 0x10
shl cx, 0x04
mov al, ch
and al, 0xf0
call hex_map
int 0x10
shl cx, 0x04
mov al, ch
and al, 0xf0
call hex_map
int 0x10
hex_print_end:
pop cx
pop ax
ret

hex_map:
cmp al, 0x00
jne zero_end
mov al, '0'
ret
zero_end:
cmp al, 0x10
jne one_end
mov al, '1'
ret
one_end:
cmp al, 0x20
jne two_end
mov al, '2'
ret
two_end:
cmp al, 0x30
jne three_end
mov al, '3'
ret
three_end:
cmp al, 0x40
jne four_end
mov al, '4'
ret
four_end:
cmp al, 0x50
jne five_end
mov al, '5'
ret
five_end:
cmp al, 0x60
jne six_end
mov al, '6'
ret
six_end:
cmp al, 0x70
jne seven_end
mov al, '7'
ret
seven_end:
cmp al, 0x80
jne eight_end
mov al, '8'
ret
eight_end:
cmp al, 0x90
jne nine_end
mov al, '9'
ret
nine_end:
cmp al, 0xa0
jne a_end
mov al, 'A'
ret
a_end:
cmp al, 0xb0
jne b_end
mov al, 'B'
ret
b_end:
cmp al, 0xc0
jne c_end
mov al, 'C'
ret
c_end:
cmp al, 0xd0
jne d_end
mov al, 'D'
ret
d_end:
cmp al, 0xe0
jne e_end
mov al, 'E'
ret
e_end:
cmp al, 0xf0
jne f_end
mov al, 'F'
ret
f_end:
ret

由于某种原因,它在读取 720KB(720 次)时会在 bochs 中引发此错误:

[FLOPPY] read() on floppy image returns 0

有人可以向我解释什么是错的吗?

注意:请忽略com_open,它仍在开发中。

4

1 回答 1

2

错误:

软盘图像上的 read() 返回 0

表明 Bochs 正在尝试从您的磁盘映像中读取数据,但它尝试访问的磁盘部分实际上并不存在。如果您创建的磁盘映像小于正在使用的软盘驱动器类型,并且您没有使用自动媒体格式,则在 Bochs 中可能会发生这种情况。

你有几种方法可以解决这个问题。在 Bochs 内将 设置Type of floppy drive为 720K,并将设置为 720K。Type of floppy media由于您的磁盘映像为 720k,因此这样的更改应该会导致此错误消失。

另一种方法是将 设置Type of floppy drive为 2.88MB 驱动器,然后将 设置Type of floppy mediaauto。在现实世界中,2.88MB 软盘驱动器也可以读取 1.44MB、720K、360k 和 180k 3.5" 媒体。Bochs 尝试使用AUTO软盘媒体类型来模拟这一点。

如果您的磁盘映像为 720K 并使用AUTO媒体,Bochs 将自动下降到您实际使用的磁盘映像的大小。唯一的要求是磁盘映像恰好是 180k、360k、720k、1440k、2880k 之一)。任何不完全符合已知标准 3.5" 磁盘大小的大小都无法与AUTO一起正常工作。


您原始问题中的代码是这样做的:

com_dir_loop:
call read_dir
cmp cl, 0x12       ; 0x12=18 decimal. CL=sector number

如果使用 3.5" 720k 介质,每个柱面的扇区数为 0x09(而不是 0x12)。此Wiki 文章中有一张很好的软盘几何表。您的代码不应尝试读取高于介质允许的扇区数. Bochs 会抛出一种不同类型的错误,类似于尝试在最后一个扇区 x 之后读取/写入扇区 xx。您的代码可能如下所示:

com_dir_loop:
call read_dir
cmp cl, 0x09       ; CL=sector number

您将在 720k 软盘的图表中观察到的另一件事(在给定的链接上)是 720K 软盘有 2 个磁头(0 和 1)。您的代码只处理磁头 0。这意味着您的代码将只能访问磁盘的一半内容。头 1 上的任何内容都将被忽略。您需要处理DH中的磁头,以便当达到每个柱面(磁道)的扇区限制时,将磁头增加 1。如果超过 2 磁头限制,则必须将扇区调整回 1并返回 0 并将圆柱体增加 1。


由于您似乎正在编写内核的一部分,因此我建议您考虑为 CHS 例程创建 LBA(逻辑块寻址)(反之亦然)。LBA 编号只是从 0 到驱动器上扇区数的扇区编号。例如,一张 720k 软盘上有 1440 个 512 字节的扇区。因此 LBA 从 0 变为 1439。您可以使用此Wiki 文章下的第二个公式将任何 LBA 转换为 CHS(气缸盖扇区)值。

CHS 元组可以通过以下公式映射到 LBA 地址:[5][6]

LBA = (C × HPC + H) × SPT + (S - 1)

在哪里

C, H and S are the cylinder number, the head number, and the sector number
LBA is the logical block address
HPC is the maximum number of heads per cylinder (reported by disk drive, typically 16 for 28-bit LBA)
SPT is the maximum number of sectors per track (reported by disk drive, typically 63 for 28-bit LBA)

LBA 地址可以通过以下公式映射到 CHS 元组(“mod”是模运算,即余数,“÷”是整数除法,即丢弃任何小数部分的除法的商):

C = LBA ÷ (HPC × SPT)
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1

实现第二个函数(将 LBA 转换为 CHS),您可以从 0 循环到 1439,将每个 LBA 转换为 CHS,然后调用读取扇区函数。如果您打算导航 DOS 的FAT12FAT16磁盘格式等,这将非常有用。

于 2015-12-13T17:41:26.597 回答