iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 10
1

loop

今天要介紹組合語言的循環指令 loop,結構如下:

p: 
   ;循環內容
loop p

loop 會搭配 cx 暫存器 一起使用,執行到 loop 時會先將 cx 遞減一,接著判斷 cx 的值,如果不為零則跳到標號地址繼續執行,如果為零則退出循環,有點像 C 語言的 do while 語句。

int cx = 10;
do {
    //循環內容
} while(cx > 0)

題目: 計算 2 x 3 並將結果存入 ax 暫存器。

組合語言有乘法指令,不過還沒有介紹就當不知道,先用加法來模擬,哈哈哈。

assume cs:code
code segment
start:
    mov ax, 2
    add ax, 2
    add ax, 2
    
    mov ax, 4c00h
    int 21h
code ends
end start

很笨的寫法,乘以幾就寫幾次 add,這種寫法會有什麼問題呢? 如果以後想要改成乘以 1000 乘以 10000呢,是不是光一個簡單的運算就要寫 10000 行程式,當然不是這樣,程式厲害的地方就在於可以將重複循環的事交給電腦來做,只要將 add 的部分改成迴圈就可以了,電腦會照著我們寫的迴圈自動完成 add 的動作。

assume cs:code
code segment
start:
    mov ax, 2
    mov cx, 999   ; 2 x 1000

p:  add ax, 2
    loop p

    mov ax, 4c00h
    int 21h
code ends
end start

結果如下,最後 ax 暫存器內的值 07D0 剛好就是十進制的 2000

https://ithelp.ithome.com.tw/upload/images/20181025/20106865pZ3nJSMmSL.jpg

補充: 可以看到 DOSBox 視窗中我有使用 -p 命令,前面介紹 Debug 工具時沒有提到現在補充說明,-p 可以用來跳過迴圈停在 loop 後的下一條指令上,不然以上面的例子使用 -t 命令我要按 1000 次才會離開迴圈...


loop + [ ]

再來看題目,將 1 - 10 數字存入記憶體 1000 - 1009 內。

前面有提到程式內可以用中括號 [] 存取和寫入記憶體,要注意記憶體的單位是 字節,而暫存器是 ,所以直接用 16 位暫存器對記憶體寫入,一次會寫入兩個字節大小的數據,不過因為數值都是存放在暫存器低位的部分,所以其實不影響結果,當然也可以直接使用低位暫存器,這樣每次就只會寫入一個字節大小的數據。

初步構思如下:

mov ax, 1
mov ds:[0], ax   ; [0]=1
inc ax
mov ds:[1], ax   ; [1]=2
...

補充: inc 指令可將暫存器的內容遞增一,類似 C 語言的 i++

是不是有點像陣列,在高階語言遇到陣列,可以利用迴圈和變數去繞陣列,組合語言也是一樣,我們可以將中括號內的偏移地址換成暫存器,並搭配 loop 指令進行遞增,這樣可以精簡不少程式碼。

assume cs:code
code segment
start:
    mov bx, 0100h
    mov ds, bx

    mov bx, 0      ; 偏移地址從0開始
    mov ax, 1      ; 數值從1開始
    mov cx, 10     ; 迴圈次數10
    
p:  mov [bx], ax   ; 將 ax 的值放入記憶體 [bx] 中
    inc bx         ; 遞增 bx
    inc ax         ; 遞增 ax
    loop p

    mov ax, 4c00h
    int 21h
code ends
end start

結果如下:

https://ithelp.ithome.com.tw/upload/images/20181025/20106865DunaDHuBiE.jpg


段前綴

看到這段程式碼:

mov ds:[0], ax
...
mov [bx], ax 

其中 ds: 就是段前綴,為什麼要加 ds 前面文章已經提過,如果中括號內放的是常數,編譯器會將其當成 mov 0, ax,如果放的是暫存器則預設會使用 ds 內容當段地址,那可以使用其他段暫存器嗎? 可以的,在存取記憶體時,可以使用段前綴明確指定要使用的段暫存器,不過令我好奇的是,編譯後的程式如何區分兩者呢。

我做了一個測試,將下面程式編譯後,用 Debug 工具查看記憶體的內容。

assume cs:code
code segment
start:
    mov ax, ds:[0]
    mov ax, ds:[bx]
    mov ax, es:[0]
    mov ax, es:[bx]
    mov ax, ss:[0]
    mov ax, ss:[bx]

    mov ax, 4c00h
    int 21h
code ends
end start

可以發現除了預設的 ds 暫存器之外,其他的暫存器編譯後都會包含段前綴,CPU 就能區分我們使用的偏移地址是屬於哪一個段暫存器。

https://ithelp.ithome.com.tw/upload/images/20181025/20106865dQwlGziF2V.jpg

結語

今天介紹了組合語言的迴圈,用法和 do while 差不多,不過 loop 需要用 cx 暫存器判斷是否離開迴圈,所以次數受限於 cx 的大小,最多只能循環 65536 次,今天就到這裡摟,感謝大家觀看。


上一篇
[Day09] 回頭再看 Hello World
下一篇
[Day11] 程式分段和字母反轉 + 斷賽感言
系列文
8086下16位元DOS組合語言學習筆記12

尚未有邦友留言

立即登入留言