以下面的代码为例,说明如何用汇编语言判断一个数是否为素数,从而进行汇编语言的学习。
点击展开/折叠代码
.MODEL SMALL
.STACK 100H
.DATA
msg_input DB 'Please input a number: $'
msg_prime DB 0DH, 0AH, 'It is a prime number.$'
msg_not DB 0DH, 0AH, 'It is not a prime number.$'
num DW 0
.CODE
START:
MOV AX, @DATA
MOV DS, AX
; 1. 显示输入提示
LEA DX, msg_input
MOV AH, 09H
INT 21H
; 2. 读取数字 (读取一个十进制数)
CALL READ_NUM
MOV num, AX
; 3. 判断素数逻辑
; 如果 n < 2,不是素数
CMP AX, 2
JL NOT_PRIME
JE IS_PRIME ; 2 是素数
; 循环判断: 从 2 到 n-1
MOV CX, 2 ; CX 是除数
CHECK_LOOP:
MOV AX, num
XOR DX, DX ; 清除高位
DIV CX ; AX / CX, 余数在 DX
CMP DX, 0 ; 如果余数为 0,说明被整除
JZ NOT_PRIME
INC CX
MOV AX, CX
MUL AX ; 计算 CX * CX
CMP AX, num ; 只需检查到 根号 n 即可优化
JA IS_PRIME ; 如果 CX*CX > num 还没除尽,就是素数
JMP CHECK_LOOP
IS_PRIME:
LEA DX, msg_prime
JMP DISPLAY
NOT_PRIME:
LEA DX, msg_not
DISPLAY:
MOV AH, 09H
INT 21H
; 4. 退出程序
MOV AH, 4CH
INT 21H
; --- 子程序:读取十进制数字 ---
READ_NUM PROC
XOR BX, BX
READ_CHAR:
MOV AH, 01H ; 读取字符到 AL
INT 21H
CMP AL, 0DH ; 回车键退出
JZ DONE_READ
SUB AL, '0' ; 转换为数字
JL DONE_READ
CMP AL, 9
JG DONE_READ
MOV AH, 0
PUSH AX
MOV AX, 10
MUL BX ; BX = BX * 10
POP DX
ADD AX, DX
MOV BX, AX
JMP READ_CHAR
DONE_READ:
MOV AX, BX
RET
READ_NUM ENDP
END START
- 首先是汇编程序的整体结构。
.MODEL SMALL:指定了程序的内存模型为 16 位。.STACK 100H:定义了堆栈段的大小为 100H。.DATA:定义了数据段,用于存储程序中的变量和常量。.CODE:定义了代码段,用于存储程序的指令。START:程序的入口点。END START:结束程序的定义。
- 数据段:
msg_input:提示用户输入的字符串。msg_prime:输出素数的字符串。msg_not:输出非素数的字符串。num:用于存储用户输入的数字。
- 代码段:
START:程序的入口点。MOV AX, @DATA:将数据段的地址加载到 AX 寄存器中。MOV DS, AX:将 AX 寄存器中的地址加载到 DS 寄存器中,设置数据段。- 显示输入提示:
LEA DX, msg_input:将msg_input的地址加载到 DX 寄存器中。MOV AH, 09H:设置 AH 寄存器为 09H,用于显示字符串。INT 21H:调用中断 21H,执行显示字符串的操作。
- 读取数字:
- 调用
READ_NUM子程序,将用户输入的数字存储在num中。
- 调用
- 判断素数逻辑:
CMP AX, 2:比较AX寄存器中的值和 2。JL NOT_PRIME:如果AX小于 2,跳转到NOT_PRIME标签。JE IS_PRIME:如果AX等于 2,跳转到IS_PRIME标签。- 循环判断:
MOV CX, 2:将 2 加载到 CX 寄存器中,作为除数。CHECK_LOOP:循环标签。MOV AX, num:将num中的值加载到 AX 寄存器中。XOR DX, DX:将 DX 寄存器清零,用于存储余数。DIV CX:将 AX 寄存器中的值除以 CX 寄存器中的值,余数存储在 DX 寄存器中。CMP DX, 0:比较 DX 寄存器中的值和 0。JZ NOT_PRIME:如果 DX 等于 0,说明被整除,跳转到NOT_PRIME标签。INC CX:将 CX 寄存器中的值加 1。MOV AX, CX:将 CX 寄存器中的值加载到 AX 寄存器中。MUL AX:将 AX 寄存器中的值乘以自身,结果存储在 AX 寄存器中。CMP AX, num:比较 AX 寄存器中的值和num中的值。JA IS_PRIME:如果 AX 大于num,跳转到IS_PRIME标签。JMP CHECK_LOOP:跳转到CHECK_LOOP标签,继续循环。
IS_PRIME:如果是素数,跳转到IS_PRIME标签。NOT_PRIME:如果不是素数,跳转到NOT_PRIME标签。
- 显示结果:
- 根据判断结果,加载相应的字符串到 DX 寄存器中。
MOV AH, 09H:设置 AH 寄存器为 09H,用于显示字符串。INT 21H:调用中断 21H,执行显示字符串的操作。
- 退出程序:
MOV AH, 4CH:设置 AH 寄存器为 4CH,用于退出程序。INT 21H:调用中断 21H,执行退出程序的操作。
- 子程序:
READ_NUM:用于读取用户输入的数字。XOR BX, BX:将 BX 寄存器清零,用于存储数字。READ_CHAR:循环标签。MOV AH, 01H:设置 AH 寄存器为 01H,用于读取字符。INT 21H:调用中断 21H,执行读取字符的操作。CMP AL, 0DH:比较 AL 寄存器中的值和回车键的 ASCII 码。JZ DONE_READ:如果 AL 等于回车键的 ASCII 码,跳转到DONE_READ标签。SUB AL, '0':将 AL 寄存器中的值减去 ‘0’ 的 ASCII 码,转换为数字。CMP AL, 9:比较 AL 寄存器中的值和 9。JG DONE_READ:如果 AL 大于 9,跳转到DONE_READ标签。MOV AH, 0:将 AH 寄存器清零。PUSH AX:将 AX 寄存器中的值压入堆栈。MOV AX, 10:将 10 加载到 AX 寄存器中。MUL BX:将 BX 寄存器中的值乘以 AX 寄存器中的值,结果存储在 AX 寄存器中。POP DX:将堆栈中的值弹出到 DX 寄存器中。ADD AX, DX:将 AX 寄存器中的值加上 DX 寄存器中的值,结果存储在 AX 寄存器中。MOV BX, AX:将 AX 寄存器中的值加载到 BX 寄存器中。JMP READ_CHAR:跳转到READ_CHAR标签,继续循环。DONE_READ:读取数字完成,跳转到DONE_READ标签。MOV AX, BX:将 BX 寄存器中的值加载到 AX 寄存器中。RET:返回。
- 总结:
- 该程序实现了判断一个数是否为素数的功能。
- 首先,程序显示输入提示,然后调用
READ_NUM子程序读取用户输入的数字。 - 接下来,程序判断输入的数字是否小于 2,如果是,则不是素数,跳转到
NOT_PRIME标签。 - 如果输入的数字等于 2,则是素数,跳转到
IS_PRIME标签。 - 如果输入的数字大于 2,则进入循环判断。
- 在循环中,程序将输入的数字除以从 2 到
num-1的每个数,如果存在能整除的数,则不是素数,跳转到NOT_PRIME标签。 - 如果循环结束后没有找到能整除的数,则是素数,跳转到
IS_PRIME标签。 - 在
IS_PRIME标签中,程序显示素数的字符串,并跳转到DISPLAY标签。 - 在
NOT_PRIME标签中,程序显示非素数的字符串,并跳转到DISPLAY标签。 - 在
DISPLAY标签中,程序调用中断 21H,执行显示字符串的操作。 - 最后,程序调用中断 21H,执行退出程序的操作。
- 该程序使用了堆栈来存储临时变量,以及一些寄存器来保存中间结果。
- 该程序的时间复杂度为 O(sqrt(n)),其中 n 为输入的数字。
- 该程序的空间复杂度为 O(1),因为只使用了常数个变量。
设计编写汇编程序要注意的是合理分配堆栈空间,避免栈溢出。同时,要注意寄存器的使用,避免寄存器冲突。另外,要注意程序的可读性和可维护性,避免出现冗余代码。