这是代码的工作版本,但我对此有问题和疑问
- BIOS 返回软盘驱动器 = 231 而不是 0,因为我读取软盘应该是 = 0,因此当在
read:
中将dl
替换为 0 时,它无法读取磁盘,但当它替换reset:
中的dl
时,它会成功重置,以便在软盘驱动器 = 0 时重置哪个驱动器 - 当我在运行程序后将
pusha
放在 print_string 中的其余代码之前,将popa
放在 print_done 中的ret
之前时,它只打印bootmsg
- 将 0x0F00 替换为 0x1000,将 0xF000 替换为 0x10000 时,不会打印 Hello World,而是打印
S
我不知道为什么 - 将 0x0F00 替换为 0x1000,将 0xF000 替换为 es:bx 时(虽然它必须等于 0x10000),但
Hello World
和S
都不起作用
编辑:我正在使用 qemu
program_intialization:
org 0x7C00 ;load program at 0x7C00
push dx ;save drive number to stack appeared to be 231 not 0
jmp program_start ;go to program_start
print_string:
cld ;clear direction flag to increase si after copying the value of the address pointed by it
lodsb ;copy [si] into al then increase si
or al,al ;check if al is null
jz print_done ;if null then go to print_done
mov ah,0x0E ;else copy 0x0E to ah which is the video output code
int 0x10 ;then interrupt the program with 0x10 the video routine code which will print the character in al
jmp print_string ;and print the next character until null is found
print_done:
mov al,0x0A ;copy the code of line feed into al
mov ah,0x0E ;copy 0x0E to ah which is the video output code
int 0x10 ;interrupt the program with 0x10 the video routine code which will print the character in al (the line feed)
mov al,0x0D ;copy the code of carriage return into al
mov ah,0x0E ;copy 0x0E to ah which is the video output code
int 0x10 ;interrupt the program with 0x10 the video routine code which will print the character in al (the carriage return)
ret ;return to the place the function is called from
program_start:
xor ax,ax ;intialize ax to 0
mov ds,ax ;intialize ds to 0
mov es,ax ;intialize es to 0
mov si,bootmsg ;copy bootmsg address to si
call print_string ;print bootmsg
reset:
mov si,rstmsg ;copy reset adress to si
call print_string ;print reset
mov ah,0 ;drive reset code
pop dx ;get drive number into dl
push dx ;save drive number again
int 0x13 ;reset drive
jc reset ;if failed try again
mov ax,0x0F00 ;copy 0x0F00 to ax
mov es,ax ;copy 0x0F00 to es
xor bx,bx ;make bx = 0x0000
mov si,scs ;if success
call print_string ;print scs
read:
mov si,rdmsg ;copy reset adress to si
call print_string ;print reset
pop dx ;get the drive number into dl
mov ah,0x02 ;copy disk read code to ah
mov al,1 ;read 1 sector
mov ch,0 ;on track 0
mov cl,2 ;read sector number 2
mov dh,0 ;head number 0
int 0x13 ;read from disk to memory address es:bx (0xF000)
jc read ;if failed try again
mov si,scs ;if success
call print_string ;print scs
end:
mov si,0xF000 ;which is es:bx
call print_string0 ;print Hello World read from memory 0xF000
cli ;clear all interupts
hlt ;halt the system
bootmsg db "Bootloader v0.1",0
rstmsg db "Trying to reset Floppy Disk",0
rdmsg db "Trying to read Floppy Disk",0
scs db "Operation Successful",0
times 510 - ($-$$) db 0 ;fill the rest of bootsector with 00
dw 0xAA55 ;boot magic number
msg db "Hello World",0 ;add hello world to the next sector (512bytes) which will be read
最佳答案
org 0x7C00 push dx ;save drive number to stack
您在使用堆栈时不知道它在内存中的位置以及它有多少空间可供您使用,这一点很重要,这与您的 pusha
/popa
问题有关.
; reset: pop dx ;get drive number into dl push dx ;save drive number again
; read: pop dx ;get the drive number into dl
在重置中,您将DriveCode保留在堆栈上。但是,在读取中,您不会将 DriveCode 放回。当失败时,读取尝试重复该操作,DriveCode 无法再从堆栈中弹出,并且一些随机值将取代它!
When I put
pusha
in print_string before the rest of the code andpopa
in print_done beforeret
after running the program it only printsbootmsg
这是您应该编写的代码:
print_string:
PUSHA <<< added
cld
lodsb
or al,al
jz print_done
mov ah,0x0E
int 0x10
jmp print_string ;and print the next character until null is found
print_done:
mov al,0x0A
mov ah,0x0E
int 0x10
mov al,0x0D
mov ah,0x0E
int 0x10
POPA <<< added
ret
发生的情况是,每次打印一个字符时,代码都会跳回到 print_string 处的开头,其中另一组寄存器被插入堆栈(因为 pusha
指令被重复)。对于包含 15 个字符的 bootmsg 文本,这种情况会发生 16 次。当最终找到终止零时,popa
指令将仅执行一次,从而在堆栈上留下大量不需要的字节。 ret
指令没有可返回的有意义的地址,因此程序崩溃。
When replacing 0x0F00 with 0x1000 and 0xF000 with 0x10000 it doesn't print Hello World but it prints
S
I don't know why
在您来自的代码中:
mov ax,0x0F00
mov es,ax
xor bx,bx
...
mov si,0xF000 ;which is es:bx
call print_string0 ;print Hello World read from memory 0xF000
至:
mov ax,0x1000
mov es,ax
xor bx,bx
...
mov si,0x10000 ;which is es:bx
call print_string0 ;print Hello World read from memory 0x10000
您的设置 ES=0x1000
对应于线性地址 0x10000 (65536) 是正确的。然而,像 mov si, 0x10000
这样的指令不会加载 SI
寄存器中的特定地址,因为它太大,无法放入字大小的寄存器中(允许的值范围为0 到 65535)。大多数汇编器不会提示这一点,只是将截断的值存储在寄存器中,例如 0。您所说的打印的“S”来自内存中的第一个字节。
When replacing 0x0F00 with 0x1000 and 0xF000 with es:bx (while it must be equivalent to 0x10000) it doesn't
Hello World
norS
如果将 mov si,0xF000
替换为 push es
push bx
pop si
pop ds
,它本来可以正常工作,因为 print_string 例程是从 DS:SI
运行的,并且您必须以这种方式使用它。
BIOS returns the floppy drive = 231 not 0 as I read floppy should be = 0 so when replacing
dl
with 0 inread:
it fails to read the disk but when it replacingdl
inreset:
it resets successfully so which drive is reset while the floppy drive = 0
考虑到上述错误和误解,我最好的猜测可能是您根本没有收到驱动器代码 231,但可能找错了地方。
我肯定会保留 BIOS 提供的值,而不是强制使用诸如 DL=0
之类的值。
这是您的代码的改进版本:
- 堆栈在引导扇区下方
- 存储在内存中的驱动器号(引导扇区的第一个字节)
- 子程序不妨碍执行路径
- 一些代码大小优化
org 0x7C00
cld ; Gets DriveCode later
xor ax, ax
mov ds, ax
mov ss, ax
mov sp, 0x7C00
mov [0x7C00], dl
mov si, bootmsg
call print_string
reset:
mov si, rstmsg
call print_string
mov dl, [0x7C00] ; DriveCode
mov ah, 0 ;drive reset code
int 0x13 ;reset drive
jc reset ;if failed try again
mov si, scs
call print_string
read:
mov si, rdmsg
call print_string
mov ax, 0x0F00
mov es, ax
xor bx, bx
mov dh, 0 ;head number 0
mov dl, [0x7C00] ;get the drive number into dl
mov cx, 0x0002 ;on track 0 read sector number 2
mov ax, 0x0201
int 0x13 ;read from disk to memory address es:bx (0xF000)
jc read ;if failed try again
mov si, scs
call print_string
end:
mov si, 0xF000 ;which is es:bx
call print_string0 ;print Hello World read from memory 0xF000
cli ;clear all interrupts
hlt ;halt the system
jmp end
print_string:
pusha
mov bx, 7
print_more:
lodsb
or al, al
jz print_done
mov ah, 0x0E
int 0x10
jmp print_more
print_done:
mov ax, 0x0E0A
int 0x10
mov al, 0x0E0D
int 0x10
popa
ret
bootmsg db "Bootloader v0.1",0
rstmsg db "Trying to reset Floppy Disk",0
rdmsg db "Trying to read Floppy Disk",0
scs db "Operation Successful",0
times 510 - ($-$$) db 0 ;fill the rest of bootsector with 00
dw 0xAA55 ;boot magic number
msg db "Hello World",0 ;add hello world to the next sector
ps:不要忘记删除call print_string0
中的拼写错误。
关于assembly - 从软盘读取数据问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71813976/