匯編實(shí)驗(yàn)3 轉(zhuǎn)移指令跳轉(zhuǎn)原理及其簡單應(yīng)用編程
實(shí)驗(yàn)任務(wù)1
源代碼
點(diǎn)擊查看代碼
assume cs:code, ds:data
data segment
x db 1, 9, 3
len1 equ $ - x ; 符號常量 , $指下一個(gè)數(shù)據(jù)項(xiàng)的偏移地址,這個(gè)示例中,是3
y dw 1, 9, 3
len2 equ $ - y ; 符號常量 , $指下一個(gè)數(shù)據(jù)項(xiàng)的偏移地址,這個(gè)示例中,是9
data ends
code segment
start:
mov ax, data
mov ds, ax
mov si, offset x
mov cx, len1
mov ah, 2
s1:mov dl, [si]
or dl, 30h
int 21h
mov dl, ' '
int 21h
inc si
loop s1
mov ah, 2
mov dl, 0ah
int 21h
mov si, offset y
mov cx, len2/2
mov ah, 2
s2:mov dx, [si]
or dl, 30h
int 21h
mov dl, ' '
int 21h
add si, 2
loop s2
mov ah, 4ch
int 21h
code ends
end start
實(shí)驗(yàn)結(jié)果

根據(jù) Intel 白皮書,LOOP指令的機(jī)器碼格式為:E2 cb(cb指一個(gè)字節(jié)單位)
cb處是一個(gè)字節(jié)的偏移量,是一個(gè)8位有符號整數(shù),范圍在-128 ~ 127
根據(jù)課堂和課本知識可知:LOOP本質(zhì)是一個(gè)近轉(zhuǎn)移,偏移量存儲時(shí)采用補(bǔ)碼表示
問題①
十六進(jìn)制(補(bǔ)碼):F2
二進(jìn)制(補(bǔ)碼):1111 0010
二進(jìn)制(原碼):1000 1110
十進(jìn)制(原碼):-14
根據(jù)LOOP指令定義:當(dāng)前IP + 有符號偏移量 = 跳轉(zhuǎn)地址
當(dāng)前IP指向下一條指令開始地址,為001B,十進(jìn)制表示:27
根據(jù)公式:27 + (-14) = 13
13的十六進(jìn)制表示為:D,跳轉(zhuǎn)地址即000D,可以發(fā)現(xiàn)的確是代碼中跳轉(zhuǎn)的地址

問題②
十六進(jìn)制(補(bǔ)碼):F0
二進(jìn)制(補(bǔ)碼):1111 0000
二進(jìn)制(原碼):1001 0000
十進(jìn)制(原碼):-16
根據(jù)LOOP指令定義:當(dāng)前IP + 有符號偏移量 = 跳轉(zhuǎn)地址
當(dāng)前IP為0039,十進(jìn)制表示:57
根據(jù)公式:57 + (-16) = 41
41的十六進(jìn)制表示為:29,跳轉(zhuǎn)地址即0029,可以發(fā)現(xiàn)的確是代碼中跳轉(zhuǎn)的地址

相關(guān)研究
1. 關(guān)于匯編中的標(biāo)號(label)
在 Intel 白皮書中,標(biāo)號一律被稱作label
目前已經(jīng)學(xué)過的匯編中有兩種標(biāo)號方式,一種有冒號(:),一種沒有冒號
上面的代碼中:
assume cs:code, ds:data
data segment
x db 1, 9, 3
len1 equ $ - x
...
data ends
code segment
start:
...
x和len1沒有冒號,而start有冒號。根據(jù)博客《匯編語言之 有冒號的標(biāo)號和沒冒號標(biāo)號的區(qū)別》的說法,區(qū)別在于x和len既可以當(dāng)做地址,也可以查看其中的內(nèi)容,而start只能作為地址使用。
但是這篇博客寫的很含糊,不明不白。因此做了以下進(jìn)一步嘗試。
嘗試1:如果在data段中給x加上冒號寫作這樣:
data segment
x: db 1, 9, 3
len1 equ $ - x
...
會提示錯(cuò)誤:Missing or unreachable CS

