因為程式需要決定要讀取的扇區數目,首先需要確定整個程式的大小
program_length dd program_end ;程式總長度[0x00],在8-2中
mov dx,[2] ;載入器部分
mov ax,[0]
接下來用存放在dx,ax暫存器中的內容除以512(存放在bx中)利用迴圈得到總扇區數目
div bx
cmp dx,0
jnz @1 ;未除盡
dec ax ;已經讀了乙個扇區,扇區總數減1
注意減一是因為一開始已經讀了乙個扇區
接著使用昨天的read_hard_disk函式,在硬碟中讀取內容
mov ax,ds ;資料存放在記憶體中ds:ax區域
mov ds,ax
xor bx,bx ;每次讀時,偏移位址始終為0x0000
inc si ;下乙個邏輯扇區
call read_hard_disk_0
loop @2 ;迴圈讀,直到讀完整個功能程式
pop ds ;恢復資料段基址到使用者程式頭部段
注意這裡第二行,ax加上0x20的原因是防止資料一直處在乙個邏輯段中,導致段內偏移暫存器過大。因此將段暫存器增加512位元組,每一次段內偏移都從開始,同時也不會浪費空間。
其餘的指令是為了給read_hard_disk傳遞引數設定的
我們現在已經有了讀取的部分程式,即程式開頭,現在需要確定每一部分程式在記憶體空間中的位址
簡單來說,輸入的是硬碟空間中的位址,輸出的是記憶體中的段基位址
calc_segment_base: ;計算16位段位址
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02] ;這兩句將硬碟空間中位址存到dx:ax中
shr ax,4 ;ax右移四位,空出開頭四位位址位置
ror dx,4 ;設定段間位址位置
and dx,0xf000 ;去除dx後幾位
or ax,dx ;兩暫存器相加,得到總段基位址
pop dx
ret
儘管dx:ax是32位位址,但是他只有20位有效。獲得段基位址的方法就是拋棄小的後四位,再把大的四位放在前面(因為其他的位作為段內偏移)以上程式就是幹這件事的
這裡shr和x86-64中彙編不同,始終為邏輯右移,並且多餘的位會被存放在暫存器cf中
ror指令指,向右移動並且把溢位的位放在cf和目標暫存器開頭的位置,像乙個圓圈的迴圈
之後再將之前程式頭部中已經定義過的各個段的位置放到上述程式中,處理過後輸出到[bx]中,接下來,準備將控制權交給使用者程式
入口方式是:間接絕對遠跳轉指令
jmp far [0x04] ;轉移到使用者程式
;使用者程式部分
code_entry dw start ;偏移位址[0x04]
dd section.code_1.start ;段位址[0x06]
注意跳轉之後,處理器會自動重寫cs和ip暫存器的內容,那麼暫存器會無腦直接獲得下一次執行的位置
1.相對短轉移:0xeb
jmp short infinite
注意infinite代表的立即數或者標號的值只能是最大一位元組大小,同時也必須有short關鍵字
2.16位相對近轉移:0xe9
jmp near infinite
jmp near 0x3000
和之前那個指令的區別是,此處可以是兩位元組跳轉大小
以上兩指令立即數部分都是相對於目標位址的偏移量,這也就是相對的含義
3.16位間接絕對近轉移
jmp (near) bx
jmp (near) cx
注意,此時不是相對偏移,而是直接用bx/cx中的內容代替指令指標暫存器ip中的內容
4.16位直接接絕對遠轉移
jmp 0x0000:0x7c00
5.16位間接絕對遠轉移
jmp_far dw 0x33c0,0xf000
jmp far [jmp_far]
jmp far [bx]
注意這句話的意思是:從cs=0x33c0,ip=0xf000處取出兩個字,分別用來代替段暫存器cs和指標暫存器ip的內容
這裡16位的意思就是,取出的大小是兩個字的大小
0010_0110_0101_0101
a.給出標號的情況下,是絕對轉移
jmp near label_proc
jmp short label_proc
b.
jmp bx
c.jmp [bx]
d:jmp 0xf000:0x0002
e:jmp_far dw [0x82],[0x80] ;0x82存放段位址,需要倒置
f:接著把兩個位元組移到相應暫存器,同e
start:
;初始執行時,ds和es指向使用者程式頭部段
mov ax,[stack_segment] ;設定到使用者程式自己的堆疊
mov ss,ax
mov sp,stack_end
mov ax,[data_1_segment] ;設定到使用者程式自己的資料段
mov ds,ax
以上程式所做工作
設定ss,sp(棧指標) 從 載入器的空間到使用者程式的空間
設定dx,將段基址從載入器拿出到使用者部分
這裡先把想要輸出的字串預先db定義,之後通過判斷字元是否為0來判斷是否到達結尾。在判斷到達結尾之後exit,未到達結尾則呼叫put_char函式,之後繼續迴圈。直到exit
介紹:螢幕游標
螢幕游標繼承於歷史悠久的顯示卡。
游標在螢幕上的位置儲存在顯示卡的兩個游標暫存器中,每個暫存器是8位的,合起來形成乙個16位的數值
標準vga模式,每行80個字元,總共25行
因此,最右下角的游標序號是25*80-1(因為從0開始)=1999。將這個值儲存在之前說的16位暫存器中,就可以得到游標位置了
之後是顯示文字階段
要處理的是 換行符,回車符(回到行首)和普通可見字元
注意這裡的換行不是預設到行首,而是直接在vga模式下,行數+1
在顯示字元的時候,用到乙個技巧:游標*2即為要顯示的字元
.put_other: ;正常顯示字元
mov ax,0xb800
mov es,ax
shl bx,1 ;游標*2為ascii碼存放的位置
mov [es:bx],cl
;以下將游標位置推進乙個字元
shr bx,1 ;恢復原先游標位置
add bx,1 ;游標位置自增
在處理換行的時候,我們還要注意是否超出螢幕的顯示區域
在樸實無華的機器語言中,處理滾動螢幕內容的方法是
將資料從乙個記憶體區域搬運到另外乙個(整體向上移動),再清除最後一行使之變為空白
核心指令:movsw
源區域開始位置:ds:si
目標區域開始位置:es:di
cx:傳送字數
在這裡:源區域是第二行第一列開始,目標區域從第一行第一列開始,傳遞字元1920(24*80)(正好乙個字元需要乙個位元組表示)
執行rep movsw完成工作
接著重置游標,完成接下來的指令
作業下一次一定補上!今天太晚了,睡覺啦?
保護模式到實模式
弄了幾天了,終於弄好了,呵呵,也得到了不少東西。從保護模式到是模式的轉換一定要注意的有 1 程式開始時在實模式下要有自己的堆疊段,進入保護模式前先暫存ss及sp的值至某記憶體處,以便從保護模式返回實模式後恢復到原先的堆疊。2 返回實模式前需把各段暫存器設定為規範段,包括ss也要設定 3 返回實模式前...
實模式與保護模式
實模式與保護模式 1.實模式,又叫實位址模式,cpu完全按照8086的實際定址方法訪問從00000h fffffh 1mb大小 的位址範圍的記憶體,在這種模式下,cpu只能做單任務執行 定址公式為 實體地址 左移4位的段位址 偏移位址,即 實體地址是由16位的段位址和16位的段內偏移位址組成的。2....
實模式與保護模式
實模式 即實位址訪問模式 它是intel公司80286及以後的x86 80386,80486和80586等 相容處理器 cpu 的一種操作模式。實模式被特殊定義為20位位址記憶體可訪問空間上,這就意味著它的容量是2的20次冪 1m 的可訪問記憶體空間 物理記憶體和bios rom 軟體可通過這些位址...