一、順序程序結構
- 程序的靜態分析 略
- 程序的動態分析 略
- 實例:讀取CMOS RAM數據程序 略
二、分支程序結構
- 無條件轉移指令
- 代碼段寄存器CS指出代碼段的段基地址,指令指針IP給出將要執行的偏移地址
- 轉移范圍
- 段內轉移——只改變IP
- 段內轉移也稱近轉移(NEAR)
- 如果轉移范圍可以用1字節編碼表達,即向地址增大方向轉移127字節,向地址減小方向轉移128字節,形成短轉移(SHORT)
- 段間轉移——更改CS和IP
- 也稱遠轉移(FAR)
- 段內轉移——只改變IP
- 指令尋址方式
- 相對尋址方式
- 提供目標地址相對於當前指令指針IP的位移量
- 目標地址(轉移后的IP)=當前IP+位移量
- 直接尋址方式
- 直接提供目標地址
- 目標地址(轉移后的CS和IP)=指令操作數
- 間接尋址方式
- 指示寄存器或存儲單元
- 目標地址來自寄存器或存儲單元,間接獲得
- 注:目標地址=目的地址=轉移地址
- 相對尋址方式
- JMP指令
- 四種類型
- 段內轉移,相對尋址
JMP LABLE ;IP=IP+位移量
- 段內轉移,間接尋址
JMP R16 ;IP=R16 寄存器間接尋址 JMP M16 ;IP=M16 存儲器間接尋址
- 段間轉移,直接尋址
JMP LABLE ;IP=LABLE的偏移地址,CS=LABLE的段選擇器
- 段間轉移,間接尋址
JMP M32 ;IP=M32,CS=M32+2
- 段內轉移,相對尋址
- MASM會根據存儲模型和目標地址等信息自動識別是哪種轉移。
- 匯編程序提供了操作符:
- SHORT
- NEAR PTR
- FAR PTR
- 可以強制轉換一個符號、段名或子程序名的類型,形成相應的控制轉移。
- 匯編程序提供了操作符:
- 實例:
;eg403.asm .model small .stack .data nvar dw ? fvar dd ? .code .startup labl0: jmp labl1 ;段內(短)轉移、相對尋址 nop labl1: jmp near ptr labl2 ;段內(近)轉移、相對尋址 nop labl2: mov ax,offset labl3 jmp ax ;段內轉移、寄存器間接尋址 nop labl3: mov ax,offset labl4 mov nvar,ax jmp nvar ;段內轉移、存儲器間接尋址 nop labl4: jmp far ptr labl5 ;段間轉移、直接尋址 nop labl5: mov ax,offset labl6 mov word ptr fvar,ax mov dx,seg labl6 mov word ptr fvar+2,dx jmp fvar ;段間轉移、間接尋址 nop labl6: .exit end
- 四種類型
- 條件轉移指令
- 格式及說明
-
JCC LABEL ;條件滿足,發生轉移; ;否則順序執行下條指令
- LABEL表示目標地址,采用段內相對尋址(只能是-128~127之間的短轉移)
- 條件轉移指令不影響標志,但要利用標志。
- 16種,兩類標志判斷如下:
- 單個標志狀態作為條件
- 兩數大小關系作為條件
-
- 實例!開始瘋狂上代碼!
- 個數折半(如果是奇數就加一,如果是偶數直接顯示)
-
mov ax,885 ;假設一個數據 shr ax,1 ;數據右移進行折半 jnc goeven ;余數為0,即CF=0條件成立,不需要處理,轉移 add ax,1 ;否則余數為1,即CF=1,進行加1操作 goeven: call dispuiw ;顯示結果
- 上述代碼可以利用ADC優化成:
mov ax,887 ;假設一個數據 shr ax,1 ;數據右移進行折半 adc ax,0 ;余數=CF=1,進行加1操作;余數=CF=0,沒有加1 call dispuiw ;顯示結果
-
-
位測試程序(如果AL的D1=1 顯示“READY TO" GO! D1=0顯示”NOT READY")
-
.data no_msg db 'Not Ready!','$' yes_msg db 'Ready to Go!','$' .code .startup mov al,56h ;假設一個數據 test al,02h ;測試D1位(使用D1=1,其他位為0的數據) jz nom ;D1=0條件成立,轉移 mov dx,offset yes_msg ;D1=1,顯示准備好 jmp done ;跳轉過另一個分支體! nom: mov dx,offset no_msg ;顯示沒有准備好 done: mov ah,9 int 21h
-
- 奇偶校驗程序(在字符的ASC碼中的1的個數為奇數令校驗位為0)程序實現在ASC碼的最高位加上校驗位
-
Tdata db ? ;保存待發送數據的變量 mov ah,1 ;1號功能 int 21h ;鍵盤輸入 and al,7fh ;最高位置“0”、其他位不變,同時標志PF反映“1”的個數 jnp next ;個數為奇數,則轉向NEXT or al,80h ;最高位置“1”、其他位不變 next: mov Tdata,al ;保存待發送的數據
-
-
數據大小比較程序(相等顯示“EQUAL”,第一個數大顯示“FIRST”,第二個數大顯示“SECOND”)
-
;eg407.asm .model small .stack .data var1 dw -3765 var2 dw 8930 msg0 db 'Equal$' msg1 db 'First$' msg2 db 'Second$' .code .startup mov ax,var1 ;取第1個數據 cmp ax,var2 ;與第2個數據比較 je equal ;兩數相等,轉移 jnl first ;第1個數據大,轉移 mov dx,offset msg2 ;第2個數據大 jmp done first: mov dx,offset msg1 jmp done equal: mov dx,offset msg0 done: mov ah,9 ;顯示結果 int 21h .exit end
-
- 個數折半(如果是奇數就加一,如果是偶數直接顯示)
- 格式及說明
- 單分支
-
- 求絕對值
-
;eg408.asm .model small .stack .data var dw 0b422h ;有符號數據 result dw ? ;保存絕對值 .code .startup mov ax,var cmp ax,0 ;比較AX與0 jge nonneg ;條件滿足:AX≥0,轉移 neg ax ;條件不滿足:AX<0,為負數,需求補得正值 nonneg: mov result,ax ;分支結束,保存結果 .exit end
-
-
字母判斷程序
-
mov ah,1 int 21h ;輸入一個字符,從AL返回值 cmp al,'A' ;與大寫字母A比較 jb done ;比大寫字母A小,不是大寫字母,轉移 cmp al,'Z' ;與大寫字母Z比較 ja done ;比大寫字母Z大,不是大寫字母,轉移 or al,20h ;轉換為小寫 mov dl,al mov ah,2 int 21h ;顯示小寫字母 done:
-
- 求絕對值
-
- 雙分支程序
-
- 顯示數據最高位
-
var dw 0b422h ;有符號數據 mov bx,var shl bx,1 ;BX最高位移入CF標志 jc one ;CF=1,即最高位為1,轉移 mov dl,'0' ;CF=0,即最高位為0:DL←'0' jmp two ;一定要跳過另一個分支體 one: mov dl,'1' ;DL←'1' two: mov ah,2 int 21h ;顯示
可以使用ADC消除分支:
-
XOR DL,DL mov bx,var shl bx,1 ;BX最高位移入CF標志 adc dl,'0' mov ah,2 int 21h ;顯示
-
- 有符號數運算溢出程序
-
;eg411.asm .model small .stack .data var1 dw 24680 var2 dw -9999 var3 dw ? okmsg db 'Correct!','$' ;正確信息 errmsg db 'ERROR ! Overflow!','$' ;錯誤信息 .code .startup mov ax,var1 sub ax,var2 ;求差 jo error ;有溢出,轉移 mov var3,ax ;無溢出,保存差值 mov dx,offset okmsg ;顯示正確 jmp disp error: mov dx,offset errmsg ;顯示錯誤 disp: mov ah,9 int 21h .exit end
-
- 顯示數據最高位
-
- 多分支程序
- 可以使用表結構實現多分支
三、循環程序結構
- 循環指令
- LOOP LABEL
- CX=CX-1 如果CX不等於0,循環到label,否則順序執行
- JCXZ LABEL
- CX=0轉移,否則順序執行
- 目標地址采用相對短轉移
- 使用CX作為計數器
- 如果CX=0將循環pow(2,16)次發生錯誤
- 此時可以使用JCXZ先判斷CX是否為零
- 實例:數組求和程序
-
ARRAY DW 136,-138,133,130,-161 SUM DW ? XOR AX,AX MOV CX,LENGTHOF ARRAY MOV BX,OFFSET ARRAY AGAIN: ADD AX,ARRAY[BX] ADD BX,TYPE ARRAY LOOP AGAIN MOV SUM,AX
-
- LOOP LABEL
- 計數控制循環
- 求最大值程序
-
;eg414.asm .model small .stack .data array dw -3,0,20,900,587,-632,777,234,-34,-56 ;假設一個數組 count = lengthof array ;數組的元素個數 max dw ? ;存放最大值 .code .startup mov cx,count-1 ;元素個數減1是循環次數 mov si,offset array mov ax,[si] ;取出第一個元素給AX,用於暫存最大值 again: add si,2 cmp ax,[si] ;與下一個數據比較 jge next ;已經是較大值,繼續下一個循環比較 mov ax,[si] ;AX取得更大的數據 next: loop again ;計數循環 mov max,ax ;保存最大值 .exit end
-
- 簡單的加密解密程序
-
-
;eg415.asm .model small .stack .data key db 234 ;假設的一個密鑰 buffer db 'This is a secret.','$' ;待加密的信息(字符串) count = sizeof buffer-1 ;不處理最后結尾字符 msg1 db 'Encrypted message: ','$' msg2 db 13,10,'Original messge: ','$' .code .startup mov cx,count ;CX=字符個數,作為循環的次數 xor bx,bx ;BX指向待處理的字符 mov al,key ;AL=密鑰 encrypt: xor buffer[bx],al ;異或加密 inc bx ;指向下一個字符 cmp bx,cx jb encrypt ;沒有指向最后字符,繼續處理 mov dx,offset msg1 ;顯示提示信息 mov ah,9 int 21h mov dx,offset buffer ;顯示加密后的密文 mov ah,9 int 21h ; xor bx,bx ;BX指向待處理的字符 mov al,key ;AL=密鑰 decrypt: xor buffer[bx],al ;異或解密 inc bx dec cx jnz decrypt ;等同於指令:loop decrypt mov dx,offset msg2 mov ah,9 int 21h mov dx,offset buffer ;顯示解密后的明文 mov ah,9 int 21h .exit end
-
-
- 求最大值程序
- 條件控制循環
- 字符個數統計(統計以0結尾的字符串中的字符個數)
-
;eg416a.asm include io.inc .model small .stack .data string db 'Do you have fun with Assembly?',0 ;以0結尾的字符串 .code .startup xor bx,bx ;BX用於記錄字符個數,同時也用於指向字符的指針 again: mov al,string[bx] cmp al,0 ;也可以使用指令“test al,al” jz done inc bx ;個數加1 jmp again ;繼續循環 done: mov ax,bx ;顯示個數 call dispuiw .exit end
-
- 斐波那契
-
;eg417a.asm include io.inc .model small .stack .data .code .startup mov ax,1 ;AX=F(1)=1 call dispuiw ;顯示第1個數 call dispcrlf ;回車換行 call dispuiw ;顯示第2個數 call dispcrlf ;回車換行 mov bx,ax ;BX=F(2)=1 again: add ax,bx ;AX=F(N)=F(N-2)+F(N-1) jc done call dispuiw ;顯示一個數 call dispcrlf ;回車換行 xchg ax,bx ;AX=F(N-2),BX=F(N-1) jmp again done: .exit end
-
- 字符個數統計(統計以0結尾的字符串中的字符個數)
- 多重循環
- 冒泡排序
-
;eg418.asm .model small .stack .data array dw 587,-632,777,234,-34 ;假設一個數組 count = lengthof array ;數組的元素個數 .code .startup mov cx,count ;CX←數組元素個數 dec cx ;元素個數減1為外循環次數 outlp: mov dx,cx ;DX←內循環次數 mov bx,offset array inlp: mov ax,[bx] ;取前一個元素 cmp ax,[bx+1] ;與后一個元素比較 jng next ;前一個不大於后一個元素,則不進行交換 xchg ax,[bx+1] ;否則,進行交換 mov [bx],ax next: inc bx ;下一對元素 dec dx jnz inlp ;內循環尾 loop outlp ;外循環尾 .exit end
-
- 字符剔除(剔除空格
-
;eg419.asm .model small .stack .data string db 'Let us have a try !',0dh,0ah,'$' ;以“$”結尾的字符串 .code .startup mov dx,offset string ;顯示處理前的字符串 mov ah,9 int 21h mov si,offset string outlp: cmp byte ptr [si],'$' ;外循環,先判斷后循環 jz done ;為“$”結束 again: cmp byte ptr [si],' ' ;檢測是否是空格 jnz next ;不是空格繼續循環 mov di,si ;是空格,進入剔除空格分支 inlp: inc di ;該分支是循環程序 mov al,[di] ;前移一個位置 mov [di-1],al cmp byte ptr [di],'$' ;內循環,先循環后判斷 jnz inlp ;內循環結束處 jmp again ;再次判斷是否為空格(處理連續空格情況) next: inc si ;繼續對后續字符進行判斷處理 jmp outlp ;外循環結束處 done: mov dx,offset string ;顯示處理后的字符串 mov ah,9 int 21h .exit end
-
- 冒泡排序
- 串操作指令
- 以字節、字和雙字為單位多個數據存放在連續的主存區形成數據串,(也就是數組)
- 特殊的尋址方式:
- 源操作數用寄存器SI間接尋址,默認在數據段DS中,即DS:[SI],允許段超越
- 目的操作數用寄存器DI間接尋址,默認在附加段ES中,即ES:[DI],不允許段超越
- 每執行一次串操作源指針SI和目的指針DI自動±1或±2
- CLD設置方向標志DF=0 指針增加
- STD設置方向標志DF=1 指針減小
- 串傳送
- 傳送 MOVS
- MOVSB ES:[DI] ← DS:[SI] SI++,DI++
- MOVSW ES:[DI] ← DS:[SI] SI+=2,DI+=2
- 存儲 STOS
- STOSB ES:[DI]←AL DI++
- STOSW ES:[DI]←AX DI+=
- 讀取 LODS
- AL←DS:[SI] SI++
- AX←DS:[SI] SI+=2
- 重復REP 計數器也是CX
- 傳送 MOVS
四、習題
4.1(2)數據的直接尋址和指令的直接尋址有什么區別?
數據的直接尋址直接給偏移地址(變量名/有效地址)
指令的直接尋址使用段內轉移相對量(標號)
(5)什么是奇偶校驗?
包括校驗位在內的數據為為“1”的個數為奇數是奇校驗。
(6)助記符JZ和JE為什么表達同一條指令?
JZ為兩個數相減差值為零,JE為兩個數相等。
(9)如果循環體的代碼量遠超過128字節,還能用LOOP指令實現計數控制循環嗎?
短轉移前后不能超過128字節。
(10)什么是“先循環、后判斷”循環結構?
先執行一遍循環體。
4.2 判斷題
(2)指令的相對尋址都是段內轉移。
對。
(4)JMP指令對應高級語言的GOTO語句,所以不能使用。
錯,可以使用。
(6)JA和JG指令的條件都是“大於”,所以是同一個指令的兩個助記符。
錯。JA判斷無符號數JG判斷有符號數。
(7)JC和JB指令的判斷條件都是CF=1,所以是同一條指令。
錯。JC前有可能是移位,JB必須是有減法操作才能判斷。
(10)若CX=0,則LOOP指令和JCXZ指令都發生轉移。
對,而且LOOP會執行pow(2,16)次。
4.3填空題
(2)MASM給短轉移、近轉移、和遠轉移定義的類型名依次是 SHORT 、 NEAR 、 FAR 。
(3)假設BX=1256H,字變量TABLE的偏移地址是20A1H,數據段偏移地址32F7H處存放3280H,執行指令“JMP BX"后IP= 1256H ,執行指令”JMP TABLE[BX]“后IP= 3280H 。
(4)"CMP AX,3721H"指令之后是JZ指令,發生轉移的條件是AX= 3271H ,此時ZF= 1 。
(5)執行""SHR BX,1"指令后,JNC發生轉移,說明BX的D0= 0 。
(6)在DX等於0時轉移,可以使用指令“CMP DX, 0 ",也可以使用"TEST DX, FFFFH "構成條件,然后使用JE指令實現轉移。
4.4 已知VAR1、VAR2、VAR3和VAR4是16位有符號整數,用匯編語言程序片段實現如下C語句:
VAR4=(VAR1*6)/(VAR2-7)+VAR3
;用匯編語言實現var4=(var1*6)/(var2-7)+var3 .model small .stack .data var1 dw 10 var2 dw 9 var3 dw 15 var4 dw ? .code .startup mov ax,var1 mov bx,6 mul bx mov cx,var2 sub cx,7 div cx add ax,var3 mov var4,ax add var4,30h mov dx,var4 mov ah,9 int 21h .exit end
4.7 為了驗證例4-3程序的執行路徑,可以在每個標號前后增加顯示一個數字的功能,是的程序運行后顯示數碼123456.
;eg403.asm .model small .stack .data nvar dw ? fvar dd ? .code .startup labl0: jmp labl1 ;段內(短)轉移、相對尋址 nop labl1: mov ah,2 mov dl,'1' int 21h jmp near ptr labl2 ;段內(近)轉移、相對尋址 nop labl2: mov ah,2 mov dl,'2' int 21h mov ax,offset labl3 jmp ax ;段內轉移、寄存器間接尋址 nop labl3: mov ah,2 mov dl,'3' int 21h mov ax,offset labl4 mov nvar,ax jmp nvar ;段內轉移、存儲器間接尋址 nop labl4: mov ah,2 mov dl,'4' int 21h jmp far ptr labl5 ;段間轉移、直接尋址 nop labl5: mov ah,2 mov dl,'5' int 21h mov ax,offset labl6 mov word ptr fvar,ax mov dx,seg labl6 mov word ptr fvar+2,dx jmp fvar ;段間轉移、間接尋址 nop labl6: mov ah,2 mov dl,'6' int 21h .exit end
4.13 8086處理器的指令CWD將AX符號擴展到DX。假若沒有該指令,編程實現該指令功能。
;編程實現將ax符號擴展到dx .model small .stack .data .code .startup ;第一種方法:用符號擴展的含義 mov ax,45h ;初始化ax test ax,ax js one mov dx,0 jmp over1 one: mov dx,0ffffh over1: nop ;空操作,第一種方法結束 ;第二種方法:用移位實現 mov ax,0f045h ;初始化ax mov bx,ax ;為了不改變ax的值,將ax值賦值給bx shl bx,1 ;取ax的最高位到cf rcr dx,1 ;將cf的值移入dx最高位 mov cl,15 sar dx,cl ;dx算數右移15,dx所有位變成ax的符號位 .exit end
4.15 編程:先提示輸入數字"INPUT NUMBER:0~9",然后再下一行顯示輸入的數字,結束;如果不是鍵入了0~9數字,就提示錯誤'ERROR',繼續等待輸入數字。
;輸入數字 .model small .stack .data errormsg db 'Error!',0dh,0ah,'$' ;9個信息 msg db 'Input number(1~9): $' ;提示輸入字符串 crlf db 0dh,0ah,'$' ;回車換行字符 .code .startup again: mov dx,offset msg mov ah,9 int 21h ;提示輸入 mov ah,1 ;等待按鍵 int 21h push ax ;暫時將輸入的按鍵字符保存到堆棧 mov dx,offset crlf ;回車換行 mov ah,9 int 21h pop ax ;恢復按鍵字符 cmp al,'1' ;數字 < 1? jb error cmp al,'9' ;數字 > 9? ja error mov dl,al mov ah,2 int 21h ;顯示數字 jmp done;結束 error: mov dx,offset errormsg mov ah,9 int 21h ;提示輸入 jmp again done: .exit end