目前還沒搞清楚這是為什么,盲猜是因?yàn)?code>assume中將data段作為數(shù)據(jù)段,里面的代碼不會被執(zhí)行所導(dǎo)致的。
但是可以知道,在data段中無法使用帶冒號的標(biāo)號(label)
嘗試2:以下代碼段masm編譯階段會報(bào)錯(cuò):
a mov ax, 0
mov ax, word ptr a

嘗試3:以下兩個(gè)代碼段編譯和運(yùn)行中均不會報(bào)錯(cuò):
a: mov ax, 0
mov ax, word ptr a
a db 1, 9, 3
mov ax, word ptr a
兩段代碼中:
第一段ax放入的均為a處指令開始的地址
第二段ax放入的為數(shù)字1
嘗試4:如下代碼段編譯和運(yùn)行中也不會報(bào)錯(cuò):
a db 1, 9
len1 = $ - a
mov ax, len1
b: db 1, 9
len2 = $ - b
mov ax, len2
在debug中進(jìn)行反匯編:

可以看到二者沒有什么差別
可以發(fā)現(xiàn):
- 不帶冒號的標(biāo)號后只能跟偽指令,而帶冒號后可以跟任何指令
- 帶冒號和不帶冒號都可以作為指令的地址使用
這里只做了簡單實(shí)驗(yàn)來研究加冒號和不加冒號兩種標(biāo)號形式的異同點(diǎn),但是資料過少且沒有時(shí)間,以后再做深入了解。
2. LOOP指令
在《Intel? 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z》(Intel白皮書)中,
LOOP指令的機(jī)器碼結(jié)構(gòu)(Vol.2A 3-597):
關(guān)于LOOP指令的描述:
The target instruction is specified with a relative offset (a signed offset relative to the current value of the instruction pointer in the IP/EIP/RIP register). This offset is generally specified as a label in assembly code, but at the machine code level, it is encoded as a signed, 8-bit immediate value, which is added to the instruction pointer. Offsets of –128 to +127 are allowed with this instruction.
目標(biāo)指令被指定為相對偏移量(相對于IP/EIP/RIP寄存器中指令指針的當(dāng)前值的有符號偏移)。這個(gè)偏移量在匯編代碼中通常被指定為一個(gè)標(biāo)號,但在機(jī)器碼層面,它被編碼為一個(gè)加在指令指針(IP)上的有符號8位立即數(shù)。這條指令允許的偏移量為-128到+127。
實(shí)驗(yàn)任務(wù)2
源代碼
點(diǎn)擊查看代碼
assume cs:code, ds:data
data segment
dw 200h, 0h, 230h, 0h
data ends
stack segment
db 16 dup(0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov word ptr ds:[0], offset s1 ; ds:[0] 存儲了s1的地址
mov word ptr ds:[2], offset s2 ; ds:[2] 存儲了s2的地址
mov ds:[4], cs ; ds:[4] 存儲了當(dāng)前段的段地址
mov ax, stack
mov ss, ax
mov sp, 16
call word ptr ds:[0] ; word為短轉(zhuǎn)移,把 s1 處的 IP 進(jìn)棧, 然后跳轉(zhuǎn)到 s1 的地址
s1: pop ax ; 把 s1 處的 IP 值送入 ax
call dword ptr ds:[2] ; dword為遠(yuǎn)轉(zhuǎn)移,把 s2 出的 CS:IP 值進(jìn)棧, 然后跳轉(zhuǎn)到 s2 處
s2: pop bx ; 把 s2 的 IP 值送入 bx
pop cx ; 把 s2 的 CS 值送入 cx
mov ah, 4ch
int 21h
code ends
end start
問題解答
根據(jù)分析:(上面代碼中的注釋為分析過程)
ax = s1 處的 IP
bx = s2 的 IP
cx = s2 的 CS
實(shí)驗(yàn)結(jié)果
和分析的結(jié)果是一致的。

實(shí)驗(yàn)任務(wù)3
僅實(shí)現(xiàn)任務(wù)中要求的源代碼
點(diǎn)擊查看代碼
; 僅能打印byte長度的數(shù)字(0-255),可以實(shí)現(xiàn)不定位數(shù)
assume ds:data, cs:code, ss:stack
data segment
x db 99, 72, 85, 63, 89, 97, 55
len equ $ - x
data ends
stack segment
dw 16 dup(?)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 32
mov cx, len ; 由于數(shù)據(jù)都是byte型,所以len就是數(shù)據(jù)個(gè)數(shù)
; print循環(huán): 依次打印所有數(shù)字
print:
mov ah, 0 ; 數(shù)據(jù)只有一個(gè)字節(jié),先把a(bǔ)h置0,子函數(shù)中除法是以ax作為被除數(shù)的
mov al, byte ptr ds:[di] ; 把數(shù)據(jù)放入al
inc di ; di指針后移
push cx ; 把cx保存起來, 子程序中會修改cx值
call printNumber ; 打印數(shù)字
call printSpace ; 打印空格
pop cx ; 恢復(fù)cx
loop print
mov ah, 4ch
int 21h
; 子程序: printNumber
; 功能: 打印數(shù)字
; 入口參數(shù):
; 寄存器ax (待輸出的數(shù)據(jù) --> ax)
; 局部變量說明:
; bx -> 存儲數(shù)字字符個(gè)數(shù)
printNumber:
mov bx, 0 ; 獲取之前位數(shù)為0
; 逐位獲取數(shù)字
; getEach循環(huán): 獲取每一位,然后壓入棧中
getEach:
mov dl, 10
div dl ; 數(shù)據(jù)除10
push ax ; 將數(shù)字壓入棧中(ah余數(shù)在ax里了)
inc bx ; 位數(shù)+1
mov ah, 0 ; ah是余數(shù),置0后ax表示除法的結(jié)果
mov cx, ax ; 除法結(jié)果賦給cx, 如果結(jié)果為0則說明所有位數(shù)都獲取完了
inc cx ; 由于loop時(shí)會-1,這里先+1,防止出現(xiàn)負(fù)數(shù)
loop getEach
; 打印數(shù)字
mov cx, bx ; 先把bx存的數(shù)字位數(shù)賦給cx
; printEach循環(huán): 依次從棧中取出數(shù)字,逐位打印
printEach:
pop ax ; 取出一位數(shù)
add ah, 30h ; ah是剛才除法的余數(shù),也就是需要得到的位數(shù),+30h是轉(zhuǎn)成對應(yīng)字符
mov dl, ah ; 放到dl, 用于打印
mov ah, 2 ; 調(diào)用int 21h的2號子程序打印
int 21h
loop printEach
ret
; 子程序: printSpace
; 功能: 打印空格
printSpace:
mov ah, 2
mov dl, 20h
int 21h
ret
code ends
end start
任務(wù)要求的實(shí)驗(yàn)結(jié)果
可以成功打印要求中的2位數(shù)。

實(shí)際上,該代碼還可以打印0 ~ 255之間的任意數(shù)字,效果如下:

改進(jìn)的源代碼
點(diǎn)擊查看代碼
; 對task3.asm的修改, 可以打印0~2559不定位數(shù)的數(shù)字
assume ds:data, cs:code, ss:stack
data segment
; 改進(jìn): db換成dw
x dw 999, 0, 856, 1024, 36, 97, 2559
len equ $ - x
data ends
stack segment
dw 32 dup(?)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 64
; 這里需要改
mov cx, len/2 ; 由于數(shù)據(jù)都是word型,所以len/2才是數(shù)據(jù)個(gè)數(shù)
; print循環(huán): 依次打印所有數(shù)字
print:
; 這里需要改, 數(shù)據(jù)讀進(jìn)ax而不是al
mov ax, word ptr ds:[di] ; 把數(shù)據(jù)放入al
add di, 2 ; di指針后移2字節(jié)
push cx ; 把cx保存起來, 子程序中會修改cx值
call printNumber ; 打印數(shù)字
call printSpace ; 打印空格
pop cx ; 恢復(fù)cx
loop print
mov ah, 4ch
int 21h
; 子程序: printNumber
; 功能: 打印數(shù)字
; 入口參數(shù):
; 寄存器ax (待輸出的數(shù)據(jù) --> ax)
; 局部變量說明:
; bx -> 存儲數(shù)字字符個(gè)數(shù)
printNumber:
mov bx, 0 ; 獲取之前位數(shù)為0
; 逐位獲取數(shù)字
; getEach循環(huán): 獲取每一位,然后壓入棧中
getEach:
mov dl, 10
div dl ; 數(shù)據(jù)除10
push ax ; 將數(shù)字壓入棧中(ah余數(shù)在ax里了)
inc bx ; 位數(shù)+1
mov ah, 0 ; ah是余數(shù),置0后ax表示除法的結(jié)果
mov cx, ax ; 除法結(jié)果賦給cx, 如果結(jié)果為0則說明所有位數(shù)都獲取完了
inc cx ; 由于loop時(shí)會-1,這里先+1,防止出現(xiàn)負(fù)數(shù)
loop getEach
; 打印數(shù)字
mov cx, bx ; 先把bx存的數(shù)字位數(shù)賦給cx
; printEach循環(huán): 依次從棧中取出數(shù)字,逐位打印
printEach:
pop ax ; 取出一位數(shù)
add ah, 30h ; ah是剛才除法的余數(shù),也就是需要得到的位數(shù),+30h是轉(zhuǎn)成對應(yīng)字符
mov dl, ah ; 放到dl, 用于打印
mov ah, 2 ; 調(diào)用int 21h的2號子程序打印
int 21h
loop printEach
ret
; 子程序: printSpace
; 功能: 打印空格
printSpace:
mov ah, 2
mov dl, 20h
int 21h
ret
code ends
end start
改進(jìn)后的實(shí)驗(yàn)結(jié)果
改進(jìn)后的程序可以實(shí)現(xiàn)打印 0 ~ 2559之間的任意數(shù)字。

一些說明
1.關(guān)于改進(jìn)的代碼
源代碼的數(shù)據(jù)存儲在字節(jié)單位,只能取0~255之間的數(shù)字。而改進(jìn)后數(shù)據(jù)存在字單位,理論上可以打印0 ~ 65535之間的任意數(shù)字。
但是上面的實(shí)驗(yàn)結(jié)果中說最大只能打印到2559,而不是65535,這和除法運(yùn)算指令div有關(guān)。
2. div指令的一些解釋
王爽《匯編語言(第2版)》P169關(guān)于div指令的說明是:
根據(jù)書上的說法,任意一個(gè)16位的被除數(shù)(十六進(jìn)制小于FFFF,也就是十進(jìn)制小于65535的數(shù))都可以放在ax中進(jìn)行除法運(yùn)算。
但是在實(shí)際操作中(操作是:除數(shù)放在一個(gè)8位寄存器中(如bl)),被除數(shù)放在bx中把65535也就是FFFFh放在ax中,進(jìn)行十進(jìn)制除10運(yùn)算卻發(fā)生了錯(cuò)誤。同樣的,對0FFFh進(jìn)行除10運(yùn)算也出錯(cuò)了。而00FFh是不會出錯(cuò)的。
這就奇了怪了。
不過按書上的說明,16位被除數(shù)放在ax中,除法運(yùn)算后的商保存在al中,余數(shù)保存ah中。而al和ah都是8位的,因此商和余數(shù)應(yīng)該均小于8位。
所以,其實(shí)div除法指令更確切的定義應(yīng)該是:
如果除數(shù)為8位,被除數(shù)為16位,且進(jìn)行除法運(yùn)算后的商和余數(shù)均為8位,除數(shù)才能放在一個(gè)8位寄存器中,被除數(shù)放在
AX中,且商和余數(shù)才會存在AH和AL中。否則,即使除數(shù)是8位,仍應(yīng)當(dāng)放在一個(gè)16位的寄存器中,被除數(shù)則應(yīng)當(dāng)放在
DX:AX中,如果是16位被除數(shù),則只放在AX即可,而商存在AX中,余數(shù)存在DX中。
根據(jù) Intel 白皮書(《Intel? 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z》)中的說明:
手冊的意思說,小于255的數(shù),被除數(shù)和結(jié)果都在AX中,而255 ~ 65535的數(shù)結(jié)果則當(dāng)DX:AX中。
實(shí)際測試中,如果被除數(shù)放在16位寄存器中,除數(shù)是放在8位寄存器中,且商和余數(shù)都在8位范圍內(nèi),則可以正常計(jì)算且結(jié)果保存在AH和AL中。而如果商或余數(shù)超過8位,則會出錯(cuò)。
如果8位除數(shù)放在16位寄存器中(除了DX以外的寄存器),無論被除數(shù)是多少(0000-FFFFh),結(jié)果都會商保存在AX中,而余數(shù)保存在DX中。
因此上面改進(jìn)的實(shí)驗(yàn)結(jié)果中提到的2559,根據(jù)上面的解釋,商和余數(shù)都在8位以內(nèi),由于做的是除10運(yùn)算,也就是十六進(jìn)制除A運(yùn)算,FF(商) * 0A + 09(余數(shù)) = 09FF,09FF即2559,因此上面的代碼最大可以支持到打印2559。
進(jìn)一步改進(jìn)的代碼
限于篇幅,這里只展示修改后的printNumber子程序
點(diǎn)擊查看代碼
; 對task32.asm的修改, 可以打印0~65535不定位數(shù)的數(shù)字
; 子程序: printNumber
; 功能: 打印數(shù)字
; 入口參數(shù):
; 寄存器ax (待輸出的數(shù)據(jù) --> ax)
; 局部變量說明:
; bx -> 存儲數(shù)字字符個(gè)數(shù)
printNumber:
mov bx, 0 ; 獲取之前位數(shù)為0
; 逐位獲取數(shù)字
; getEach循環(huán): 獲取每一位,然后壓入棧中
getEach:
; 改進(jìn): 除數(shù)放在16位寄存器bp中
mov bp, 10 ; 除10運(yùn)算
mov dx, 0 ; 由于除數(shù)是16位寄存器,dx也是被除數(shù)一部分,需要置零
div bp ; 數(shù)據(jù)除10
push dx ; 將數(shù)字壓入棧中(余數(shù)在dx里)
inc bx ; 位數(shù)+1
mov cx, ax ; 除法商賦給cx, 如果商為0則說明所有位數(shù)都獲取完了
inc cx ; 由于loop時(shí)會-1,這里先+1,防止出現(xiàn)負(fù)數(shù)
loop getEach
; 打印數(shù)字
mov cx, bx ; 先把bx存的數(shù)字位數(shù)賦給cx
; printEach循環(huán): 依次從棧中取出數(shù)字,逐位打印
printEach:
pop dx ; 取出一位數(shù)
add dl, 30h ; dl是剛才除法的余數(shù),也就是需要得到的位數(shù),+30h是轉(zhuǎn)成對應(yīng)字符
mov ah, 2 ; 調(diào)用int 21h的2號子程序打印
int 21h
loop printEach
ret
進(jìn)一步的結(jié)果
至此,代碼task33.asm已經(jīng)可以實(shí)現(xiàn)輸出0 ~ 65535的任意數(shù)字了

實(shí)驗(yàn)任務(wù)4
源代碼
點(diǎn)擊查看代碼
assume cs:code, ds:data, ss:stack
data segment
string db 'try'
len = $ - string
data ends
stack segment
dw 2 dup(?)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 2
mov cx, len ; cs: 字符串長度
mov ax, 0
mov si, ax ; si: 0
; 打印頂部的綠色字符
mov bl, 0Ah ; bl: 顏色(背景黑+高亮+綠色:0 000 1 010)
mov bh, 0 ; bh: 行號(第1行)
call printStr
; 打印底部紅色字符
mov bl, 0Ch ; bl: 顏色(背景黑+高亮+綠色:0 000 1 100)
mov bh, 24 ; bh: 行號(第25行)
call printStr
mov ah, 4ch
int 21h
; 子程序: printStr
; 功能: 在指定行、以指定顏色,在屏幕上顯示字符串
; 入口參數(shù):
; 字符串首字符地址 --> ds:si (其中,字符串所在段的段地址—> ds, 字符串起始地址的偏移地址—> si)
; 字符串長度 --> cx
; 字符串顏色 --> bl
; 指定行 --> bh (取值:0 ~ 24)
printStr:
mov al, bh ; 把行號放在 al
mov dl, 0A0h ; 每行160字節(jié),放在 dl 中
mul dl ; 與行號相乘獲得行起始地址, al中存的是行起始地址
mov di, ax ; di存行起始地址
mov ax, 0b800h
mov es, ax ; 顯存段地址
; 開始打印
; cx已經(jīng)存了字符串?dāng)?shù)量, 直接循環(huán)就行
push si ; 先保存si, 以便下次再用
push cx ; 保存cx, 以便下次用
; 循環(huán)依次打印字符
startToPrint:
mov al, ds:[si]
mov es:[di], al ; 把ds:[si]的字符放進(jìn)es:[di]
mov es:[di+1], bl ; 放入顏色
inc si
add di, 2
loop startToPrint
pop cx ; 恢復(fù)cx
pop si ; 恢復(fù)si
ret ; 打印完成, 返回
code ends
end start
實(shí)驗(yàn)結(jié)果
可以看到,打印了符合預(yù)期的字符

