0x120-從頭開始寫操作系統-啓動扇區與內存的關係及內存尋址的應用

目錄

回顧

上一篇,我們講到了以下內容:

  • 16-bit Real Mode 是 x86 系列 CPU 的一種工作模式,所有 x86 系列 CPU 啓動時,都處於 16-bit Real Mode
  • 16 bit 系統中,只有 64 KB 內存可以被識別,CPU 一次只能處理 16 bit 的數據
  • 8086 架構,因爲有 20 bit 的地址總線,所以尋址總量有 1 MB
  • 8086 架構的內存管理採用內存分段機制,內存分段將程序不同的部分加載到不同的不連續的內存中
  • 16-bit Real Mode 真實物理地址的計算公式爲:物理地址 = 段地址左移 4 位 + 偏移地址
  • 中斷可以讓 CPU 暫時停止當前任務,執行我們指定的任務,然後回去執行原先的任務
  • 中斷在 interrupt vector 中由一個數值做索引,interrupt vector 包含 interrupt service routines 的內存地址
  • 第一個 Hello World 程序使用中斷,在屏幕上顯示 HelloWorld 字符串

今日目標

本篇,我們將結束 16-bit Real Mode 的內容,儲備更多的知識,爲下一篇開啓 32-bit Protected Mode 做準備。

本篇內容主要以彙編代碼爲主,講解一些必要的指令。我們會從一個打印 16 進制字符串的程序開始,將

  • boot sector 與內存的關係
  • 字符串定義
  • 堆棧的使用
  • 方法調用
  • 條件跳轉
  • 文件包含
  • 物理內存尋址

這些內容一一覆蓋到。

需要大家注意的是,我們現在的編程環境和編程的內容都有很大的侷限性,所以不要將現在所講的東西不加推敲直接應用到其他環境中,會造成不正確的結果。

比如現在的環境只是 8086 16-bit,那麼不能認爲通用寄存器就只是 AX,而不去考慮是否因爲環境不同,寄存器會發生變化。32-bit 環境下,通用寄存器爲 EAX,64-bit 環境下,爲 RAX。

又比如目前的 boot sector 程序沒有分段,同樣不能認爲其他所有的彙編程序都不用分段。

當前討論的,是如何在 8086 架構下,從頭寫一個簡易操作系統,一切都圍繞這個目標來展開。

Boot Sector 與內存的關係

我們從研究 boot sector 程序與內存的關係開始。

回想之前,計算器啓動,BIOS 做自檢(專業術語叫 POST - Power-On Self-Test,詳見這篇文章),然後讀取存儲介質上的前 512 個字節,如果 0x55 和 0xaa 出現在第 510 和 511 個字節上,BIOS 就認爲這是一個正規的 boot sector,開始執行其代碼,然後加載操作系統。

一切程序的執行,第一步要將程序加載到內存。那麼 boot sector 被 BIOS 加載到內存的那一部分呢?

首先,肯定不是整個內存的起始位置(0x0)。因爲之前說過,遠在 BIOS 尋找 boot sector 之前,就已經做開始檢測硬件等的操作。想必有一些必要的指令,已經被加載到內存最初的位置上。另外,還記得 interrupt vector table 以及 interrupt service routines (ISRs),這些都必須首先存在於內存,我們在 boot sector 中才有能力去調用中斷,做相應的操作。

書中說,boot sector,會被 BIOS 固定加載到 0x7c00 這個位置上。

直接引用書上的內存示意圖(來源:https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf)。

在這裏插入圖片描述

原書中作者寫着寫着打了一個問號在這裏。本着懷疑一切的態度,我們要證實一下,boot secotr 是不是被加載到了 0x7c00 這個位置。

在這裏插入圖片描述

打印任意內存位置上的內容(以 16 進制輸出)

