1. 程式人生 > >王爽《組合語言》第三版 第十章 實驗十

王爽《組合語言》第三版 第十章 實驗十

1、顯示字串

程式碼如下:

assume cs:code

    data segment
        db 'Welcome to masm!', 0
    data ends

    code segment
    start:      mov dh, 8
                mov dl, 3
                mov cl, 2
                mov ax, data
                mov ds, ax
                mov si, 0
                call show_str
                mov ax, 4
c00h int 21h show_str: push ax push bx push cx push dx push es push di push si ;根據上節中的框架,為了不讓子程式干擾主程式中暫存器的值,將所有子程式會用到的暫存器進行壓棧 mov ax, 0b800h mov es, ax ;顏色區的段地址 mov al, 160
mul dh ;每行佔160個位元組,乘以行數 push ax ;將行計算的結果儲存到棧中 mov al, 2 mul dl ;每列佔2個位元組,乘以列數 pop bx ;將上次運算的結果(160×行數)的值轉移到bx中 add bx, ax ;此時的ax值為(2×列數) ;將兩者相加,最終結果儲存到bx中 mov dl, cl ;因為下面的跳轉指令jcxz需要用到cx暫存器,故需要將cl的值先儲存在dl中 mov di, 0
change: mov cl, ds:[di] mov ch, 0 jcxz ok mov ch, dl mov es:[bx+si], cx add si, 2 inc di jmp short change ok: pop si pop di pop es pop dx pop cx pop bx pop ax ret code ends end start

執行結果

這裡寫圖片描述
在8行3列處開始顯示綠色字串

2、解決除法溢位的問題

程式碼如下:

assume cs:code

data segment
    dw 16 dup(0)
    ;此資料段用來臨時存放資料
data ends

code segment
start:  mov ax, 4240h
        mov dx, 000fh
        mov cx, 0ah
        mov bx, data
        mov ds, bx
        call divdw
        mov ax, ds:[0]
        mov dx, ds:[2]
        mov cx, ds:[4]

        mov ax, 4c00h
        int 21h

divdw:  push ds
        push dx
        push cx
        push ax 

        mov ax, dx
        mov dx, 0
        div cx
        ;ax存放商,dx存放餘數
        ;根據公式,使用被除數高位除以除數得到的商×65536
        ;*65536等價於在低位加16個0,因此操作就會變得非常簡單
        ;使用被除數高位除以除數得到的餘數×65536+被除數的低位,再將得到的結果除以除數
        ;兩者的結果相加,即可得到32位/16位的無溢位結果
        push dx
        ;使用棧臨時儲存餘數
        mov dx, ax
        mov ax, 0
        mov ds:[0], ax
        mov ds:[2], dx
        pop dx
        ;彈出餘數,作為右運算元中被除數的高16位 
        pop ax
        ;得到被除數的低16位
        push ax
        ;恢復棧頂資料,避免對主程式造成干擾 
        ;將右運算元[]中的左運算元的低16位和被除數的低16位相加
        ;但是右運算元[]中的左運算元的低16位一定是全0的,因此我們可以省略這一步
        ;直接執行pop ax,將被除數的低16位作為右運算元的低16位
        div cx
        ;ax存放商,dx存放餘數
        ;由於左運算元的低16位一定是全0,所以不必與其相加,直接將
        ;右運算元的低16位儲存到ds:[0]記憶體單元即可
        mov ds:[0], ax
        ;商的低16位放到ds:[0]單元中
        mov ds:[4], dx  
        ;餘數放到ds:[4]單元中
        ;ds:[2]中一直儲存的都是商的高16位,且沒有被更改過,因此無須任何操作

        pop ax
        pop cx
        pop dx  
        pop ds
        ret
code ends
end start

執行結果
這裡寫圖片描述
與書中結果一致

3、數值顯示

無註釋版:

assume cs:code

data segment
    dw 123, 12666, 1, 8, 3, 38
data ends

ascii segment
    db 100 dup(0) 
ascii ends

code segment
start:      mov bx, data
            mov ds, bx
            mov si, 0
            call dtoc

            mov cx, 6
            mov ax, 0
            mov dh, 8
