python numpy for迴圈矩陣乘法
一、實驗目的
-
理解80×25彩色字元模式顯示原理
-
理解轉移指令jmp, loop, jcxz的跳轉原理,掌握使用其實現分支和迴圈的用法
-
理解轉移指令call, ret, retf的跳轉原理,掌握組合使用call和ret/retf編寫彙編子程式的方法,掌握
引數傳遞方式
-
理解標誌暫存器的作用
-
理解條件轉移指令je, jz, ja, jb, jg, jl等的跳轉原理,掌握組合使用匯編指令cmp和條件轉移指令實
現分支和迴圈的用法
-
瞭解在visual studio/Xcode等環境或利用gcc命令列引數反彙編c語言程式的方法,理解編譯器生成
的反彙編程式碼
-
綜合應用定址方式和彙編指令完成應用程式設計
二、實驗準備
實驗前,請複習/學習教材以下內容:第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 moves, 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中暫存器個數有限,通常要出入棧暫時儲存,以便將暫存器挪作他用。