我們有能力打印字符(上一個 HelloWorld 程序),那麼要證實 boot sector 被加載到 0x7c00,只需要打印出該內存地址開始的連續一些字節,和 od 命令的輸出做比對,就能知道書中是否正確。記住 16-bit Real Mode 下內存沒有保護機制,所以,是可以訪問或者寫入到任意內存,目前也沒有什麼後果,大家可以自行嘗試。

這裏的關注點不要放在代碼上,後面會對代碼做深入討論。

打印 16 進制的代碼在 @cfenollosa 的 Github 上能找到。這裏需要兩個文件,一個是 print 方法,負責打印字符串,另一個,是 print_hex 方法,負責轉換 16 進制數爲字符串,然後調用 print 來打印。

print.asm:

print:
    pusha

; keep this in mind:
; while (string[i] != 0) { print string[i]; i++ }

; the comparison for string end (null byte)
start:
    mov al, [bx] ; 'bx' is the base address for the string
    cmp al, 0 
    je done

    ; the part where we print with the BIOS help
    mov ah, 0x0e
    int 0x10 ; 'al' already contains the char

    ; increment pointer and do next loop
    add bx, 1
    jmp start

done:
    popa
    ret

print_nl:
    pusha
    
    mov ah, 0x0e
    mov al, 0x0a ; newline char
    int 0x10
    mov al, 0x0d ; carriage return
    int 0x10
    
    popa
    ret

print_hex.asm:

; receiving the data in 'dx'
; For the examples we'll assume that we're called with dx=0x1234
print_hex:
    pusha

    mov cx, 0 ; our index variable

; Strategy: get the last char of 'dx', then convert to ASCII
; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.
; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40
; Then, move the ASCII byte to the correct position on the resulting string
hex_loop:
    cmp cx, 4 ; loop 4 times
    je end
    
    ; 1. convert last char of 'dx' to ascii
    mov ax, dx ; we will use 'ax' as our working register
    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros
    add al, 0x30 ; add 0x30 to N to convert it to ASCII "N"
    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'
    jle step2
    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7

step2:
    ; 2. get the correct position of the string to place our ASCII char
    ; bx <- base address + string length - index of char
    mov bx, HEX_OUT + 5 ; base + length
    sub bx, cx  ; our index variable
    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'
    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234

    ; increment index and loop
    add cx, 1
    jmp hex_loop

end:
    ; prepare the parameter and call the function
    ; remember that print receives parameters in 'bx'
    mov bx, HEX_OUT
    call print

    popa
    ret

HEX_OUT:
    db '0x0000',0 ; reserve memory for our new string

利用 cfenollosa 代碼,我們寫一個簡單的測試代碼:

boot_sect_mem_chk.asm

org 0x7c00
mov dx, [0x7c00] ; 將內存地址 0x7c00 位置上的數據,寫入到 dx 寄存器
call print_hex ; 調用 print_hex 方法輸出 dx 中的內容

jmp $  ; 跳轉到當前地址(無限循環)

%include "print.asm"
%include "print_hex.asm"

times 510 - ($ - $$) db 0 ; 512 個字節中剩餘字節全部填充 0
dw 0xaa55 ; 最後一個字節,是 0xaa55,讓 BIOS 知道這是 boot sector

編譯:

nasm -f bin boot_sect_mem_chk.asm -o boot_sect_mem_chk.bin

運行:

在這裏插入圖片描述

od 命令的輸出做對比:

od -t x1 -A n boot_sect_mem_chk.bin

在這裏插入圖片描述

證實了 boot sector 被加載到 0x7c00 內存的說法。也可以寫一個循環,輸出前 N 個字符,對比輸出與 od 命令是完全一致的。

在這裏插入圖片描述

不要忘了,x86 架構是小字節序,QEMU 中打印出來的每個字節,與 od 命令顯示的是相反的。

物理尋址的應用

前一篇文章鋪墊了很多關於 8086 架構物理地址計算的信息,現在,該嘗試一下在代碼中做應用,加深理解。

找到 Boot Sector 在內存中的前兩個字節的位置