show:       push cx
            mov dl, 3
            mov cl, 2
            call show_str
            pop cx
            inc dh
            loop show
            mov ax, 4c00h
            int 21h 

dtoc:       push ax
            push bx
            push cx
            push dx
            push ds
            push si
            push es

            mov ax, ascii
            mov es, ax
            mov di, 0
            mov cx, 6

loop_zone:  push cx
            mov dx, 0
            mov ax, ds:[si]

split:      push dx
            mov cx, 0ah
            mov dx, 0
            div cx
            mov cx, dx

            pop dx
            push cx
            inc dx
            mov cx, ax
            add cx, dx
            jcxz ok1
            jmp short split 

ok1:        pop ax
            add al, 30h
            mov byte ptr es:[di], al
            inc di
            dec dx
            mov cx, dx
            jcxz last
            jmp short ok1

last:       mov ah, 0
            mov byte ptr es:[di], ah 
            inc di 
            pop cx
            add si, 2
            loop loop_zone

            pop es
            pop si
            pop ds
            pop dx
            pop bx
            pop cx
            pop ax
            ret

show_str:   push bx
            push cx
            push dx
            push ds
            push es
            push di
            push ax
            push si 

            mov di, ax
            mov ax, 0b800h
            mov es, ax 
            mov ax, ascii
            mov ds, ax 
            mov al, 160 
            mul dh 
            push ax 
            mov al, 2
            mul dl 
            pop bx 
            add bx, ax 
            mov dl, cl 

change:     mov cl, ds:[di]
            mov ch, 0 
            inc di 
            jcxz ok2
            mov ch, dl 
            mov es:[bx+si], cx
            add si, 2
            jmp short change

ok2:        pop si
            pop ax
            mov ax, di
            pop di
            pop es
            pop ds
            pop dx
            pop cx
            pop bx 
            ret
code ends
end start 

**

詳解版

