1. 程式人生 > 其它 >x86 16位真實模式 04——完善程式

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指向的記憶體當作一個普通的記憶體,可以當成一個程式碼段也可以當成一個數據段


參考書籍

《組合語言 第四版》——王爽

參考網站

https://fishc.com.cn/forum-39-1.html

https://b23.tv/qUfphX