我們還是拿 boot_sect_mem_chk.asm 舉例,順便開始講解一些必須知道的彙編指令。

我們的目標是,用前文所說的寄存器,換一種方式,使用段寄存器來找到 Boot Sector 的前兩個字節的內容,打印出來。

org 0x7c00
mov dx, [0x7c00] ; 將內存地址 0x7c00 位置上的數據,寫入到 dx 寄存器
call print_hex ; 調用 print_hex 方法輸出 dx 中的內容

jmp $  ; 跳轉到當前地址(無限循環)

%include "print.asm"
%include "print_hex.asm"

times 510 - ($ - $$) db 0 ; 512 個字節中剩餘字節全部填充 0
dw 0xaa55 ; 最後一個字節,是 0xaa55,讓 BIOS 知道這是 boot sector

我們先講一些完成這個任務的必要知識,然後完成這個任務,再然後,我們來拆解這個程序,進入彙編指令的講解。

寄存器

如果有人問說計算機有哪些寄存器?你的回答只是計算機有 AX,BX,CX,DX 等浙西寄存器,然後就沒有然後了。那麼,在繼續閱讀下面的內容之前,請再次全面來了解一下寄存器。

最基本的寄存器描述如下:

由於 CPU 訪問內存的操作會大大降低計算機運行效率,所以 CPU 內部內置了一些記憶單元用於臨時儲存要處理的小量數據,這些記憶單元,就被稱之爲寄存器。

下面展開討論寄存器。

寄存器的分類

x86 架構下,按照寄存器的作用,可以把寄存器分爲 5 大類,分別是:

  • 通用寄存器 General Registers
  • 段寄存器 Segment Registers
  • 指針寄存器 Pointer Registers
  • 索引寄存器 Index Registers
  • 控制寄存器 Control Registers

再一一展開。


- 通用寄存器 -

x86 架構的 CPU 包含 4 個通用寄存器,32 位架構下,這 4 個寄存器分別是:

EAXEBXECXEDX

相應的,我們現在討論的 80806 16 位架構下,這 4 個寄存器分別是:

  • AX:‘A’ 代表 Accumulator,該寄存器也稱爲累積寄存器,通常用於做數學運算
  • BX:‘B’ 代表 Base,該寄存器也稱爲基址寄存器,可以用於存放臨時值,也可以用於做內存尋址
  • CX:‘C’ 代表 Count,該寄存器也稱爲計數寄存器,通常用於做循環計數
  • DX:‘D’ 代表 Data,該寄存器也稱爲數據寄存器,通常用於輸入輸出的操作,同時也可以和 AX 配合用於大數值乘除的運算


- 段寄存器 -

對於段寄存器的講解比較重要,粗體字加深理解

8086 CPU 有 3 個常用段寄存器,3 個附加段寄存器。

常用段寄存器分別是:

  • 代碼段寄存器 Code Segment Register:代碼段包含所有的可以被執行的指令;代碼段寄存器存放代碼段的起始內存地址(邏輯地址)
  • 數據段寄存器 Data Segment Register:數據段包含程序需要的所有數據,常量,字符串等;數據段寄存器存放數據段的起始內存地址(邏輯地址)
  • 棧段寄存器 Stack Segement Register:棧段包含方法調用所需的參數,方法的返回地址等數據;棧段寄存器存放棧段的起始內存地址(邏輯地址)

邏輯地址指的是 16-bit 地址,也是後面段和段寄存器小結中提到的段選擇符。

例如,DS 的值是 0x6F70,那麼 mov ax, [0x1000] 中的物理內存地址是

0x6F70 * 10H + 0x1000 = 0x6F700 + 0x1000 = 0x70700

附加段寄存器分別是:

ESFSGS。這些寄存器提供了額外儲存數據的空間。通常,MOVSCMPS 等字符串操作的會用 ES。程序員也可以在代碼中手動指定這些寄存器的使用。


- 指針寄存器 -

