x86 16位真實模式 04——完善程式
鎮樓圖
Pixiv:岸Yasuri
〇、BX暫存器的特性
通用暫存器中也有一類特殊的,BX暫存器可以像資料段地址的變數一樣來使用
/*比如*/
mov [0],1234h
mov [2],5678h
/*為資料段ds:0~ds:3賦值*/
mov bx,0
mov ax,[bx]
add bx,2
mov ax,[bx]
其他大部分暫存器都無法做到這樣,而bx暫存器卻可以使用這樣的語法
基於bx暫存器的特性,我們不能再簡單地將bx暫存器理解為一個通用暫存器,它還常常作為資料段的一個地址變數
一、inc指令、loop指令與CX暫存器
inc
這個指令類似於C語言的++
inc bx
等效於add bx,1
loop
這個指令允許我們使用迴圈結構
比如求解\(2^{15}\)(目前還沒學乘除,組合語言也沒有pow這樣一個函式)
assume cs:code
code segment
mov ax,1
mov cx,15
s: mov ax,2
add ax,ax
loop s
/*這是一個迴圈結構*/
mov ax,4c00h
int 21h
code ends
end
標號
你在某一程式碼處設定標號s,在下面再寫上loop <標號>
即可設定一個迴圈段
從s的這一行程式碼開始到loop結束為要迴圈的程式碼
這裡不斷迴圈的程式碼是
mov ax,2 add ax,ax
迴圈次數如何確定?
在8086組合語言中,次數完全由CX來定,我們已經知道CX暫存器可以用來計算一個彙編程式的程式碼行數,但同時CX暫存器還擔當了作為迴圈次數的一個作用
你設定CX的值為多少就迴圈多少次
mov cx,15/*設定次數為15*/
...
loop s/*會隱含地讓cx-1*/
/*當cx為0時則退出迴圈
因此你設定cx為多少就迴圈就執行多少次
*/
迴圈結構框架
mov cx,次數
迴圈體
loop 標號
二、debug與masm的一個不同的細微的處理方式
debug是用來除錯程式碼
masm是用來編譯程式碼
在處理某一方面不太一樣
assume cs:code code segment mov ax,2000 mov al,[0] code ends end
我們首先看一下從test.asm到test.obj再到test.exe經過masm編譯器執行後的程式碼
再對比一下我在debug上寫的
可以發現debug會處理成[0]
而masm會處理成0
[0]
的含義是偏移地址
而0
的含義是一個記憶體單元
就效率而言[0]
更高,我們可以將程式碼mov al,[0]
改成mov al,ds:[0]
這樣可以達到與debug一樣的處理方式,效率也更高
三、一個完整的程式
在彙編程式中你要詳細地設定好各個段以及作用,一個段最多隻能容納64KB的記憶體
我們利用assume設定了程式碼段,同理我們也可以設定棧段和資料段
設定段
assume cs:code,ss:stack,ds:data
stack segment
...
stack ends
data segment
...
data ends
code segment
...
code ends
end
這裡我定義code與cs連結起來,ss與stack連結起來,ds與data連結起來
這些段暫存器的作用不用多說了
其記憶體空間是由系統自動分配好的
我們無需再關心它到底是在哪一段,只需要知道其偏移地址即可,我們可以用BX暫存器索引資料段,sp索引棧段,ip索引程式碼段。
不過一般情況下我們只會去索引資料段
設定程式從哪開始
當設定的段多起來後我們需要一個標號來確定程式從哪開始,結束的是哪一段
assume cs:code,ss:stack,ds:data
stack segment
...
stack ends
data segment
...
data ends
code segment
start: ...
...
code ends
end start
這裡我設定標號start,同時在下面的end偽指令表明我結束的是從start開始的那一段程式碼
dw指令——定義字型資料
dw指令類似於C語言裡面的定義變數,不過有些不一樣
在彙編裡是定義一個整數或者更準確點你是定義一個4位十六進位制資料
dw是定義一個字型資料(也就是定義一個佔2位元組的資料)
dw 1234
/*
定義字型資料1234H
類似於C語言的int a = 0x1234;
*/
dw 12
/*定義字型資料1200H*/
習慣上會在棧段和程式碼段那一塊專門定義資料
四、現在能做點什麼?
雖然現在學得有限,但還是能從記憶體角度實現一些功能
①記憶體資料COPY
下面程式碼能將data段的資料copy至stack段
assume cs:code,ds:data,ss:sg
data segment
dw 3031h,3233h,3435h,3637h,3839h
/*注意加上h,否則會預設為十進位制的3031*/
data ends
sg segment stack
dt 1 dup (0)
/*這塊的語法再下一節會有講
可以忽略這行程式碼
*/
sg ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,10
s:
push ds:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
同理依據記憶體操作,你還能完成其他的操作,比如清空記憶體、資料交換等
LAST、關於暫存器
我們發現通用暫存器並不普通,似乎每個暫存器都具有特殊功能一樣,但總體上就分為段暫存器、偏移地址暫存器、通用暫存器和標誌暫存器(後續會講)。
這三類具有一個最基本的功能,通用暫存器就是用來儲存資料,段暫存器就是用來指向段,偏移地址暫存器就是指向段的偏移地址的記憶體
其餘的功能如果你不使用你就可以只用它的基本功能
如果你在某一個塊不用迴圈結構,那麼CX就可以用來儲存資料而不是專門去儲存迴圈次數
如果在某一個程式不需要使用棧段,你就可以把SS指向的記憶體當作一個普通的記憶體,可以當成一個程式碼段也可以當成一個數據段