彙編語言:知識點拾遺
前言
讀王爽的書已經有一段時間了,馬上就要學習有關“中斷”的知識了。在繼續學習之前,我想把一些我感覺比較有意思的小細節,小技巧總結一下。文章不長,如果以後有新的收穫,我會把這一系列繼續寫下去。
大小寫字轉換的技巧
一般來說,要想實現大小寫字母的轉換,有兩種基本思路。
- 根據大小寫字母在ascii表中的相對位置進行轉換
- 根據字母在字母表中相對於首字母a的位置進行轉換
第一種思路:
如果字母爲大寫字母,小寫字母 = 大寫字母 + 20H
如果字母爲小寫字母,大寫字母 = 小寫字母 - 20H
第二種思路:
如果字母爲大寫字母,小寫字母 = ‘a’ + 大寫字母 - ‘A’
如果字母爲小寫字母,大寫字母 = ‘A’ + 小寫字母 - ‘a’
王爽的書中有提供了令一種方法:
假設當前字母在寄存器AL中
如果AL爲大寫字母,則and al,11011111B
如果AL爲小寫字母,則 or al,00100000B
此時AL中即爲所要轉換的結果。
爲什麼可以這樣做呢?我下面給出一張表格,供大家驗證。
大寫 | 十六進制 | 二進制 | 小寫 | 十六進制 | 二進制 |
---|---|---|---|---|---|
A | 41H | 01000001 | a | 61H | 01100001 |
B | 42H | 01000010 | b | 62H | 01100010 |
C | 43H | 01000011 | c | 63H | 01100011 |
D | 44H | 01000100 | d | 64H | 01100100 |
E | 45H | 01000101 | e | 65H | 01100101 |
F | 46H | 01000110 | f | 66H | 01100110 |
JMP、CALL、RET指令的機制
以下用s表示標號
jmp short s
——依據位移修改IP進行段內短轉移(8位位移)jmp near ptr s
——依據位移修改IP進行段內近轉移(16位位移)jmp far ptr s
——段間遠轉移,使(CS) = s的段地址,(IP) = s的偏移地址jmp 16位reg
——使(IP) = (reg)jmp word ptr 內存單元地址
——使(IP) = (內存單元地址)jmp dword ptr 內存單元地址
——使(CS) = (內存單元地址+2),(IP) = (內存單元地址)
詳細內容見原書。
我們可以用彙編語言(儘管不符合語法)去理解ret和call指令
ret
——相當於pop IP
retf
——相對於先pop IP
,然後pop CS
call s
——相當於先push IP
,然後jmp near ptr s
call far ptr s
——相當於先push CS
,再push IP
,最後jmp far ptr s
call 16位reg
——相當於push IP
,然後jmp reg(16位)
call word ptr 內存單元地址
——相當於push IP
,然後jmp word ptr 內存單元地址
call dword ptr 內存單元地址
——相當於先push CS
,再push IP
,最後jmp dword ptr 內存單元地址
由於call與ret常常配合使用,所以之前我以爲這兩條指令必須成對出現(在設計子程序的時候)。彙編語言是相當自由的語言,當然沒有這種約束,這些指令你想怎麼用,你就怎麼用,只不過它們的配合使用是一種“套路”罷了。
如果不熟悉call,ret的執行原理,在設計子程序的堆棧傳參時,就會很難理解。
我在課堂上學習這兩條指令的時候,老師完全沒說堆棧的事,之後我發現有一個實驗(輸出一個集合的所有子集)是要用遞歸的技巧的,我當時就要罵人了,老師當時堆棧傳參講的不清不楚(說難聽點,講了和沒講是一樣的),果斷放棄這個選題,選了一個更簡單的實驗,但這樣怎麼體現出我認真的學習態度呢?(不要臉)
後來學習了王爽的書,弄懂了這些原理,堆棧傳參就很好理解了。
最後補充一點:
ret n
——相當於pop IP
,然後add sp,n
C語言中局部變量也在堆棧中存放。
標誌位DF與串處理指令
這部分老師在課堂上是不講的(不知道其他學校是怎麼樣的)。其實這部分很簡單。
方向標誌位DF
- DF = 1,每次操作後
inc si, inc di
- DF = 2,每次操作後
dec si, dec di
cld
指令使DF=0,std
指令DF =1
下面用匯編指令(不符合語法)來理解下面兩條指令
movsb
功能:
mov es:[di], byte ptr ds:[si]
;如果DF = 0
inc si
inc di
;如果DF = 1
dec si
dec di
movsw
功能:
mov es:[di], word ptr ds:[si]
;如果DF = 0
add si,2
add di,2
;如果DF = 1
sub si,2
sub di,2
rep指令
rep指令常常和以上兩條指令配合使用。舉個例子:
rep movsb
相當於
s: movsb
loop s ;根據CX決定循環次數
小結
串傳送指令的注意點
- 傳送的初始位置:ds : si
- 傳送的目的位置:es : di
- 傳送的長度: CX
- 傳遞的方向: 標誌爲DF
其他注意事項
由於篇幅原因(其實就是懶得再寫下去了),還有一些注意點就不詳細討論了。
比如:
- 除法指令的溢出問題
- 80X25彩色字符模式的顯示緩衝區
總結
彙編語言是一門相當自由的語言,只要你有耐心,你可以用它完成好多事情。
這種感覺是在學習王爽的教材中體會得到。在此之前,我只是通過學校課堂大概瞭解了一下彙編語言,我甚至不知道數據段,代碼段,堆棧段是可以通過自己修改段寄存器來“定義”的(就是蠢,沒別的解釋)。但這種自由也帶來一種壞處,就是代碼的可讀性較差,核心代碼往往被其他操作掩蓋(比如中間值傳遞,傳參,保護寄存器),因此看似很長的代碼,實際實現的功能很簡單(這讓我怎麼顯擺),甚至昨天剛寫的代碼,第二天居然讀不懂了(好吧,我只是因爲懶,沒寫註釋,但不管怎麼說,彙編就是難讀!)。
但不管怎麼說彙編還是很有用的。下面就用王爽書中的實驗11——編寫子程序,結束這篇博文。(我怎麼還沒寫註釋!反正沒有老師看,偷個懶啦)
assume cs:codesg
datasg segment
db 'Beginner`s All-purpose Symbolic Instruction Code.',0
datasg ends
codesg segment
main: mov ax,datasg
mov ds,ax
mov si,0
call letterc
mov ax,4c00h
int 21h
;-----------------------------------------------------------
;proc_name: letterc
;function: translate uppercase into lowercase in a string which ends with digit 0
;interface: ds:si points to the first address of the string
letterc: push ax
push si
letterc_s: mov al,[si]
cmp al,'a'
jb letterc_next
cmp al,'z'
ja letterc_next
and al,11011111b
mov [si],al
letterc_next: inc si
cmp al,0
je letterc_out
jmp short letterc_s
letterc_out: pop si
pop ax
ret
;----------------------------------------------------------
codesg ends
end main