3 個指針寄存器分別是:

  • 指令指針寄存器 Instruction Pointer(IP):存放下一條指令的內存地址偏移量,與 CS 一起,[CS:IP] 提供下一條指令的真實物理地址
  • 棧指針寄存器 Stack Pointer(SP):存放棧中的當前數據的內存地址偏移量,與 SS 一起,[SS:SP] 提供當前數據的真實物理地址,獲得當前位置上的數據
  • 基址指針寄存器 Base Pointer(BP):存放棧底的內存地址偏移量,與 SS 一起,[SS:BP] 提供當前方法參數的真實物理地址;同時,BP 還可以跟 DI,SI 這兩個索引寄存器一起使用,用於物理地址計算,如 [BP + SI + 0x10]


- 索引寄存器 -

2 個指針寄存器分別是:

  • 源索引寄存器 Source Index(SI):常用於字符串操作的源索引
  • 目標索引寄存器 Destination Index(DI):常用於字符串操作的目標索引

不過實際中,這兩個寄存器也常與段寄存器配合,進行物理地址尋址,如 [DS:SI],[ES:DI]。


- 控制寄存器 -

控制寄存器屬於高級話題,不在討論範圍。有興趣的同學參考這篇文章

在這裏插入圖片描述

段和段寄存器小結

8086 架構中,內存段十分重要,所以我們把段和段寄存器拿出來做一個小總結。

關於每一個段的特徵以及每個段與內存尋址的關係,總結如下:

  1. 8086 架構下,每個段容量最大 64 KB(216 Bits)
  2. 彙編中,任何段中的任一內存地址的尋址,都是相對於段的起始內存地址
  3. 一個段,總是開始於一個能被 16 整除(10 進制,或者 16 進制被 10H 整除)的內存地址上
  4. 任一段寄存器存放的,都是相應段的起始內存地址。段寄存器中的地址,被稱爲段選擇符(Segment Selector 這裏有詳解),段選擇符 * 16 (16 進制乘以 10H,二進制左移 4 位)之後的,纔是段地址(所以,前文勘誤,直接說段寄存器中儲存的是段地址是錯誤的表述)
  5. 物理地址,或者線性地址,是通過 段地址 + 偏移量 計算得到

來自 Wiki 的圖片,解釋物理地址的計算(二進制形式)。

在這裏插入圖片描述

完成任務

好了,有了上面的鋪墊,我們來使用段寄存器找到 boot sector 的前兩個字節。

首先,我們講 org 指令註釋掉。彙編的註釋,使用分號 ;

我們看一下注釋掉之後是個什麼情況,還能打印出我們需要的前兩個字節嗎?

答案是否定的。

在這裏插入圖片描述

爲什麼會出現這樣的情況?

先看一下這條指令

mov dx, [0x7c00]

[] 操作符中的地址,都是一個相對於段起始位置的內存偏移量

當我們使用

mov dx, [0x7c00]

的時候,事實上,assembler 內部是這樣處理這個內存尋址的

mov dx, [DS:0x7c00]

DS 是段寄存器,我們會使用段寄存器中的段選擇符,乘以 16 再加上 0x7c00 這個偏移量來計算最後的物理地址。

那我們來看一下沒有 org 指令的情況下,DS 寄存器的值是多少?

我們嘗試講 DS 的值打印出來,結果發現沒有輸出,意味着 DS 中沒有存放任何值(沒有值不代表是 0)。

在這裏插入圖片描述

因此,無法找到 boot sector 前兩個字節是理所當然的。

解決方案就很簡單了,存放一個段選擇符到 DS 中即可。

前文說過,彙編中所有尋址,都是相對於段的起始地址而言。我們已經知道並證實 boot sector 代碼會被加載到 0x7c00 這個位置,我們的目標是打印物理地址位 0x7c00 這個位置上的數據。

根據

物理地址 = 段選擇符 * 10H + 偏移量

的公式,DS 的值應該是 0x7c0,偏移量應該是 0x0

