80806彙編(5)——[BX]和Loop指令

80806彙編(5)——[BX]和Loop指令

已經很久沒寫點東西了,國慶節就一直想弄個我的網站,這段時間一直在弄那個,雖然有現成的框架(Hexo),可是總想弄出本身的效果來,可是最後仍是有些差強人意,只好打翻了有重來(強迫症表示難受),也懶得弄那麼多了。好在最近差很少事情也理順了,今天強迫本身靜下來寫了點東西。框架


簡述

今天看了下彙編中的[BX]和Loop指令,Loop指令容易知道,一看就是用來作循環的指令,那麼[BX]又是什麼呢?oop

首先咱們知道,要完整的描述一個內存單元,須要兩種信息:網站

  • 內存單元的地址
  • 內存單元的長度(類型:字節或字等)

對於第一個——【內存單元的地址】,咱們知道,用[addr]能夠表示一個內存單元,其中addr表示這個內存單元的偏移地址,段地址默認在DS寄存器中。而單元的長度(類型)則能夠由指令中的其餘操做對象指出(如寄存器),就像下面這樣:code

mov ax, [0]

那麼,在這裏[BX]也表示一個內存單元,其偏移地址存於BX寄存器中,段地址默認在DS寄存器中。也把BX寄存器叫作基址寄存器對象

兩個約定

爲了方便後面的敘述,這裏做兩個約定,固然也是跟着書上的來啦,哈哈,不過我發現不少書上也都默認有這個敘述方法。內存

1. 描述性符號"( )"

用描述性符號「( )」表示一個寄存器或一個內存單元中的內容,如:it

(ax) 表示寄存器AX中的內容,(al) 表示寄存器AL中的內容。
(addr) 表示物理地址爲addr的內存單元中的內容。

所以"( )"中的元素類型有3種:asm

  1. 寄存器名;
  2. 段寄存器名;
  3. 內存單元的物理地址。

其所表示的數據類型有:class

  1. 字節;
  2. 字。

具體是那種類型則由寄存器名或具體的運算符決定。

2. 常量符號"idata"

用idata表示一個常量。
如用mov ax, [idata]表明mov ax, [1]mov ax, [2]等。
如用mov bx, idata表明mov bx, 1mov bx, 2等。

[BX]

咱們對[BX]的使用有以下用法:

mov ax, [bx]  ; bx中存放的數據做爲偏移地址,段地址默認在ds中,(ax) = ((ds) * 16 + (bx))
mov [bx], ax  ; bx中存放的數據做爲偏移地址,段地址默認在ds中,((ds) * 16 + (bx)) = (ax)

Loop指令

Loop指令用於實現循環功能,循環次數存於CX寄存器中 。
【格式】loop 標號
示例:

mov cx, 5      ; 設置循環次數
s:                  ; 標號
add ax, ax     ; 循環執行的程序段
loop s           ; Loop指令

Loop指令一般用於執行某些須要重複運行的指令,好比操做某一塊連續的內存地址塊。一般把循環次數存於CX寄存器中,CPU經過判斷CX裏面的值是否爲零來決定是否執行循環。CPU執行Loop指令的描述以下:

1. (CX) = (CX) -1
2. (CX)不爲零,轉去標號處執行,反之不執行循環,向下執行指令

這裏的標號"s"並非固定的,你也能夠指定其餘的標號。在這裏標號其實是標識了一個地址,在該地址處存在着咱們的程序指令(如上面的add ax, ax),在執行循環的時候,若CX的值不爲零,則"Loop 標號"指令將IP的值設置爲標號所標識的地址,CPU就將執行IP所指向的指令。

咱們能夠獲得這樣一個Loop指令的簡單框架:

mov cx, 循環次數
label:
須要循環執行的程序段
loop label

【注意】書上的一個實例程序中提到在彙編程序中的數據表示問題,咱們知道,在彙編中,數據多以16進製表示,而16進制中有 'A~F' 6個字母,可是在彙編源程序中,數據的書寫不能以字母開頭,如「9876H」能夠直接書寫爲「9876H」,可是「A000H」不能書寫成「A000H」,而需寫爲「0A000H」(以0開頭)。

[BX]和Loop指令