assume cs:code

    data segment
        dw 123, 12666, 1, 8, 3, 38
    data ends

    ascii segment
        db 100 dup(0)
        ;ascii碼值,一個位元組即可儲存
    ascii ends

    div segment
        dw 16 dup(0)
        ;除法溢位計算需要使用該資料段來臨時儲存結果
    div ends

    code segment
    start:      mov bx, data
                mov ds, bx
                ;ds段暫存器用來存放待處理的資料
                mov si, 0
                call dtoc

                mov cx, 6
                mov ax, 0
                mov dh, 8
    show:       push cx
                mov dl, 3
                mov cl, 2
                call show_str
                pop cx
                ;我們需要更改行號來避免覆蓋
                inc dh
                loop show
                mov ax, 4c00h
                int 21h 

    dtoc:       ;該子程式用於將數值型的數字轉換為字串
                ;十進位制數值轉換為ASCII碼值,轉換關係為:ascii=10進位制+30H
                ;要想將一個十進位制的整數拆分成一個一個的數值,那我們需要讓這個數
                ;除以10,然後將得到的結果依次入棧,除完之後再依次出棧,即可得到由高位到低位
                ;的所有數值,之後將這些值加上30H,即得到其對應的ASCII碼值,然後將這些
                ;ASCII碼值存放到一個數據段中,呼叫show_str函式,來在螢幕上顯示這些數值
                ;為了儲存轉換後的ASCII碼值,我們需要新開闢一個數據段
                push ax
                push bx
                push cx
                push dx
                push ds
                push si
                push es

                mov ax, ascii
                mov es, ax
                ;使用es段暫存器來儲存轉換後的ascii碼值
                mov di, 0
                ;儲存ASCII資料時用來指向ascii段中的每個記憶體單元 
                mov cx, 6
    loop_zone:  push cx
                ;因為內層迴圈會更改cx的值,所以我們需要使用棧結構來儲存cx的值
                mov dx, 0
                ;記錄十進位制資料的位數
                mov ax, ds:[si]
                ;ax存放被除數
    split:      push dx
                ;下面要用到dx暫存器,因此我們先儲存dx
                mov cx, 0ah
                ;cx存放除數 
                mov dx, 0
                ;dx作為被除數高16位,置0
                div cx
                ;32/16的除法運算,商儲存在ax中,餘數儲存在dx中
                mov cx, dx
                ;call divdw
                ;其實用不著呼叫divdw,這個除法溢位問題不是真正的除法溢位問題
                ;我們只需要將被除數湊成32位的,除數當做16位的即可
                ;此程式返回運算後的商和餘數,分別儲存在ax和cx中

                ;如果被除數大於2550,al是無法存放商的,會造成溢位,因此,我們需要呼叫本實驗中第二個函式
                ;專門用於解決除法溢位問題的函式,雖然程式2解決的是32/16的除法運算的溢位問題,但是對於16/8位的
                ;除法運算也是完全適用的

                ;由於入棧時只能使用字型資料,所以我們壓入的是ax,此時需要將
                ;無關資料,也就是al置0
                pop dx
                ;取出dx更改前的值
                push cx
                ;餘數入棧
                inc dx
                ;當迴圈終止的時候可以進行彈棧儲存操作了,但是我們需要一個標記,來標識我們需要
                ;彈出多少次,我們使用dx來進行儲存
                mov cx, ax
                add cx, dx
                ;ax中的值在下一次運算中一定會用到,dx中的值也有可能會用到(當被除數很大時)
                ;此時可以臨時儲存資料的只有cx了,因此我們直接將運算結果放到cx中
                ;一舉兩得
                jcxz ok1
                ;處理過程是需要迴圈的,迴圈結束的條件是商==0 
                ;我們只需要將執行jcxz指令即可,當cx的值位0的時候,它會自動跳轉到ok1迴圈的
                jmp short split 

    ok1:        pop ax
                add al, 30h
                mov byte ptr es:[di], al
                inc di
                dec dx
                mov cx, dx
                jcxz last
                jmp short ok1

    last:       ;最後一步,在資料的ASCII資料形式的最後加上一個0
                mov ah, 0
                mov byte ptr es:[di], ah 
                inc di
                ;從split到ok1到最後一步是對data段中第一個資料的處理
                ;這個過程需要進行迴圈操作
                ;在這個迴圈中,di,bx是放在迴圈外的
                pop cx
                add si, 2
                loop loop_zone

                pop es
                pop si
                pop ds
                pop dx
                pop bx
                pop cx
                pop ax
                ret

    divdw:      push ds
                push dx
                push cx
                push ax 

                mov ax, div
                mov ds, ax
                mov dx, 0
                ;由於本程式中被除數是16位,但是divdw是32/16,所以我們需要將被除數的高位補16個0,也就是將dx置0
                mov ax, dx
                mov dx, 0
                div cx
                ;ax存放商,dx存放餘數
                ;根據公式,使用被除數高位除以除數得到的商×65536
                ;*65536等價於在低位加16個0,因此操作就會變得非常簡單
                ;使用被除數高位除以除數得到的餘數×65536+被除數的低位,再將得到的結果除以除數
                ;兩者的結果相加,即可得到32位/16位的無溢位結果
                push dx
                ;使用棧臨時儲存餘數
                mov dx, ax
                mov ax, 0
                mov ds:[0], ax
                mov ds:[2], dx
                pop dx
                ;彈出餘數,作為右運算元中被除數的高16位
                pop bx
                ;得到被除數的低16位
                push bx
                ;恢復棧頂資料,避免對主程式造成干擾
                ;add ax, bx
                ;將右運算元[]中的左運算元的低16位和被除數的低16位相加
                ;但是右運算元[]中的左運算元的低16位一定是全0的,因此我們可以省略這一步
                ;直接執行mov ax, bx
                mov ax, bx
                div cx
                ;ax存放商,dx存放餘數
                ;由於左運算元的低16位一定是全0,所以不必與其相加,直接將
                ;右運算元的低16位儲存到ds:[0]記憶體單元即可
                mov ds:[0], ax
                ;商的低16位放到ds:[0]單元中
                mov ds:[4], dx  
                ;餘數放到ds:[4]單元中
                ;ds:[2]中一直儲存的都是商的高16位,且沒有被更改過,因此無須任何操作

                pop ax
                pop cx
                pop dx
                mov ax, ds:[0]
                ;ax儲存商的低16位
                mov dx, ds:[2]
                ;dx儲存商的高16位
                mov cx, ds:[4]
                ;cx儲存餘數  
                pop ds
                ;之所以要在pop ds之前將資料轉移,是因為子程式divdw呼叫前,ds已經被使用
                ;指向的是其他的段,如果不在pop之前轉移資料,那麼div段的資料就無法獲取了
                ret

    show_str:   push bx
                push cx
                push dx
                push ds
                push es
                push di
                push ax
                push si
                ;根據上節中的框架,為了不讓子程式干擾主程式中暫存器的值,將所有子程式會用到的暫存器進行壓棧

                mov di, ax
                mov ax, 0b800h
                mov es, ax
                ;顏色區的段地址
                mov ax, ascii
                mov ds, ax
                ;待輸出的ASCII碼值資料段
                mov al, 160 
                mul dh
                ;每行佔160個位元組,乘以行數
                push ax
                ;將行計算的結果儲存到棧中
                mov al, 2
                mul dl
                ;每列佔2個位元組,乘以列數
                pop bx
                ;將上次運算的結果(160×行數)的值轉移到bx中
                add bx, ax  ;此時的ax值為(2×列數)
                ;將兩者相加,最終結果儲存到bx中
                mov dl, cl
                ;因為下面的跳轉指令jcxz需要用到cx暫存器,故需要將cl的值先儲存在dl中

    change:     mov cl, ds:[di]
                mov ch, 0 
                inc di
                ;我們需要記錄下di的值,下一輪迴圈還會用到它
                ;這樣一來,我們就需要調整入棧和出棧暫存器的位置了
                ;我們在pop di之前pop ax,然後使用ax來儲存di的值
                jcxz ok2
                mov ch, dl 
                mov es:[bx+si], cx
                add si, 2
                jmp short change

    ok2:        pop si
                pop ax
                mov ax, di
                pop di
                pop es
                pop ds
                pop dx
                pop cx
                pop bx 
                ret
    code ends
    end start 

