1. 程式人生 > 其它 >python numpy for迴圈矩陣乘法

python numpy for迴圈矩陣乘法

一、實驗目的

  1. 理解80×25彩色字元模式顯示原理

  2. 理解轉移指令jmp, loop, jcxz的跳轉原理,掌握使用其實現分支和迴圈的用法

  3. 理解轉移指令call, ret, retf的跳轉原理,掌握組合使用call和ret/retf編寫彙編子程式的方法,掌握

    引數傳遞方式

  4. 理解標誌暫存器的作用

  5. 理解條件轉移指令je, jz, ja, jb, jg, jl等的跳轉原理,掌握組合使用匯編指令cmp和條件轉移指令實

    現分支和迴圈的用法

  6. 瞭解在visual studio/Xcode等環境或利用gcc命令列引數反彙編c語言程式的方法,理解編譯器生成

    的反彙編程式碼

  7. 綜合應用定址方式和彙編指令完成應用程式設計

二、實驗準備

實驗前,請複習/學習教材以下內容:第9章 轉移指令的原理
第10章call和ret指令
第11章 標誌暫存器

三、實驗內容 略

四、實驗結論

1.實驗任務1

task1.asm彙編源程式程式碼

assume cs:code, ds:data
data segment
        db 'Welcome to masm!'
data ends
font segment
        db 8AH, 0ACH, 0FBH
font ends

code segment
start:
        mov bx, 0b86EH
        mov
es, bx mov di, 0040H mov si, 0 mov cx, 3 mov bx, 0 a: push cx mov si, 0 mov cx, 16 s: mov ax, data mov ds, ax mov al, [si] mov ah, 0 mov es:[di], ax mov ax, font mov ds, ax mov al, [bx]
inc di mov es:[di], al inc si inc di loop s inc bx pop cx add di, 0080H loop a mov ah, 4ch int 21h code ends end start

經過反覆除錯,編譯連線後執行結果

輸出樣式計算

經過試驗任務1的摸索,掌握了輸出位置的計算:B800H:0000開始的4000byte視訊記憶體區域,彩色字元模式80*25彩色字元模式,每一個字元第一byte決定內容,第二byte決定樣式

7 6 5 4 3 2 1 0

BL R G B I R G B

這樣可以通過需要的樣式計算出效果的資料,存放在資料segment中,用於輸出時呼叫

例如最後一排字元,閃爍1,背景白色111,高亮1,字型藍色011,得出十進位制251,轉換為十六進位制0BFH,作為單位元組資料放在資料段中,其餘同理

多行輸出原理

由於需要多行輸出,每行需要遍歷data段中16個字元 ,採用了雙重迴圈的結構,外層迴圈進入內層迴圈時需要儲存CX暫存器當前值,所以push入棧儲存,當內層迴圈結束時在將其pop取回。需要注意的是每次迴圈需要將di,si重新設定。

關於輸出位置定位,似乎在我筆記本上第0行(B800:0000)不能在黑框中顯示出來,第一個能顯示的行是B800:0000加上80*2個位元組,(160)dec=00A0H,B80A:0000

*修正:如果執行前使用cls指令清屏則能顯示第0行

同理計算出顯示初始位置:

行號(總共25行-顯示3行)/2=開始顯示的11行,11行*80輸出列*2byte/列=1760byte,(1760)dec=06E0H,B800:0000+06E0=B86E:0000

故設定開始位置存放在附加段暫存器es中,mov es,B86E

列號(總共80列-顯示16列)/2=開始列號32列,32列*2byte/列=64byte,(64)dec=0040H

設定偏移量目的變址暫存器di,mov di, 0040H

試驗時遇到的錯誤以及修正,可以看到下圖中每行多出一個字元

一開始在向視訊記憶體地址輸入樣式時使用通用暫存器AX作為容器取出字資料,向視訊記憶體單位元組輸入時也是輸入字資料,從而推測想視訊記憶體輸出時多輸出了一個位元組(向高位傳入了髒資料),從而會多顯示一個字元。將AX改為AL後,傳入單位元組資料,從而問題解決。

OMT

同樣十六個位元組,有內味了

2.實驗任務2

task2.asm彙編源程式程式碼

assume cs:code, ds:data
data segment
    str db 'try', 0
data ends

code segment
start:  
    mov ax, data
    mov ds, ax

    mov si, offset str
    mov al, 2
    call printStr

    mov ah, 4ch
    int 21h

printStr:
    push bx
    push cx
    push si
    push di

    mov bx, 0b800H
    mov es, bx
    mov di, 0
s:      mov cl, [si]
    mov ch, 0
    jcxz over
    mov ch, al
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:   pop di
    pop si
    pop cx
    pop bx
    ret

