1

我正在开发一个“只在过程中握住你的手,使用汇编编写你自己的操作系统”项目。除了硬盘读写之外,我已经用 MikeOS 之类的东西从头开始编写了所有内容。读写的重要性在于允许操作系统大于512长度基本输入输出系统内存。

我编写了一个测试用例,用于从硬盘读取并在屏幕上打印第一个字符。使用qemu,我可以定义哪个文件用作硬盘驱动器,因为我定义了程序可以在读取函数中断期间使用 0x80 选项指向该硬盘驱动器的寄存器的位置。

第一个测试代码没有选择硬盘驱动器,因此读取代码将读取它自己的运行二进制文件并产生一个“a”,假设由于二进制文件在加载到的运行文件的开头产生了一个“a”(ascii)基本输入输出系统。_ _ _ _

所以我的问题是......

  1. 当我定义硬盘驱动器选择时,为什么它不像我没有定义硬盘驱动器时那样读取屏幕上文件中的字符串“Hello”?

  2. 我的代码开头是否正确,屏幕上生成的“a”是巧合,在模拟器中运行的生成的二进制文件的开头有一个“a”?

为了大家,请用解释和代码回复。我在 Stack Overflow 中发现了两个问题,它们都用英文解释来回答他们的代码有什么问题,但没有代码可以证明为什么他们的解释是正确的。解释但不展示你的工作是一个有争议的问题。

不要建议我使用 C、C++ 或除汇编之外的任何其他语言,如果您已经完成研究并且我已经完成研究并反汇编高级语言(如 C、C++ 或其他任何语言),您会看到一个简单的“定义并在控制台上读取字符串”不仅要大得多,而且在效率方面也很粗心。这些反汇编的 C 代码与手写汇编示例的比较也将在项目文件中发布。

感谢您的宝贵时间,当我将项目作为开源教程发布时,社会将感谢您为与我处于相同位置的其他人节省了多少时间。(人类知识属于世界——反垄断电影,2001 年)

[bits   16]
[org 0x7c00]

message db "Hello"

mov ah, 0x02    ;point to read sector function
mov ch, 0x00    ;ch track/cylinder number
mov dh, 0x00    ;dh head number
mov cl, 0x00    ;cl sector number
mov dl, 0x80    ;drive number 80 is drive0, 81 is drive1
int 0x13

;read disk into terminal.... might be working, just displays 'a'...
mov ah, 0x0e
mov al, [bx]
int 0x13

;Purpose, move to the next character read from file
mov ah, 0x0e
mov es, bx
mov al, [bx]
int 0x13

times 510 - ($-$$) db 0
dw 0xAA55



;==============================================================
;               Reference Notes
;==============================================================
;
;;;;;;;;;;;;; Interupt 02 - Read from sector ;;;;;;;;;;;;
;AH = 02
;AL = number of sectors to read (1-128 dec.)
;CH = track/cylinder number  (0-1023 dec., see below)
;CL = sector number  (1-17 dec.)
;DH = head number  (0-15 dec.)
;DL = drive number (0=A:, 1=2nd floppy, 80h=drive 0, 81h=drive 1)
;ES:BX = pointer to buffer
;
;on return:
;AH = status  (see INT 13,STATUS)

;AL = number of sectors read
;CF = 0 if successful
;   = 1 if error

;;;;;;;; Inturrupt 13 - Video card functions ;;;;;;;;
;AH = 02, means TTY mode, print to console mode
;AL = which character to print to console
4

1 回答 1

2

通过将message代码放在开头,处理器将尝试将字符串解码为指令并执行它们。这可能导致意外行为和崩溃。您可以将数据放在代码之后和引导签名之前。汇编器 ( NASMhello ) 生成了一个包含引导加载程序开头的字节序列。CPU 无法区分构成字符串的字节或实际代码。CPU 看到一个字节序列并尝试解码字节并执行它们。

您的代码不会阻止处理器在我们实际指令结束后执行。您可以通过使用jmp $创建无限循环或关闭中断和hlt处理器来解决此问题,并cli使用hlt.

Int 13h/ah=2h磁盘读取要求:

  • 设置要在AL中读取的扇区数
  • 需要在中断调用之前在ES:BX中读取磁盘的段:偏移地址
  • 不要硬编码 DL(引导驱动器)并使用 BIOS 在 DL 中传递给我们的内容
  • 扇区号(与柱面和磁头不同)是基于 1 的值,而不是基于 0 的值。这意味着磁盘上的第一个扇区(引导加载程序)位于 CHS=(0,0,1),而不是 (0,0,0)。使用扇区号 0 无效。

此引导加载程序将引导加载程序的副本从 BIOS 传递给DL中引导加载程序的引导磁盘中的 Cylinder, Head, Sector(CHS) (0,0,1) 读取到内存中的 0x0000:0x7E00 。此代码遵循我的通用引导加载程序提示,设置我们自己的堆栈并将DS寄存器显式设置为 0。

bits   16
org 0x7c00

    cld             ; Set forward direction for string related functions like LODSB
    xor ax, ax      ; XOR register with itself sets register to 0
    mov ds, ax      ; DS = 0x0000
    mov ss, ax      ; SS:SP =0x0000:0x7C00
    mov sp, 0x7c00  ; Set SS:SP since we will be loading data from disk to memory
                    ;    We want to ensure the data we read is not in the same
                    ;    memory as the stack. Stack will grow down from 0x0000:0x7c00
                    ;    in memory just below the bootloader

    ; Read 1 sector starting at CHS = 0,0,1 (the boot sector)
    ; to 0x0000:0x7E00. This creates a copy of the bootloader
    ; in memory in the memory just after where we are loaded at
    ; 0x0000:0x7c00
    mov es, ax      ; ES:BX memory to read to 0x0000:0x7E00
    mov bx, 0x7e00  ;     0x0000:0x7E00 is in the memory just after our bootloader
                    ;     which ran from 0x7c00 to 0x7dff.
    mov al, 0x01    ; Number of sectors to read
    mov ah, 0x02    ; point to read sector function
    mov ch, 0x00    ; ch track/cylinder number
    mov dh, 0x00    ; dh head number
    mov cl, 0x01    ; cl sector number
                    ; dl is set by the BIOS before transferring control to our bootloader
    int 0x13

    ; Print the message from the copy of the bootloader loaded at 0x7E00
    mov si, message + 0x200
    call print_string
    cli
    hlt

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:               ; Routine: output string in SI to screen
    mov ah, 0eh             ; BIOS tty Print
    xor bx, bx              ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 10h                 ; print character
.getch:
    lodsb                   ; Get character from string
    test al,al              ; Have we reached end of string?
    jnz .repeat             ;     if not process next character
.end:
    ret

message db "Hello", 0

times 510 - ($-$$) db 0
dw 0xAA55

print_string是我编写的打印 NUL 终止字符串的函数。字符串的内存偏移量在SI寄存器中提供。

于 2018-03-12T22:26:32.683 回答