執行結果:
這裡寫圖片描述

相關推薦

組合語言 實驗

1、顯示字串 程式碼如下: assume cs:code data segment db 'Welcome to masm!', 0 data ends code segment start:

Java 線程 極簡同步技巧 讀書筆記

prev ear ont java else 停止 第三版 不同的 結合 一、能避免同步嗎? 取得鎖會由於下面原因導致成本非常高: 取得由競爭的鎖須要在虛擬機的層面上執行很多其它的程序代碼。 要取得有競爭鎖的線程總是必須等到鎖被釋放後。 1. 寄

<深入理解計算機系統()》第一

第一章 計算機系統漫遊 計算機系統是由硬體和系統軟體組成的,它們共同工作來執行應用程式. 1.1 資訊就是位+上下文 源程式實際上就是由一個值0和1組成的位(bit)序列,8個位被組織成一組,稱為位元組.每個位元組表示程式中某個文字字元. 大部分現代系統都是有ASCII標準表示文字字元,只由ASCII字

C語言程式設計() 實驗題 2 任務4

#include <iostream> #include<iomanip> #include<time.h> using namespace std; int main() {     int flag,n1;     char m;     double i=0,a=0,

C語言程式設計() 實驗題 2 任務6

#include <iostream> #include<iomanip> #include<time.h> using namespace std; int main() {     int flag,n1,n2;     char m;     double i=0,l

演算法導論 合併K個有序連結串列的種解法(最小堆法和分治遞迴法)