0x7c0 * 10H + 0x0 = 0x7c00

因此,修改代碼如下,即可得到 boot sector 前兩個字節。

在這裏插入圖片描述

這裏有一個點要說明一下,所有的段寄存器和索引寄存器都是不能直接寫入的,mov ds, 0x7c0 無法編譯通過,必須有一箇中間過渡,所以用 ax 作爲過渡,將值寫入到 ds 中。

拓展部分
另外,再拋出一個問題,爲什麼不能設置 DS 寄存器的值爲其他值,如 0x0,或者 0x100,然後設置偏移量到相應的值去獲取前兩個字符。

首先我目前的解釋是,因爲我們編譯出來的是原始文件,意味着所有字節都是數據,那麼將被加載到 1 個段中(一個段 64 KB,我們的數據只有 512 Bytes),段的起始邏輯地址,就應該被寫入到 DS 中,所有的對於段中字節的尋址,都以 DS 爲相對地址。
爲了證實這一點,我們可以獲取一下最後兩個字節 0xaa55。按照公式,DS 爲 0x7c0,偏移量爲 0x1fe(第 510 和 511 個字節)。

可以成功獲取到最後兩個字節的內容。
在這裏插入圖片描述
當然,我試過將 DS 設置爲 0x0,偏移爲 0x7c00 會有很奇怪的現象出現,大家自行嘗試。

程序拆解及必要彙編指令

拆解一下前文的程序,代碼如下:

org 0x7c00
mov dx, [0x7c00] ; 將內存地址 0x7c00 位置上的數據,寫入到 dx 寄存器
call print_hex ; 調用 print_hex 方法輸出 dx 中的內容

jmp $  ; 跳轉到當前地址(無限循環)

%include "print.asm"
%include "print_hex.asm"

times 510 - ($ - $$) db 0 ; 512 個字節中剩餘字節全部填充 0
dw 0xaa55 ; 最後一個字節,是 0xaa55,讓 BIOS 知道這是 boot sector

【1】:org 指令,明確告訴 assembler 我們的 Boot Sector 代碼被加載到 0x7c00 的位置
【2】:mov 指令,將相對於 0x0,偏移量爲 0x7c00 內存位置上的值寫入 dx
【3】:調用 print_hex 方法,打印出 dx 寄存器中的值
【4】:掛起 CPU
【5】【6】:包含兩個打印方法的文件
【7】:除去該行指令以上所有指令的長度,除去最後兩個字節的長度,其餘位置全部填充 0
【8】:最後兩個字節固定值,0xaa55

開始展開。

org 指令

這個解釋只有英文才能區分了,不知道該怎麼翻譯纔好,中文的翻譯都是指令。各種資料對於 org 的解釋,指出 org 不是一個 instruction,而是一個 directive。類似 C 語言中的 define

org 指令,在書中的解釋是,明確告訴 assembler 我們的 Boot Sector 代碼被加載到指定的位置(0x7c00)。但是範範這麼一句話,感覺什麼都沒有講明白。根據資料,assembler 內部,有一個 Location Counter(LC),它負責記錄當前內存中下一個可以用於存放編譯後指令的空位。

org 指令更改 LC 到指定的內存地址。例如這裏的 0x7c00

當前的 LC 的值,可以使用之前看到過的 $ 符號來表示。

我們可以打印出來看一下。

沒有使用 org 指令,LC 的值是 0000 (打印出來的亂碼至今未理解是爲什麼)

在這裏插入圖片描述

使用了 org 指令,LC 的值是指定值。

在這裏插入圖片描述

LC 的值,是根據指令遞增的,一條指令被編譯存放入上一個 LC 的位置之後,LC 會增加這個指令的長度,準備存放下一個指令。

下圖展示了 LC 在第一個 call print_hex 指令之後,遞增了 3 個字節。

在這裏插入圖片描述

LC 作爲概念大家明白就行。我試着找 LC 與內存尋址相關的資料,結果都沒有找到。意味着 LC 對於我們理解內存尋址來說沒有什麼作用。