在實際編程中可能會遇到要處理某一段地址連續的內存單元中的數據,一般可用循環來解決這類問題。在這樣的循環過程當中,須要一個變量來保存內存單元的偏移地址,這時候「基址寄存器——BX」就起做用了。

【注:如下內容來自王爽的《彙編語言》(第三版)】
考慮這麼一個問題,咱們要計算 ffff:0~ffff:b 單元中的數據的和,要求將結果存儲在dx寄存器中。
咱們首先分析一下:

  1. 運算後的結果是否會超出dx能存儲的範圍?
    ffff:0~ffff:b 內存單元中的數據是字節型的數據,範圍在0~255之間,12個這樣的數據相加的結果不會大於65535,是能夠存儲在dx寄存器中的。
  2. 咱們可否將 ffff:0~ffff:b 中的數據直接累加到dx中?
    這是比不可能的,爲何?由於數據類型不匹配,dx是16位寄存器,而 ffff:0~ffff:b 中的數據是8位的。
  3. 那麼是否能夠將其累加到dl中,並設置(dh)=0,從而實現累加到dx中?
    看第一條分析,12個8字節的數據相加是有可能超過255的,而咱們的dl是8位寄存器,那麼答案很明顯了。不能!
  4. 那到底應該如何作呢?
    這裏有兩個問題,類型的匹配和結果的不超界,咱們能夠考慮用一個16位的寄存器來作中介。將內存單元中的8位數據賦值到一個16位寄存器ax中,再將ax中的數據加到dx上,從而使兩個運算對象類型匹配而且結果不會超界。

考慮完以上問題,很明顯,若是是在高級語言中,這是一個很容易作的問題,一個循環就能夠實現,那麼在彙編中,咱們也能夠用循環來實現這個操做。咱們能夠把BX用做存儲內存單元偏移地址的變量,在每次循環的時候改變BX中的值,從而實現把 ffff:0~ffff:b 12個內存單元中的數據相加。
實現以下:

assume cs:code
code segment
    start:             ;程序入口
    mov ax, 0ffffh
    mov ds, ax
    mov bx, 0       ;初始化ds:bx指向ffff:0

    mov dx, 0      ;初始化累加寄存器dx,(dx)=0

    mov cx, 12
    s:
    mov al, [bx]
    mov ah, 0
    add dx, ax
    inc bx          ;使ds:bx指向下一個內存單元
    loop s

    mov ax, 4c00h
    int 21h
code ends
end start        ;程序結束

段前綴

在指令「mov ax, [bx]」中,內存單元的偏移地址由bx指出,而段地址則默認存儲在ds中。可是咱們能夠在訪問內存單元的指令中顯示給出內存單元的段地址所在的段寄存器,如:

mov ax, ds:[bx]
mov ax, cs:[bx]
mov ax, ss:[bx]
mov ax, es:[bx]
mov ax, ss:[0]
mov ax, cs:[0]

這些出如今訪問內存單元的指令中,用於顯示地指明內存單元的段地址的「ds:」 「cs:」 「es:」 「ss:」,在彙編語言中稱爲段前綴

段前綴的使用

考慮一個問題,將內存單元 ffff:0~ffff:b 單元中的數據複製到 0:200~0:20b 單元中。
分析:

  1. 0:200 ~0:20b 單元等同於 0020:0~0020:b 單元,它們描述的是同一段內存空間。
  2. 複製過程用循環實現,咱們可使用一個變量[BX]同時操做兩段內存單元(使用段前綴來標識不一樣的段)。
  3. 將 0:200~0:20b 用 0020:0~0020:b 描述,是爲了使目標單元的偏移地址和源始單元的偏移地址從同一數值0開始。

實現以下:

assume cs:code
code segment
    mov ax, 0ffffh
    mov ds, ax           ;(ds)=0ffffh

    mov ax, 0020h
    mov es, ax         ;(es)=0020h

    mov bx, 0   ;此時ds:bx指向ffff:0,es:dx指向0020:0

    mov cx, 12
    s:
    mov dl, [bx]      ;段地址默認在ds中
    mov es:[bx], dl   ;段前綴指明內存段
    inc bx
    loop s

    mov ax, 4c00h
    int 21h
code ends
end
相關文章
相關標籤/搜索