自己動手從零寫桌面作業系統GrapeOS系列教學——16.封裝列印字串函數

2023-03-17 12:00:30

學習作業系統原理最好的方法是自己寫一個簡單的作業系統。


在上一講中我們向螢幕列印字串「GrapeOS」用了十幾行組合程式碼,如果要輸出的字元比較多,這種方法太繁瑣了。本講我們將列印字串封裝成一個函數,使用時就方便多了。

一、mbr7.asm

mbr7.asm程式碼如下:

org 0x7c00 ;如果沒有該行將無法正確列印要顯示的字串。

;初始化段暫存器。
mov ax,cs
mov ds,ax ;ds指向與cs相同的段。
mov ax,0xb800
mov es,ax ;本程式中es專用於指向視訊記憶體段。

;列印字串:"GrapeOS boot start."。
mov si,boot_start_string
mov di,80 ;在螢幕第2行顯示。
call func_print_string

stop:
hlt
jmp stop 

;列印字串函數。
;輸入引數:ds:si,di。
;輸出引數:無。
;ds:si 表示字串起始地址,以0為結束符。
;di 表示字串在螢幕上顯示的起始位置(0~1999)。
func_print_string:
mov ah,0x07 ;ah表示字元屬性,0x07表示黑底白字。
shl di,1 ;乘2(螢幕上每個字元對應2個視訊記憶體位元組)。
.start_char: ;以點開頭的標號為區域性標號,完整形式是 func_print_string.start_char,但在同一個全域性標號func_print_string內部不需要寫完整形式。
mov al,[si]
cmp al,0
jz .end_print
mov [es:di],ax ;將字元和屬性放到對應的視訊記憶體中。
inc si
add di,2
jmp .start_char
.end_print:
ret

boot_start_string:db "GrapeOS boot start.",0

times 510-($-$$) db 0
db 0x55,0xaa

下面我們來編譯執行,看看效果:

從上面QEMU的截圖中看到,我們在螢幕第二行成功列印出了字串「GrapeOS boot start.」,符合預期。

二、拓展講解偽指令org

如果我們將程式碼中的第一行「org 0x7c00」刪除掉,會怎麼樣呢?請看下面實驗截圖:

從上面QEMU截圖中可以看到,螢幕第二行顯示出了一個奇怪的字元,這並不是我們想要顯示的字串「GrapeOS boot start.」。原因是程式碼「org 0x7c00」表示本程式將來會載入到地址為0x7c00的記憶體處,這樣在組合器組合時標號「boot_start_string」才能計算出正確的地址。如果沒有「org 0x7c00」,組合器預設會認為本程式將來會載入到地址為0x0的記憶體處,標號「boot_start_string」計算出的地址會在記憶體的前512個位元組中,這明顯是錯誤的地址,列印出來的字串自然也是錯的。
下面我們用反組合驗證一下:

從上面的反組合截圖可以看到,此時標號「boot_start_string」被計算為0x29。
我們將程式中的「org 0x7c00」恢復,重新組合後反組合,截圖如下:

從上面的反組合截圖可以看到,此時標號「boot_start_string」被計算為0x7c29,這才是正確的。


本講視訊版地址:https://www.bilibili.com/video/BV1qT411D7oV/
本教學程式碼和資料:https://gitee.com/jackchengyujia/grapeos-course
GrapeOS作業系統QQ群:643474045