但是我們可以通過程序來的行爲來進一步解釋 org 指令到底幹了什麼。

我們可以打印寄存器中的值,那麼,我就把所有寄存器的值全部打印出來,然後對比一下使用 org 指令和沒有使用 org 指令前後的區別,看是否是因爲 org 指令初始化了 DS 寄存器,讓 mov dx, [0x7c00] 可以獲取到相應數據。

對比結果如下圖。

使用了 org 指令,除了 sp 是一個非 0 值,其他所有寄存器都被設置成了 0x0。因此,根據尋址公式,0x0 * 10H + 0x7c00 = 0x0 + 0x7c00 = 0x7c00,就可以獲取到 boot sector 前兩個字節的內容。

在這裏插入圖片描述

然後我將 org 指令註釋掉,結果是各個寄存器全部是亂碼。最後也無法獲取到 boot sector 前兩個字符的內容。

在這裏插入圖片描述

可以初步得出結論,org 指令除了資料上說的設置 LC 到當前地址,還初始化段寄存器的值爲 0x0

jmp 指令

jmp $ 在這裏的作用,是做一個無限循環,讓 CPU 停在該指令處,不能再往下執行。

試想一下如果沒有這個無限循環,CPU 就會按着 CS:IP 一路往下執行,能執行的則執行,不能執行的就 crash,我們不想讓這樣的事情發生。

jmp 跳轉分 short jump,long jump,還分向前跳轉,向後跳轉。細看這個 jmp $ 指令,機器碼是 eb fe,還有很多可以挖掘的地方,它是一個 short jump,是一個 reverse short jump,意思是向後跳轉。具體操作是從 jmp $ 緊接着的下一個指令的地址開始算,往回跳轉兩個字節,因爲 eb fe 就是兩個字節,所以跳回指令本身,造成一個無限循環。

詳細不展開,關於 jmp $ 指令,好文一篇

times 指令

重複執行後面的操作 N 次。在 boot sector 程序中,最後兩個字節固定,因此,填充 0 的操作應該進行 times 指令當前的地址,減去段起始地址的結果這麼多次。

$ 操作符代表當前指令地址,$$ 代表段起始地址。

pusha popa 指令

調用方法的時候,如果方法不小心修改了寄存器的值,可能會造成意想不到的結果。我們不希望寄存器的值被修改,有一種辦法是在調用方法之前,將所欲寄存器的值以及方法的返回地址都 push 到棧中,然後調用結束,再全部 pop 回來。

這樣的操作很麻煩,所以 pusha 和 popa 指令,會幫助我們完成這一操作。pusha 在方法中調用一次,會將所有寄存器以及方法返回地址都保存到棧,popa 則執行相反的操作。

print_hex:
    pusha
...
	popa
	ret

條件控制指令

看完這篇文章,一切都很清楚

跳轉跳轉跟在 CMP 指令之後使用,如:

cmp ax, 0x4
je jump_point

jump_point:
	do_something_here

最常用的是下面幾個:

  • JE 如果相等,跳轉
  • JNE 如果不相等,跳轉
  • JG 如果大於目標,跳轉
  • JGE 如果大於等於目標,跳轉
  • JL 如果小於目標,跳轉
  • JLE 如果小於等於目標,跳轉

其他必要的彙編指令

最重要的部分已經講完了,接下來,用示例代碼的方式帶過剩餘簡單的部分。

  • 字符串定義

最後的 0,添加一個 null byte 作爲字符串結尾

HELLO:
    db 'Hello, World', 0

GOODBYE:
    db 'Goodbye', 0

  • 文件包含

關於文件包含,有一個問題還沒有解決,就是爲什麼文件包含要寫在 jmp $ 指令之後。我試過將他們放在文件其他地方,確實會發生無法預料的結果。有待研究。

%include "boot_sect_print.asm"
%include "boot_sect_print_hex.asm"
  • 堆棧的使用