一些記錄
-
在
printStr子程序中,進(jìn)行打印前,可以先將si和cx入棧保存。由于字符串需要打印兩次重復(fù)利用,而這兩個(gè)寄存器的值在打印時(shí)需要修改(si控制讀入字符,cx控制打印字符個(gè)數(shù)的循環(huán)),因此先壓入棧中保存,打印結(jié)束后再彈出放回寄存器,下次可以繼續(xù)重復(fù)打印這一段字符串,簡化程序編寫這樣做的好處在于:
? 根據(jù)高級語言編寫函數(shù)的經(jīng)驗(yàn),除非需要,否則函數(shù)內(nèi)部最好不要修改外部變量。而
si和cx作為外部變量,在內(nèi)部需要進(jìn)行修改,因此先保存起來,修改完成后,在函數(shù)退出前再恢復(fù)回去,這樣相當(dāng)于把si和cx拷貝為局部變量使用,不會修改外部變量 -
字符屬性值(僅作為記錄,來自王爽《匯編語言(第2版)》P189)
![]()
實(shí)驗(yàn)任務(wù)5
源代碼
點(diǎn)擊查看代碼
assume cs:code, ds:data
data segment
stu_no db '201983290048'
len = $ - stu_no
data ends
code segment
start:
mov ax, data
mov ds, ax
mov di, 0
call printStuNum ; 調(diào)用打印子程序
mov ah, 4ch
int 21h
; 打印子程序:
; 參數(shù)說明:
; 學(xué)號字符串存儲在 -> ds:[di]
printStuNum:
mov ax, 0b800h
mov es, ax ; 控制顯存區(qū)域段指針
mov si, 1 ; 顯存區(qū)域列指針
; 先把屏幕前24行背景打印成藍(lán)色
mov al, 24 ; 前24行
mov dl, 80 ; 每行80個(gè)字符需要修改顏色
mul dl ; 24*80, 獲得需要填充藍(lán)色的字符數(shù)
mov cx, ax
printBlue:
mov al, 17h ; 藍(lán)底+白字:0 001 0 111 -> 17h
mov es:[si], al ; 把顏色填充到位
add si, 2 ; 后移2個(gè)
loop printBlue
sub si, 1 ; 指針回退一個(gè), 從最后一行起始位置開始
; 打印最后一行
mov ax, 80
sub ax, len ; 80列 - 學(xué)號長度
mov dl, 2
div dl ; (80 - len)/2, 就是學(xué)號左右兩側(cè)需要填充'-'的長度
mov dx, ax ; 使用dx保存'-'的長度
; 調(diào)用打印'-'的子程序, 打印學(xué)號左側(cè)的'-'
mov cx, dx
call printSeparator
; 打印學(xué)號字符串
mov cx, len
printNumber:
mov al, ds:[di] ; 低位是字符
mov ah, 17h ; 高位是顏色
mov word ptr es:[si], ax ; 按字放入
inc di
add si, 2
loop printNumber
; 再次調(diào)用打印'-'的子程序, 打印學(xué)號右側(cè)的'-'
mov cx, dx
call printSeparator
ret
; 子程序: 打印分隔符'-'
; 參數(shù): 長度 -> cx
; 位置 -> es:[si]
printSeparator:
mov al, '-'
mov ah, 17h
mov word ptr es:[si], ax
add si, 2
loop printSeparator
ret
code ends
end start
實(shí)驗(yàn)結(jié)果
代碼說明全部寫在代碼注釋中。
可以看到,成功實(shí)現(xiàn)了要求實(shí)現(xiàn)的效果。

