2022年2月13日 星期日

作業系統前的程式


我這邊寫的不一定是對的,可能有些前提條件,不過我可能還不是全懂?

一直以來都很想自己寫個 x86 作業系統來玩,相關的書也買了好多本,但一直沒有時間好好消化。終於在這星期開始來做些小嘗試,大部份書的第一個例子,都是藉由 BIOS 的幫助,來把磁區 0 的 512 Byte 資料載入到某塊記憶體,此資料是以 0xAA55 來結尾,故扣掉這兩個 magic number 後,我們應該還有 510 Byte 可以來寫些程式。

接著的例子就是會介紹如何切換到 big real mode 或是保護模式,好突破記憶體的限制,再來可能是想辦法把硬碟上的程式載入到記憶體,接著是初始化 C 語言環境,然後我們就可以有好用的 C 語言可用了。

雖然大部份的書入門都是這樣介紹,但我個人比較好奇的是 510 Byte 大概可以寫下怎樣的程式?如果把 x86 CPU 當成 MCU 來操作,在這樣的大小之下,可以寫一個猜數字或是簡單的打磚塊遊戲嗎?

故我第一個程式便是先嘗試用到 INT 0x10 和 INT 0x16 的 BIOS 中斷,這樣就可以印東西到螢幕,也可以讀取鍵盤的輸入字元。即使之後可能有很大機會就只是玩到這裡為止,但至少是個開始。

真正的程式很簡單不到 30 行,機器碼也只有 30 Byte,在 qemu 上跑起來也算順利。


但在真實的機器上跑便遇到兩個問題:

01. 在我家的板子上無法使用 INT 0x16 讀取鍵盤。
02. 在我自己的筆電上無法使用 USB 隨身碟開機。

我決定先把這兩個問題搞定就好。

2022/02/14 更新

我的程式沒有使用 org 告知組譯器開始位置 0x7C00,那是因為我並未宣告字串的空間,故沒有定址到那塊空間的需求,即使我有宣告字串,也可以自己計算 offset 的位置,當然使用 org 讓組譯器幫你計算是比較好的作法。順帶一提,這個 0x7C00 不是 INTEL 的標準,而是當初開發 IBM PC 5150 時用的,就一直延用至今,變成一個類似 BIOS 的標準。

2022/02/15 更新

Short jump—A near jump where the jump range is limited to –128 to +127 from the current EIP value.

opcode EB 是短跳轉,後面帶的是從目前的 EIP 要跳多少位置。這個位置是 -128 ~ +127。

故 section start 的最後一道指令 jmp check 轉成 opcode 是 EB E4(負數,二補數法),也就是往回跳 28 個 Byte,也就是 0x02 的位置,也就是 section check 的第一道指令 mov ah, 0x0d,也就是 opcode B4 0D。

mov 會隨著使用不同的暫存器而有不同的 opcode。


2022/03/08 更新

不懂 x86 也不懂 BIOS 的我,實在不知道為什麼 Legacy BIOS 的鍵盤中斷 INT 0x16 無法使用?沉思中 ...

2022/03/09 更新

找到原因了,從 Google 得知,某些 BIOS 中斷需要 stack 才能正常工作,故我們必須一併設定 SS 和 SP,但我只設定這兩個還是不正常,故把 DS、ES 也順便一起設定。

org 0x7c00
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0xb800

雖然現在鍵盤中斷可以正常工作,但我判斷是否按下 enter 鍵的 code 無法成功判斷,目前還要努力,至於其他鍵是沒有問題的。

2022/03/10 更新

即使是支小程式,在真實的機器上跑還是有很多眉角要注意,我終於知道為什麼我嘗試想要換行會無法成功,原因是我誤用了 BL 暫存器,0EH 的 INT 10h 中斷會用到 BL,但我嘗試在比較時對這個暫存器設值,導致 INT 10h 異常,同樣的之前我以為要設定 stack 才能正常使用鍵盤中斷也是錯的,只要我不要用到 BL 暫存器,即使不設定 stack,鍵盤中斷仍然是可以使用的。

寫組語用暫存器真的是要小心呀XD

最後版的程式

;org 0x7c00
;mov ax, cs
;mov ds, ax
;mov es, ax
;mov ss, ax
;mov sp, 0xb800

init:
    jmp start

check:
    mov cl, 0x0d
    cmp al, cl
    je nextline
    
print:
    mov ah, 0x0e  ;tty mode
    int 0x10

    jmp start

nextline:
    mov ah, 0x0e  ;tty mode
    
    mov al, 0x0d
    int 0x10
    
    mov al, 0x0a
    int 0x10
    
    jmp start

nextline1:
    mov ah, 0x03
    int 0x10
    
    inc dh
    mov dl, 0x00
    
    mov ah, 0x02
    int 0x10

start:
    mov ah, 0x10
    int 0x16
    jmp check

debug:
    ;mov al, 0x4c
    out 0x80, al
    hlt
    jmp debug

; padding and magic number
times 510 - ($-$$) db 0
dw 0xaa55

沒有留言:

張貼留言