汇编语言学习:使用masm32寻找1-100中的质数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/caozixuan98724/article/details/83512392

最近有点自闭,事情贼多,还要学这么变态的汇编(╥╯^╰╥)。经过异常艰难的探索,终于完成了课程的第一个作业——用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

猜你喜欢

转载自blog.csdn.net/caozixuan98724/article/details/83512392