題目要求是將k個有序連結串列合併為一個連結串列,時間複雜度限定為O(nlogk)。下面給出應用最小堆方法的兩個程式,最後再貼上利用分治遞迴法的程式碼,雖然時間複雜度不及堆方法,但思路相對簡單好理解。 (1)最小堆方法1 用一個大小為K的最小堆(用優先佇列+自定義降序實現)(

演算法導論思考題

4-1 a. T(n)=Θ(n4) 先用代入法證明T(n)≤cn4: T(n)≤2⋅c(n2)4+n4=(c8+1)n4 故T(n)=O(n4) 再用代入法來證明T(n)≥cn4: T(n)≥2⋅c(n2)4+n4=(c8+1)n4 故T(n

影象處理-離散傅立葉變換-數字影象處理內容

  影象傅立葉變換方法有很多,可以通過空間光調製器輸入影象後在通過平行光照明經過傅立葉變換透鏡進行傅立葉變換,另一個方法就是利用計算機進行傅立葉變換,其中傅立葉變換有兩種演算法一種是DFT還有一種是FFT(快速傅立葉變換)。   首先我介紹一下影象的定義,影象是怎麼去得到的

深入理解計算機系統 家庭作業 答案

3.58long decode2(long x,long y,long z) { int ret; y=y-z; x=x*y; ret=y; ret<<=63; ret>>=63; return ret^x; }算術左移63再右移6

演算法導論 中文 2-25部分課後習題答案

由於最近在學習演算法相關的東西,發現課後的習題沒有答案,給我造成很大困擾,以下分享了從網上找到的答案連結: https://pan.baidu.com/s/1Vy2LjDxTOgYz5gdc0Cjrzg 密碼: nijb

【讀書筆記】演算法導論()第一

練習: 1.1-1 給出現實生活中需要排序的一個例子或者現實生活中需要計算凸殼的一個例子 排序:淘寶銷量排序,凸殼:淘寶綜合排序 注:凸殼是一個點集中最小點集,就像一碗盛著飯的碗,凸殼就是那個碗 1.1-2 除速度外,在真實環境中還可能使用

Java語言程序設計()第二課後習題答案(僅供參考)

[] main 是否 支付 都去 port span 時區 div 2.1 註意不同類型轉換 1 import java.util.Scanner; 2 3 public class Ch02 { 4 public static void main

組合語言 實驗

assume cs:codesg datasg segment db "Beginner's All-purpose Symbolic Instruction Code.",0 datasg ends stack segment db 16

組合語言》(實驗12個人方法記錄

assume cs:code code segment start: mov ax,cs mov ds,ax mov si,offset d0 mov ax,0 mov es,a

組合語言】(實驗

實驗二 實驗任務(一) 實驗前要求我們將書本P74的程式段按理論分析,將結果進行填空。之後用Debug,將該程式段寫入記憶體,逐條執行之後, 根據指令執行後的實際執行的結果填空如下。   mov ax,0021   mov  ax , ffff   &n

組合語言學習歷程——實驗16

編寫包含多個功能子程式的中斷例程 要求: 安裝一個新的int 7ch中斷例程,為顯示輸出提供如下功能: (1)、清屏; (2)、設定前景色; (3)、設定背景色; (4)、向上滾動一行; 入口引數說明如下。 (1)、用ah暫存器傳遞功能號:0表示

組合語言)》筆記(3)

第三章 暫存器(記憶體訪問) 使用0、1記憶體單元存放資料0420H, 則低地址單元0存放低位位元組20,高地址單元存放高位位元組04。 字單元:即存放一個字型資料(16位)的記憶體單元,由兩個地址連續的記憶體單元組成,高地址存放高位位元組,低地址存放低位位元組

組合語言)》筆記(10)CALL和RET指令

第十章 CALL和RET指令 call和ret也是轉移指令,它們都修改IP或同時修改CS和IP。他們經常被共同用來實現子程式設計。 ret指令用棧中資料修改IP,實現近轉移。使用方法:ret retf指令用棧中資料修改CS和IP,實現遠轉移。使用方法:retf

組合語言-實驗9 根據材料程式設計

assume cs:code,ds:data data segment ;在資料段定義字串 db 'Welcome to masm!' data ends code segment start: mov ax,data mov ds,ax mov ax,0b800h

組合語言檢測點答案和解析

檢測點3.1 (1)  在DEBUG中,用 "D 0:0 lf" 檢視記憶體,結果如下:  0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60  0000:0010 62 26 E6 D6 CC 2E