關於棧,記住幾個點即可。

第一,後進先出 (LIFO);第二,BP 寄存器指向棧底,SP 寄存器指向棧頂;第三,棧從內存高位地址向低位地址增長;第四,push 一個值到棧,SP - 2;pop 一個值出棧,SP + 2

可以使用書中的代碼進行理解。

mov ah, 0x0e ; int 10/ ah = 0eh -> scrolling teletype BIOS routine
mov bp, 0x8000 ; Set the base of the stack a little above where BIOS
mov sp, bp ; loads our boot sector - so it won ’t overwrite us.
push 'A' ; Push some characters on the stack for later
push 'B' ; retreival. Note , these are pushed on as
push 'C' ; 16 - bit values , so the most significant byte
; will be added by our assembler as 0 x00.
pop bx ; Note , we can only pop 16 - bits , so pop to bx
mov al, bl ; then copy bl ( i.e. 8- bit char ) to al
int 0x10 ; print (al)
pop bx ; Pop the next value
mov al, bl
int 0x10 ; print (al)
mov al , [0x7ffe ] ; To prove our stack grows downwards from bp ,
; fetch the char at 0 x8000 - 0x2 ( i.e. 16 - bits )
int 0x10 ; print (al)
jmp $ ; Jump forever.
; Padding and magic BIOS number.
times 510 - ($ - $$) db 0
dw 0xaa55

總結

  • Boot Sector 被 BIOS 加載到 0x7c00 的內存位置
  • 用程序證實了 0x7c00 物理內存位置上,確實是我們的 Boot Sector 程序
  • 寄存器的分類,寄存器的作用
  • 用段寄存器來完成尋找 Boot Sector 前兩個字節內容的任務
  • 必要的彙編指令

下一篇,我們將講解如何讀取磁盤數據,之後就要開啓我們的 32-bit


推薦閱讀(參考鏈接):

  • https://wiki.osdev.org/Boot_Sequence
  • https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
  • https://nets.ec/Ascii_shellcode
  • https://thestarman.pcministry.com/asm/debug/Segments.html
  • https://www.tutorialspoint.com/assembly_programming/assembly_registers.htm
  • http://dewkumar.blogspot.com/2012/01/what-is-org-origin-directive-in.html#:~:text=What%20is%20ORG%20(origin)%20directive%20in%20assembly%20level%20language%3F,expression%20in%20the%20operand%20field.
  • https://stackoverflow.com/questions/38318230/how-to-display-register-value-using-int-10h
  • https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D02h:_Read_Sectors_From_Drive
  • https://stackoverflow.com/questions/50260277/is-there-a-difference-between-org-0x7c00-and-mov-ax-07c0h
  • https://thestarman.pcministry.com/asm/2bytejumps.htm
  • http://ps-2.kev009.com/wisclibrary/aix52/usr/share/man/info/en_US/a_doc_lib/aixassem/alangref/absolute_add.htm
  • http://et.engr.iupui.edu/~skoskie/ECE362/lecture_notes/LN2_html/text8.html
  • https://en.m.wikipedia.org/wiki/X86_memory_segmentation
  • https://en.wikipedia.org/wiki/Segment_descriptor
  • http://ref.x86asm.net/coder32.html
  • https://www.tortall.net/projects/yasm/manual/html/objfmt-bin.html
  • http://www.sce.carleton.ca/courses/sysc-3006/s13/Lecture%20Notes/Part5-SimpleAssembly.pdf
  • https://stackoverflow.com/questions/4903906/assembly-using-the-data-segment-register-ds
  • https://wiki.osdev.org/Real_Mode
  • https://www.daniweb.com/programming/software-development/threads/291076/whats-org-100h#:~:text=ORG%20(abbr.,only%20one%20segment%20of%20max.
  • https://www.nasm.us/doc/nasmdoc3.html#section-3.5
  • https://www.tutorialspoint.com/assembly_programming/assembly_conditions.htm
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章