1. 程式人生 > >組合語言學習:使用masm32尋找1-100中的質數

組合語言學習:使用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

https://blog.csdn.net/bobo1356/article/details/51683904