以下面的代码为例,说明如何用汇编语言判断一个数是否为素数,从而进行汇编语言的学习。

点击展开/折叠代码

.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),因为只使用了常数个变量。

设计编写汇编程序要注意的是合理分配堆栈空间,避免栈溢出。同时,要注意寄存器的使用,避免寄存器冲突。另外,要注意程序的可读性和可维护性,避免出现冗余代码。


参考

王爽网课