1. 程式人生 > >組合語言學習第十章-CALL和RET指令

組合語言學習第十章-CALL和RET指令

     (sp)=(sp)-2      ((ss)*16+(sp))=(IP)(IP與CS壓棧) (2) (CS)=標號處所在的段地址       (IP)=標號處所在的偏移地址 call far ptr 標號相當於: push CS push IP jmp far ptr 標號      10.5轉移地址在暫存器中的call指令
指令格式:call  16位暫存器 執行步驟: (SP)=(SP)-2 ((SS)*16+(SP))=(IP) (IP)=(16位暫存器) call  16位暫存器相當於:
PUSH IP jmp 16位暫存器 10.6轉移地址在記憶體中的call指令
兩種格式: 一 call word ptr 記憶體地址
    相當於: push IP                      jmp word ptr 記憶體地址 二 call dword ptr 記憶體地址     相當於:push  cs                push  ip                jmp  dword ptr 記憶體地址 10.7  call和ret的配合使用
如下程式碼執行後,bx暫存器中的值為多少:
assume cs:code
code segment
start:mov ax,1
      mov cx,3
      call s
      mov bx,ax 
      mov ax,4c00h
      int 21h

    s:add ax,ax
      loop s
      ret
code ends
end  start
debug單步執行結果看出(bx)=8 分析步驟如下: 1.cpu將call s的機器碼讀入,IP指向了 call後的指令mov bx,ax,然後CPU執行call s指令,將當前IP(指令mov bx,ax的偏移地址)壓棧,並將IP改為標號S的偏移地址。 2.cpu從標號s開始執行,迴圈結束後,(ax)=8 3.cpu將ret的機器碼讀入,IP指向了ret指令後的記憶體單元,然後CPU執行ret指令,從棧中(即先前壓入棧的mov bx,ax的偏移地址)彈出一個值存入IP,此時CS:IP指向mov bx,ax 4.cpu 從mov bx,ax開始執行,直至結束 10.8 mul指令
mul作為乘法指令,由如下規則 1.均為8位的乘數,一個放在AL中,另外一個放在8位暫存器或記憶體中   均為16位的乘數,一個放在AX中,另外一個放在16位暫存器或記憶體中
2.結果:如果乘數為8位,結果放在AX中,如果乘數為16位,高位放在DX,低位放在AX 格式: mul reg mul 記憶體單元 比如: mul byte ptr ds:[0]  含義:(AX)=(AL)*((DS)*16+0); mul word ptr [bx+si+8]  含義:(DX)=(AX)*((DS)*16+(bx)+(si)+8)結果的高16位      (DX)=(AX)*((DS)*16+(bx)+(si)+8)結果的低16位 10.9模組化程式設計及引數和結果傳遞問題
call與ret配合使用可以使得程式達到模組化設計的問題,這在編寫功能龐大的程式時候是很用的,模組化程式的邏輯清楚,且可以重複複用模組程式碼。 子程式需要將程式功能執行完成後將結果返回給呼叫者。這裡我們需要考慮如何儲存和傳遞子程式需要的引數和返回值。 比如一個問題,設計一個子程式,提供一個引數N,計算N的三次方。 這裡存在兩個問題: (1)將引數N儲存在什麼地方 (2)計算得到的數值,儲存在什麼地方。 這裡我們可以將引數N放入暫存器BX中,將結果放到DX與AX中,因為是求三次方,我們可以用兩次mul指令完成。 程式碼如下:
cube:mov ax,bx
     mul bx
     mul bx 
     ret

用暫存器是常用的傳遞引數和結果的方法,在呼叫的時候將引數傳入引數暫存器。在呼叫返回時候,將結果存入結果暫存器。這種方式雖然常用,但是有個缺陷就是傳遞的引數和結果的個數有限,因為寄存的個數是有限的。 10.10 批量資料的傳遞
如何解決前面通過暫存器傳遞引數和結果的時候暫存器數量的限制,導致有很多引數的時候這種暫存器傳遞引數或者結果的方式不可取。 這種時候,我們需要將要傳遞的引數放到記憶體中,然後將記憶體地址的首地址放在暫存器中,傳遞給子程式。對於批量返回結果也使用此方法。 有如下例子,需要將一個字串中字元全部轉換為大寫字元。 我們可以將字串儲存於記憶體中的一段連續區域,然後將該記憶體地址存入暫存器。通過LOOP指令迴圈遍歷記憶體取出每個字元然後進行大小寫轉換。結果如下:
assume cs:code

data segment
   db 'conversation'
data ends

code segment
start: mov ax,data
       mov ds,ax
       mov si,0            ;ds:si指向字串(批量資料)所在空間的首地址
       mov cx,12           ;cx存放字串的長度
       call capital 
       mov ax.4c00h
      int 21h

captical:add byte ptr [si],11011111b
        loop captical
         ret
code ends
end  start

10.11暫存器衝突的問題
暫存器衝突是指在子程式使用的暫存器很有可能在主程式中也有使用,這會造成暫存器在使用上的衝突。 解決這個問題是在執行子程式開始時,將子程式中所有用到的暫存器的值全部儲存起來,在子程式返回前,再將其恢復。可以用棧來儲存暫存器的內容。 以後編寫子程式的框架如下:
子程式開始:子程式使用的暫存器壓棧
	          子程式內容
                  子程式使用的暫存器出棧
                  返回(ret,retf)

我們對10.10中的子程式capital修改以防止寄存衝突,修改後如下:
captical: push cx
          push si

change:   mov cl,[si]
          mov ch,0
          jcxz ok
          and byte ptr [si],11011111b
          inc si
          imp short change

ok:       pop si
          pop cx
          ret