組合語言學習:使用masm32尋找1-100中的質數
最近有點自閉,事情賊多,還要學這麼變態的彙編(╥╯^╰╥)。經過異常艱難的探索,終於完成了課程的第一個作業——用masm32尋找1-100的質數,寫下此篇部落格,轉換一下心情。( • ̀ω•́ )✧
事先宣告,我是組合語言的萌新,接下來的程式碼可能存在許多多餘和不合適的地方,而且我把所有的變數都存放在暫存器裡面,這是極不合理的,在接下來的兩次作業(斐波那契數列和八皇后問題)中我會加深對彙編的理解,改正程式碼中由於技術不足而出現的問題。
一. C語言參考程式
#include "stdio.h" int main() { for(int i=2;i<100;i++){ bool flag = true; for(int j=2;j<i;j++){ if(i%j==0){ flag = false; break; } } if(flag){ printf("%d ",i); } } }
上述程式碼有幾個地方需要強調:
(1)使用flag作為標誌記錄是否是一個質數。
(2)在判斷是否除盡的時候我直接使用的條件是j<i而不是j<i/2是為了方便寫彙編。
二. 環境
使用masm for windows作為程式設計工具,使用起來還是很方便的,也可以對彙編程式碼進行除錯,個人覺得比使用VS要簡潔一些。
三. 暫存器
AX,BX,CX,DX分別代表四個32位的暫存器。而AH,AL分別代表AX的高32位和低32位,BH,BL,CH,CL,DH,DL同理。
四. 常用指令
move AX,BX (將BX中的值放入AX中)
inc AX (將暫存器AX中的值加1)
jmp XXX (無條件跳轉到XXX)
cmp AX,BX (比較AX,BX中的值是否相等)
je XXX (如果相等,則跳轉到XXX)
push AX (把AX的值壓入棧保護)
pop AX (把AX的值恢復)
add DL,BL (DL=DL+BL)
div操作複雜一些,為了取餘,可以參考這篇博文彙編 DIV 指令
四. 整體思路
參照C語言程式碼的邏輯,分別用兩個暫存器儲存被除數和除數。外部的迴圈是被除數不斷增加,直到100,判斷其是否需要輸出,內部迴圈是除數不斷增加,直到被除數本身,遇到可以整除的即可判斷其不是質數,便可以結束這個迴圈。當然,這個過程要變成一個直線化的過程才能寫成彙編程式。
五. 難點
這個作業最大的難點竟然不是找質數,而是把找到的數字輸出出來。使用了彙編才知道,有printf,cin,cout的日子是多麼的幸福。
首先看到的是使用這樣的命令進行輸出:
mov DL,CH
mov AH,2
int 21H
將要輸出的值寫入DL暫存器,然後讓ah為2,最後呼叫中斷來輸出,但是最開始這樣做的時候只得到了一串奇怪的字元,查閱資料發現,這樣輸出的暫存器值對應的ASCII碼,無法直接輸出數字。
這對於從沒有從底層考慮過問題的我確實非常難辦,後來諮詢了大佬,看了別人的原始碼才知道應該怎麼處理這種情況:
(1)通過對暫存器的值進行調整,把其轉化成對應的能輸出數字的ASCII值,比如對於1,1+30h得到的就是對應的1的ASCII碼,也就可以輸出'1'了。
(2)但是這樣只能輸出一位,那對於多位數字應該怎麼處理呢?很簡單,取餘。比如對於17,17/10 得1餘7,7壓入棧,1/10得0餘1,結束迴圈,依次輸出1和7,我們就得到了17.
六. 原始碼
DATAS SEGMENT
DATAS ENDS
STACKS SEGMENT
;此處輸入堆疊段程式碼
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
mov BL, 2 ; save i(that is the dividend)
mov BH, 2 ; save j(that is the divider)
mov CL, 1 ; save flag(1 represent isPrime, 0 represent isNotPrime)
ISPRIME:
cmp BL,100 ; check if need to end the program
je STOP ; end the program
cmp BL,BH ; check if divider reach to dividend iteself
je ISCANPRINT ; judge if can print
XOR AX,AX ; empty AX
MOV AL,BL ; move dividend to AL
DIV BH ; AL%BH=AH remainder stored in AH
cmp AH,0 ; Is there any exception
je NOTPRIME ; if AH stores 0, that means the value stored in BL is not prime
inc BH ; increase the divider
jmp ISPRIME ; turn to the beginning and loop
ISCANPRINT:
cmp CL,1 ; check flag
je PRINT ; print the value
mov BH,2 ; if not prime, divider begin again at 2
mov CL,1 ; reset flag
jmp ISPRIME ; turn to the beginning and loop
PRINT:
XOR BH,BH ; empty BH
mov AX,BX ; move value to AX
call PRINTNUMBER ; print the number
NOTPRIME:
inc BL ; increase dividend
mov CL, 0 ; set flag
jmp ISCANPRINT ; turn to judge
PRINTNUMBER proc near
push ax
push bx
push cx
push dx
mov bx,10
mov cx,0
PUSHTOSTACK:
mov dx,0
div bx
push dx
inc cx
cmp ax,0
jz POPFROMSTACK
jmp PUSHTOSTACK
POPFROMSTACK:
pop dx
add dl,30h ; change ASCII to real number
mov ah,2
int 21h
loop POPFROMSTACK
pop dx
pop cx
pop bx
pop ax
mov AH,2
mov DL,0
int 21h
ret
PRINTNUMBER endp
STOP: RET
CODES ENDS
END START
七. 參考資料
感謝各位大佬提供的原始碼,沒有原始碼的學習,我不可能完成這次作業,掌握彙編的基本知識和解決一些難點。
https://github.com/LLipter/assembly/blob/master/Intel-16/primeNumber.asm