總結(jié)與思考

- 課本上的內(nèi)容說的比較簡潔,很多細(xì)節(jié)沒有說的很清楚。這樣的好處是比較易懂,缺點(diǎn)是如果想知道更進(jìn)一步的原理就比較困難。之前偶然知道了 Intel 白皮書(《Intel? 64 and IA-32 Architectures Software Developer’s Manual》),這本手冊里可以查到 Intel 匯編指令的所有信息。常用的就是查看匯編指令的具體使用方法和機(jī)器碼等細(xì)節(jié)。
- 在研究過程中,關(guān)于loop和div兩個(gè)指令使用時(shí)產(chǎn)生了一些問題,由于國內(nèi)搜索引擎查找匯編相關(guān)資料時(shí)得到的內(nèi)容很少,幫助有限。而在查閱 Intel 白皮書后得到了很好的解決。
- 8086的實(shí)模式下控制顯存在屏幕上打印內(nèi)容相當(dāng)方便,只要知道了顯存的地址結(jié)構(gòu)就可以隨意修改屏幕上的顏色和內(nèi)容。
- 匯編中編寫子程序很像高級語言中的函數(shù),但是比函數(shù)更靈活一點(diǎn)。不過由于需要來回跳轉(zhuǎn),程序的結(jié)構(gòu)性可能不如高級語言來的好。
- 在實(shí)驗(yàn)任務(wù)1中對于標(biāo)號進(jìn)行了一些研究,但是無奈搜不到什么相關(guān)資料,只能靠猜測來解釋。
- 子程序需要合理分配寄存器,如果需要修改寄存器時(shí)最好先把寄存器的內(nèi)容壓棧,操作完后再恢復(fù),這樣可以在不同程序段中多次使用一個(gè)寄存器。






浙公網(wǎng)安備 33010602011771號