assembly - 从软盘读取数据问题

标签 assembly x86 x86-16 bootloader

这是代码的工作版本,但我对此有问题和疑问

  • 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 WorldS 都不起作用

编辑:我正在使用 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 and popa in print_done before ret after running the program it only prints bootmsg

这是您应该编写的代码:

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 nor S

如果将 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 in read: it fails to read the disk but when it replacing dl in reset: 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/

相关文章:

assembly - eax 如何存储大于 4 个字节的返回值?

x86 - ELF 文件类型 - ET_EXEC 和 ET_DYN

assembly - 重定位被截断以适合 : 16 against `.text'

assembly - 我应该为 Intel 8086 DOS 程序集使用什么代码框架?

assembly - 试图获取当前运行的 .COM 程序的名称。它存储在哪里?

c - 编译时asm中不可能的约束

assembly - ldr 内存加载指令可以从内存的堆栈部分加载

汇编语言: printing lowercase to uppercase

html - x86 汇编 DIV 素数分解

assembly - 汇编器: why BCD exists?