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

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


在上一講中我們向屏幕打印字符串“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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章