code ends
end start

編譯連結後執行結果

修改line3, line12後執行結果

對問題的解答

  • line19-22,line36-39,這組對稱使用的push、pop,這樣用的目的是什麼?

  答:19-22push操作將暫存器中的值暫時存放在棧中,以便在跳轉到當前程式段時將寶貴的暫存器空出以便使用。在當前程式段執行完後,line36-39使用pop還原暫存器先前的值,以便繼續正常執行呼叫當前程式段的外層程式段。

  • line30的功能是什麼?

  答:mov es:[di], cx輸出操作,這裡巧妙地對視訊記憶體輸出的兩個位元位同時寫入了資料。cl讀取的是輸出字元的單個位元組,將預先設定好的彩色字元編碼2(00000010紅色)傳入ch。值得注意的是,由於小端法存放,cl位元組存放在低地址,ch存放在高地址。平時書寫雙位元組資料時習慣HHLL,然而在記憶體中(debug時)記憶體自左向右地址從小到大排序,比較容易搞混淆。

3.實驗任務3

task3.asm彙編源程式程式碼

assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret
code ends
end start

子任務1

反彙編檢視MOV AH, 4C的地址

直接使用debug中-g命令執行到程式結束前

檢視資料段中內容

C007->07C0H->(1984)dec

Q: 為什麼這裡dw 1984 沒有佔據16位元組?

以字元形式存放的1984

子任務2

修改後的部分程式碼

start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str
      mov di, offset str
      mov si, 0

print:
    mov ax, 0B800H
    mov es, ax
    mov ch, 0
    mov cl, [di]
    jcxz over
    mov ch, 7
    mov es:[si], cx
    inc di
    add si, 2
    jmp print 

over:   
    mov ah, 4ch
    int 21h

執行結果

注意mov di, offset str的地方應該在迴圈外,否則每次都初始化自增將無效,導致程式陷入死迴圈

4.實驗任務4

task4.asm彙編程式原始碼

assume cs:code, ds:data
data segment
        str db 80 dup(?)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov si, 0

s1:        
        mov ah, 1
        int 21h
        mov [si], al
        cmp al, '#'
        je next
        inc si
        jmp s1
next:
        mov cx, si
        mov si, 0
s2:     mov ah, 2
        mov dl, [si]
        int 21h
        inc si
        loop s2

        mov ah, 4ch
        int 21h
code ends
end start

彙編連結後執行結果

問題解答

  • line12-19實現的功能是?

  答:int 21h呼叫功能1,從標準輸入裝置鍵盤讀取字元,重定向儲存到記憶體資料段中,知道遇到輸入符號#結束迴圈。

  

  • line21-27實現的功能是?

  答:將剛剛從鍵盤輸入並存儲到資料段的字元逐個輸出到標準輸出流顯示器上。

  

5.實驗任務5

在Xcode中建立Command Line Tool工程,語言選擇為C

在Debug選項中將Debug Workflow中總是顯示反彙編(disassembly)選項勾選

C語言源程式

#include <stdio.h>
int sum(int, int);
int main() {
    int a = 2, b = 7, c;
    c = sum(a, b);
    return 0;
}
int sum(int x, int y) {
    return (x + y);
}

設定斷點Debug檢視其反彙編結果

主函式

被呼叫函式

體會:不太懂

  • 可以看出sum函式入口地址0x100000fa0,在主函式中使用callq呼叫。
  • 引數使用值傳遞方式放入暫存器di和si中,最後藉助通用暫存器ax實現加法?
  • 進入函式(程式碼段)時都要push %rbp,執行完後pop並且ret。

五、實驗總結

1、雖然彙編課程已經接近尾聲,但是在實踐中一些基礎的細節仍然經常混淆

  • 一個位元組byte有8位元bit 0000 0000,在十六進位制表示時就是兩個字元00,而字資料有兩個位元組所以格式形如0000H
  • 在彙編源程式編寫時,如果資料不帶H,則是十進位制資料,然而在debug中,資料預設為十六進位制
  • 資料傳送時注意是一個位元組還是兩個位元組,一個暫存器有兩個位元組,拆分成h和l使用則為一個位元組
  • inc指令給暫存器加1,如果要加多個則用多次或者用add指令。這裡+1是(通常對偏移地址)增加一個位元組,而非一個字(2byte)。

2、從試驗任務2中學習的一個技巧:在字串後跟一個0,使用cx讀取輸出到視訊記憶體,使用jcxz(cx equals zero時)判斷跳轉,從而不需要人為數字符個數設定迴圈次數。在實驗3中子任務2進行使用。

3、在X86中暫存器個數有限,通常要出入棧暫時儲存,以便將暫存器挪作他用。