基础知识

   ;; 输入
MOV AH,01H ; 调用 DOS 功能 01H,从标准输入获取一个字符
INT 21H ; 中断 21H,调用 DOS 功能

;; 输出
MOV AH,02H ; 输出一个字符
INT 21H

;; 终止程序
MOV AX,4C00H
INT 21H

;; 终止程序
MOV AH,4CH
INT 21H

;; 模板
DSEG SEGMENT

DSEG ENDS
CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG

START:
; PROGRAM

CSEG ENDS
END START



;; 回车
MOV DL,0DH ; 为达到换行输出,突出效果的目的,换行输出
MOV AH,02H
INT 21H

;; 换行
MOV DL,0AH
MOV AH,02H
INT 21H

;;1. **回车符 (CR - Carriage Return, ASCII 0x0D)**:在计算机中,回车符通常表示光标移动到当前行的开头。当输出字符时,光标会回到行首,但不会移动到下一行。在老式打字机中,回车的含义是将打印头移至纸张的开头位置。
;;2. **换行符 (LF - Line Feed, ASCII 0x0A)**:换行符表示光标向下移动到下一行,但保持在当前列。它将光标从当前行移到下一行的相同列。在老式打字机中,换行的含义是将纸张向上移动一行。

LEA SI,NUM ; 将数组NUM的地址加载到SI寄存器

;; JAE

下面是一些常见汇编指令的简要说明:

  1. LOOP: 用于循环,根据CX寄存器的值判断是否继续循环。
  2. JZ (Jump if Zero): 如果标志寄存器的Zero标志位为1(表示上一次运算结果为零),则跳转。
  3. JNZ (Jump if Not Zero): 如果标志寄存器的Zero标志位为0(表示上一次运算结果不为零),则跳转。
  4. ADD (Addition): 将两个操作数相加,并将结果存储在目标操作数中。
  5. SUB (Subtraction): 将目标操作数减去源操作数,结果存储在目标操作数中。
  6. SHL (Shift Left): 将操作数左移,相当于乘以2的n次方。
  7. INC (Increment): 将目标操作数加1。
  8. JMP (Jump): 无条件跳转到指定的标签或地址。
  9. XOR (Exclusive OR): 将目标操作数与源操作数进行异或运算。
  10. JLE (Jump if Less Than or Equal): 如果标志寄存器的Zero标志位为1或者Sign标志位(表示结果为负数)为1,则跳转。
  11. JS (Jump if Sign): 如果标志寄存器的Sign标志位为1(表示结果为负数),则跳转。
  12. JZ (Jump if Zero): 如果标志寄存器的Zero标志位为1(表示上一次运算结果为零),则跳转。
  13. SAL (Shift Arithmetic Left): 算术左移,相当于将操作数左移,最高位(符号位)被移出并影响进位标志。
  14. NEG (Negate): 将目标操作数取负值。
  15. JAE (Jump if Above or Equal): 如果标志寄存器的Carry标志位为0(表示无进位)且Zero标志位为0(表示不等于),则跳转。

实验一

1、题目:编制程序利用功能调用键入及显示字符和字符串,将BUF中两个压缩BCD码数值相加并显示结果(十进制形式)。

DSEG SEGMENT ; 数据段定义
DSEG ENDS ; 数据段结束

CSEG SEGMENT ; 代码段定义
ASSUME CS:CSEG,DS:DSEG ; 假定 CS 寄存器指向代码段,DS 寄存器指向数据段

START: ; 程序入口标签
MOV AX,DSEG ; 将数据段地址加载到 AX 寄存器
MOV DS,AX ; 将 DS 寄存器设置为数据段地址

; 先弹窗口,后输入
MOV AH,01H ; 调用 DOS 功能 01H,从标准输入获取一个字符
INT 21H ; 中断 21H,调用 DOS 功能
MOV BL,AL ; 将输入的字符存储到 BL 寄存器

; 先输入,后弹窗口
MOV DL,BL ; 将 BL 寄存器的值复制到 DL 寄存器
MOV AH,02H ; 调用 DOS 功能 02H,输出一个字符
INT 21H ; 中断 21H,调用 DOS 功能

MOV AX,4C00H ; 调用 DOS 功能 4C00H,程序退出
INT 21H ; 中断 21H,调用 DOS 功能
CSEG ENDS ; 代码段结束

END START ; 程序结束

解析

2、设内存DATA单元中存放一个无符号字节数据,编写程序,将其拆分成两位十六进制数,并将存入在HEX,和,HEX+1 单元中,HEX存放高位十六进制数,HEX+1存放低位十六进制数。(拆字)

DSEG    SEGMENT         ; 数据段定义
DATA DB 34H ; 定义DB(字节 8bit)类型的变量DATA,其值为34H
HEX DB 0,0 ; 定义DB类型的变量HEX,即其后连续的一个变量HEX+1,初始值都为0
DSEG ENDS ; 数据段定义结束

CSEG SEGMENT ; 代码段定义
ASSUME CS:CSEG,DS:DSEG ; 将段与段寄存器关联
START:
MOV AX,DSEG ; 将数据段首地址送入DS中
MOV DS,AX ; 将AX中的值加载到DS寄存器

MOV AL,DATA ; 将待拆字的变量DATA送入AL中(DATA仅一个字节)
MOV BL,AL ; 将AL中值送入BL备份

AND AL,0F0H ; 将待拆字值高四位与F0H进行与(&)操作,获得第一个数字字符
MOV CL,4 ; 需要将高四位移位至第四位,由于位移运算符仅能配合操作数 1,而此次要位移四次,故将移动次数4送入CL中
ROR AL,CL ; ROR(循环右移)配合CL使用,达到高四位变低四位的目的,至此,拆出来的第一个“字”已获得
MOV HEX,AL ; 将第一个字符送入要求的HEX中
ADD AL,30H ; 为方便后期输出,将寄存器中数值加30H(数字零在十六进制ASCII中为30H)

MOV DL,AL ; 将要输出字符送至DL中
MOV AH,02H ; 调用系统中断输出显示
INT 21H

MOV DL,0DH ; 为达到换行输出,突出效果的目的,换行输出
MOV AH,02H
INT 21H
MOV DL,0AH
MOV AH,02H
INT 21H

AND BL,0FH ; 将BL中的备份与0FH相与,达到仅提取出低四位的目的
MOV HEX+1,BL ; BL中已然是拆出来的第二个“字”,送入目标HEX+1中
ADD BL,30H ; 为方便后期输出,将寄存器中数值加30H
MOV DL,BL ; 将带输出值送入DL
MOV AH,02H ; 调用系统中断输出显示
INT 21H

MOV AH,4CH ; 将返回码带回结束
INT 21H
CSEG ENDS ; 代码段结束
END START ; 源程序结束

解析

实验二

1、 已知在数据区中DATA开始存放3个字符,编制分支程序完成,输出其中最大的字符

老师版

DSEG SEGMENT ; 数据段定义
ARG DW 1001H,2002H,3003H ; 定义一个包含三个字的数组 ARG,初始值分别为 1001H,2002H,3003H
MAX DW ? ; 定义一个变量 MAX 用于存储最大值,初始化为空
DSEG ENDS ; 数据段结束

CSEG SEGMENT ; 代码段定义
ASSUME CS:CSEG,DS:DSEG ; 假定 CS 寄存器指向代码段,DS 寄存器指向数据段

START: ; 程序入口标签
MOV AX,DSEG ; 将数据段地址加载到 AX 寄存器
MOV DS,AX ; 将 DS 寄存器设置为数据段地址
LEA SI,ARG ; 将数组 ARG 的地址加载到 SI 寄存器

; 比较第一个和第二个元素
MOV AX,[SI] ; 将数组的第一个元素加载到 AX 寄存器
MOV BX,[SI+2] ; 将数组的第二个元素加载到 BX 寄存器
CMP AX,BX ; 比较两个元素
JAE MAX1 ; 如果 AX >= BX,跳转到 MAX1 标签
XCHG AX,BX ; 交换 AX 和 BX 寄存器的值
MAX1:

; 比较当前最大值和第三个元素
MOV BX,[SI+4] ; 将数组的第三个元素加载到 BX 寄存器
CMP AX,BX ; 比较当前最大值和第三个元素
JAE MAX2 ; 如果 AX >= BX,跳转到 MAX2 标签
XCHG AX,BX ; 交换 AX 和 BX 寄存器的值
MAX2:

MOV MAX,AX ; 将最大值存储到 MAX 变量中

MOV AX,4C00H ; 调用 DOS 功能 4C00H,程序退出
INT 21H ; 中断 21H,调用 DOS 功能

CSEG ENDS ; 代码段结束

END START ; 程序结束

学生版

DSEG SEGMENT
NUM DW 1001H,2002H,3003H ; 定义一个包含三个WORD类型元素的数组NUM
MAX DW 0 ; 定义一个用于存储最大值的变量MAX
DSEG ENDS

CSEG SEGMENT
ASSUME DS:DSEG,CS:CSEG ; 指定数据段和代码段寄存器
START:
MOV AX,DSEG ; 将数据段地址加载到AX寄存器
MOV DS,AX ; 将AX中的值加载到DS寄存器
LEA SI,NUM ; 将数组NUM的地址加载到SI寄存器
MOV AX,[SI] ; 将数组的第一个元素加载到AX寄存器
MOV BX,[SI+2] ; 将数组的第二个元素加载到BX寄存器
CMP AX,BX ; 比较数组的第一个和第二个元素
JAE MAX1 ; 如果第一个元素大于等于第二个元素,跳转到MAX1标签
XCHG AX,BX ; 交换AX和BX的值

MAX1:
MOV BX,[SI+4] ; 将数组的第三个元素加载到BX寄存器
CMP AX,BX ; 比较AX和BX的值
JAE MAX2 ; 如果AX大于等于BX,跳转到MAX2标签
XCHG AX,BX ; 交换AX和BX的值

MAX2:
MOV MAX,AX ; 将最大值存储到MAX变量中

MOV BL,AH ; 将AX的高字节(原数组的最后一个元素的高字节)存储到BL中
MOV AL,AH ; 复制AX的高字节到AL中
AND AL,0F0H ; 将AL中的高四位保留,低四位清零
MOV CL,4 ; 移动高四位到低四位的次数
ROR AL,CL ; 将高四位移到低四位
ADD AL,30H ; 转换为ASCII字符

MOV DL,AL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

AND BL,0FH ; 取出AX的高字节的低四位
ADD BL,30H ; 转换为ASCII字符
MOV DL,BL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

; 后两位
MOV AX,MAX ; 将最大值重新加载到AX中
MOV BL,AL ; 复制AX的低字节到BL中
AND AL,0F0H ; 将低四位清零
MOV CL,4 ; 移动高四位到低四位的次数
ROR AL,CL ; 将高四位移到低四位
ADD AL,30H ; 转换为ASCII字符

MOV DL,AL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

AND BL,0FH ; 取出AX的低字节的低四位
ADD BL,30H ; 转换为ASCII字符
MOV DL,BL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

MOV AH,4CH ; 调用21H中断以结束程序
INT 21H
CSEG ENDS
END START ; 源程序结束

后面是拆字进行输出,由于数字是4字节,所以需要进行2次的拆字,输出4下

阿威版:

DSEG SEGMENT
NUM DW 1001H,2002H,4124H
MAX DW ?
DSEG ENDS

CSEG SEGMENT
ASSUME DS:DSEG,CS:CSEG
START:
MOV AX,DSEG
MOV DS,AX
LEA SI,NUM
MOV AX,[SI]
MOV BX,[SI+2]
CMP AX,BX
JAE MAX1
XCHG AX,BX

MAX1:
MOV BX,[SI+4]
CMP AX,BX
JAE MAX2
XCHG AX,BX

MAX2:
MOV MAX,AX

SHOW:
MOV BL,AH
AND BL,0F0H
MOV CL,4
ROR BL,CL
ADD BL,30H ; ASCII
MOV DL,BL
MOV AH,02H
INT 21H

MOV AX,MAX
MOV BL,AH
AND BL,00FH
ADD BL,30H ; ASCII
MOV DL,BL
MOV AH,02H
INT 21H

MOV AX,MAX
MOV BL,AL
AND BL,0F0H
MOV CL,4
ROR BL,CL
ADD BL,30H ; ASCII
MOV DL,BL
MOV AH,02H
INT 21H

MOV AX,MAX
MOV BL,AL
AND BL,00FH
ADD BL,30H ; ASCII
MOV DL,BL
MOV AH,02H
INT 21H

MOV AH,4CH
INT 21H


CSEG ENDS
END START

分段函数

DSEG SEGMENT
ARGX DB -1 ; 定义一个字节型变量 ARGX,并初始化为 -1
RLTY DB 00H ; 定义一个字节型变量 RLTY,并初始化为 00H
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 定义代码段和数据段寄存器

START:
MOV AX,DSEG ; 将数据段地址加载到 AX 寄存器
MOV DS,AX ; 将 DS 寄存器设置为数据段地址

MOV AL,ARGX ; 将 ARGX 中的值加载到 AL 寄存器
AND AL,AL ; 按位与操作,检查 AL 是否为负数
JS A1 ; 如果为负数,跳转到标签 A1
JZ SAVE ; 如果为零,跳转到标签 SAVE
CMP AL,8 ; 比较 AL 和 8
JLE ONE ; 如果 AL 小于等于 8,跳转到标签 ONE
CMP AL,15 ; 比较 AL 和 15
JGE SAVE ; 如果 AL 大于等于 15,跳转到标签 SAVE
SAL AL,1 ; 算术左移 AL 一位,相当于 AL = AL * 2
SAL AL,1 ; 再次左移,相当于 AL = AL * 2
ADD AL,ARGX ; 将 ARGX 加到 AL
SUB AL,2 ; 减去 2
JMP SAVE ; 无条件跳转到标签 SAVE

ONE:
ADD AL,10 ; 如果 AL 小于等于 8,则加上 10
JMP SAVE ; 无条件跳转到标签 SAVE

A1:
NEG AL ; 如果 AL 为负数,取负值

SAVE:
MOV RLTY,AL ; 将 AL 的值保存到 RLTY 变量

SHOWHFX:
ADD AL,30H ; 将 AL 转换为 ASCII 码
MOV DL,AL ; 将 AL 的值加载到 DL 寄存器
MOV AH,02H ; 设置功能号为 02H(字符输出)
INT 21H ; 调用 DOS 中断 21H,输出字符

MOV AX,4C00H ; 设置功能号为 4C00H(程序终止)
INT 21H ; 调用 DOS 中断 21H

CSEG ENDS
END START

  1. JS A1:如果 AL 为负数,跳转到标签 A1
  2. JZ SAVE:如果 AL 为零,跳转到标签 SAVE
  3. CMP AL,8:比较 AL 和 8。
    • JLE ONE:如果 AL 小于等于 8,跳转到标签 ONE
  4. CMP AL,15:比较 AL 和 15。
    • JGE SAVE:如果 AL 大于等于 15,跳转到标签 SAVE
  5. 以下是一系列的算术和逻辑操作,最终结果保存在 RLTY 中。

在x86汇编语言中,JS 是条件跳转指令,其含义是“Jump if Sign (SF=1)”,即当符号标志 SF(Sign Flag)为1时跳转。在这里,JS A1 表示如果符号标志 SF 为1(也就是 AL 寄存器中的值为负数),则跳转到标签 A1 执行相关的代码。这是一种条件性的跳转,用于根据结果是否为负数来执行不同的代码分支。

在x86汇编语言中,JZ 是条件跳转指令,其含义是“Jump if Zero (ZF=1)”,即当零标志 ZF(Zero Flag)为1时跳转。在这里,JZ SAVE 表示如果零标志 ZF 为1(也就是 AL 寄存器中的值为零),则跳转到标签 SAVE 执行相关的代码。这是一种条件性的跳转,用于根据结果是否为零来执行不同的代码分支。

JMP(Jump)指令是无条件跳转指令,执行时会直接跳转到指定的标签或地址,而不受其他条件的限制。因此,一旦JMP执行,程序就会跳转到指定的目标,不再执行JMP之后的代码。

实验三

统计由DATA单元开始的字节数据中的负数元素的个数。其中数据的总个数放在CNT单元中,最终结果放在RLT单元。

DSEG SEGMENT
DATA DB -1, 2, -3, 4, -5 ; 定义一个数据段 DATA,包含 5 个字节的有符号整数
COUNT DB 5 ; 定义一个字节 COUNT,表示数组的长度
RLT DB 0 ; 定义一个字节 RLT,用于存储结果
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 定义代码段和数据段寄存器

START:
MOV AX, DSEG ; 将数据段地址加载到 AX 寄存器
MOV DS, AX ; 将 DS 寄存器设置为数据段地址
MOV BX, OFFSET DATA ; 将数组 DATA 的偏移地址加载到 BX 寄存器

MOV CL, COUNT ; 将数组的长度加载到 CL 寄存器
MOV DX, 0 ; 将 DX 寄存器清零,用于存储负数的个数

AGAIN:
MOV AL, [BX] ; 将 BX 地址指向的字节数据加载到 AL 寄存器
ADD AL, AL ; 将 AL 寄存器的值乘以 2
JNS PLUS ; 如果结果为正数或零,跳转到标签 PLUS
INC DL ; 如果结果为负数,将 DX 寄存器加 1
PLUS:
INC BX ; 将 BX 寄存器的值加 1,指向下一个数组元素
LOOP AGAIN ; 循环,CL 寄存器递减,直到为零

MOV RLT, DL ; 将负数的个数保存到 RLT 变量

SHOW:
ADD DL, 30H ; 将结果转换为 ASCII 码
MOV AH, 02H ; 设置功能号为 02H(字符输出)
INT 21H ; 调用 DOS 中断 21H,输出字符

MOV AX, 4C00H ; 设置功能号为 4C00H(程序终止)
INT 21H ; 调用 DOS 中断 21H

CSEG ENDS
END START

2、题目2:从键盘输入0-9任何一个数字,然后让系统按指令响出铃声。

CSEG SEGMENT           ; 代码段开始
ASSUME CS:CSEG ; 段寄存器关联
START: ; 程序入口标签
XOR AX, AX ; 清零 AX 寄存器
XOR CX, CX ; 清零 CX 寄存器
MOV AH, 01H ; 调用 DOS 中断服务功能 01H,等待从键盘输入一个字符,并在显示器上回显
INT 21H
SUB AL,30H
MOV CL,AL

RING: ; 循环标签
MOV DL, 07H ; 声音字符
MOV AH, 02H ; 调用 DOS ,发出声音
INT 21H

LOOP RING ; 循环指令,指定循环标签为 RING,CX 寄存器的值减 1,直到 CL 为零

MOV AX, 4CH ; 通过 DOS 中断服务功能 4C00H 结束程序
INT 21H

CSEG ENDS ; 代码段结束
END START ; 程序结束

在内存中存放一个完全平方数,求平方根,并把平方根送入ROOT单元。

DSEG SEGMENT
DATA DW 25 ; 定义一个数据 DATA,初始值为 25
ROOT DW 0 ; 定义一个变量 ROOT,用于保存计算得到的平方根
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG, DS:DSEG ; 段寄存器的默认段寄存器 CS 的别名是 CSEG,数据段 DS 的别名是 DSEG

START:
MOV AX, DSEG ; 将 DSEG 的地址加载到 AX 寄存器
MOV DS, AX ; 将 DS 寄存器设置为 DSEG,让 DS 指向数据段

XOR CX, CX ; 清零 CX 寄存器,用于保存计数器
XOR BX, BX ; 清零 BX 寄存器,用于保存中间结果
XOR AX, AX ; 清零 AX 寄存器,用于保存循环计数器
MOV DX, DATA ; 将 DATA 的地址加载到 DX 寄存器

AGAIN:
AND DX, DX ; 判断 DX 是否为零
JZ SAVE ; 如果 DX 为零,则跳转到 SAVE

MOV BX, AX ; 将 AX 寄存器的值复制到 BX,相当于 BX = AX
SHL BX, 1 ; 将 BX 寄存器的值左移一位,相当于 BX = BX * 2
INC BX ; 将 BX 寄存器的值加一
SUB DX, BX ; 将 DX 寄存器的值减去 BX,相当于 DX = DX - BX
INC CX ; 将 CX 寄存器的值加一,用于计数

INC AX ; 将 AX 寄存器的值加一,用于循环计数
JMP AGAIN ; 无条件跳转到 AGAIN

SAVE:
MOV ROOT, CX ; 将计算得到的平方根保存到 ROOT 变量

SHOW:
MOV DX, CX ; 将 CX 寄存器的值复制到 DX
ADD DX, 30H ; 将 DX 寄存器的值加上 ASCII '0',转换为字符
MOV AH, 02H ; 设置功能号为 02H(字符输出)
INT 21H ; 调用 DOS 中断 21H

MOV AX, 4C00H ; 设置功能号为 4C00H(程序终止)
INT 21H ; 调用 DOS 中断 21H

CSEG ENDS

END START

实验四

现在有一组数据:1,3,5,2,9,8,4。请编写程序,按降序排序,并将排序结果输出到屏幕上。同时,将十进制结果输出到显示器上。

DSEG    SEGMENT
A DW 2,5,4,8,3,9 ; 定义一个包含整数2,5,4,8,3,9的数组A
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 假设代码段寄存器为CS,数据段寄存器为DS

START:
MOV AX,DSEG ; 将数据段DSEG的地址加载到寄存器AX
MOV DS,AX ; 将数据段地址设置到数据段寄存器DS
XOR AX,AX ; 寄存器AX清零

MOV CX,6 ; 初始化寄存器CX为数组元素个数6
DEC CX ; CX减一,用于循环计数

LOOP1:
MOV DI,CX ; 将CX的值移动到DI寄存器,用于外层循环计数
MOV BX,0 ; 初始化BX为0,用于内层循环计数

LOOP2:
MOV AX,A[BX] ; 从数组A中加载元素到寄存器AX
CMP AX,A[BX+2] ; 比较当前元素和下一个元素
JGE LOOP3 ; 如果当前元素大于等于下一个元素,跳转到LOOP3
XCHG AX,A[BX+2] ; 这两个东西的值交换了
MOV A[BX],AX ; 将交换后的值放回数组

LOOP3:
ADD BX,2 ; 移动到数组的下一个元素
LOOP LOOP2 ; 内层循环

MOV CX,DI ; 恢复CX的值
LOOP LOOP1 ; 外层循环

SHOW:
XOR CX,CX ; 清零CX寄存器
MOV CX,6 ; 设置CX为数组元素个数6
XOR BX,BX ; 清零BX寄存器
MOV BX,OFFSET A ; 将数组A的偏移地址加载到BX寄存器

LOOP4:
MOV DX,A[BX] ; 从数组A中加载元素到寄存器DX
ADD DX,30H ; 将数字转换为ASCII码
MOV AH,02H ; DOS功能号,用于显示字符
INT 21H ; 调用DOS中断

ADD BX,2
LOOP LOOP4 ; 循环显示数组元素

MOV AX,4C00H ; 设置DOS功能号,用于程序退出
INT 21H ; 调用DOS中断

CSEG ENDS
END START ; 结束程序

在 x86 汇编语言中,DI 是一个 16 位寄存器,通常用作源变址寄存器(Source Index Register)。DI 寄存器经常与字符串和数组操作相关的指令一起使用,用于指示数据的源地址。在上述程序中,MOV DI, CX 语句将 CX 寄存器的值移动到 DI 寄存器,用于循环计数。

  1. 外层循环(LOOP1):

    • MOV DI, CXCX 寄存器的值移动到 DI 寄存器,用于保存外层循环计数值。
    • MOV BX, 0 初始化 BX 寄存器为 0,用于内层循环计数。
  2. 内层循环(LOOP2):

    • MOV AX, A[BX] 从数组 A 中加载元素到 AX 寄存器。
    • CMP AX, A[BX+2] 比较当前元素和下一个元素。
    • JGE LOOP3 如果当前元素大于等于下一个元素,跳转到 LOOP3
    • XCHG AX, A[BX+2] 交换当前元素和下一个元素的值。
    • MOV A[BX], AX 将交换后的值放回数组。
  3. 跳转和循环:

    • ADD BX, 2 移动到数组的下一个元素。
    • LOOP LOOP2 内层循环。如果 CX 寄存器不为零,就会跳回到 LOOP2,继续内层循环。
  4. 外层循环结束后(LOOP1):

    • MOV CX, DI 恢复 CX 的值,以便回到外层循环的起始位置。
    • LOOP LOOP1 外层循环。如果 CX 寄存器不为零,就会跳回到 LOOP1,继续外层循环。

LOOP 指令用于实现循环结构。它的工作方式是首先减小 CX 寄存器的值,然后检查 CX 是否为零。如果 CX 不为零,则根据指定的相对偏移量跳转到循环的起始位置。DI 寄存器并不直接参与 LOOP 指令的工作,而是在循环中的其他指令中使用。

求1到100的和,并将十六进制结果输出到屏幕上。

DSEG    SEGMENT
M DW 0 ; 定义数据段DSEG,包含一个字(2字节)的变量M,初始值为0
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 假设代码段寄存器为CS,数据段寄存器为DS

START:
MOV AX,DSEG ; 将数据段DSEG的地址加载到寄存器AX
MOV DS,AX ; 将数据段地址设置到数据段寄存器DS

MOV AX,0 ; 初始化寄存器AX为0
MOV BX,0 ; 初始化寄存器BX为0

L1:
INC BX ; BX自增
ADD AX, BX ; 将BX的值加到AX寄存器
CMP BX,100 ; 比较BX和100
JL L1 ; 如果BX小于100,则跳转到标签L1

MOV M,AX ; 将AX的值存储到变量M中
MOV BX,AX ; 将AX的值存储到寄存器BX中

SHOW:
MOV CH,4 ; 初始化寄存器CH为4,用于外循环

L2:
MOV CL,4 ; 初始化寄存器CL为4,用于内循环
ROL BX,CL ; 将BX左循环移位CL位
MOV DX,BX ; 将移位后的值存储到寄存器DX中
AND DX,000FH ; 与操作,保留低4位的值

ADD DL,30H ; 转换为ASCII码
; 数字9的后面一个字符是 :
CMP DL,3AH ; 比较DL和ASCII码字符":"的值
JL PRINT ; 如果DL小于":",则跳转到PRINT标签
ADD DL,7H ; 如果DL大于等于":",则加7H,转化成16进制

PRINT:
MOV AH,02H ; DOS功能号,用于显示字符
INT 21H ; 调用DOS中断
DEC CH ; 外循环计数减1
JNZ L2 ; 如果CH不为0,继续内循环

MOV AX,4C00H ; 设置DOS功能号,用于程序退出
INT 21H ; 调用DOS中断

CSEG ENDS
END START ; 结束程序

在给 DL 寄存器的值加上 30H 后,DL 中存储的值就变成了相应的 ASCII 码字符。然后,通过比较 DL 和 ASCII 码字符 3AH,程序判断 DL 中的字符是否小于 ':',如果是,则跳转到 PRINT 标签。

之后,如果 DL 中的字符大于等于 ':',则执行 ADD DL, 7H,相当于将 DL 中的字符加上 7。这样的处理可能是为了将字符 ‘9’ 转换为字母 ‘A’。在 ASCII 码中,字符 ‘0’ 到 ‘9’ 的 ASCII 码是连续的,紧接着是字符 ‘:’,然后是字符 ‘A’ 到 ‘F’,它们也是连续的。为了符合16进制。

ascii码中,字符9与A,相差7H

实验五

十进制转十六进制

DECIHEX SEGMENT ;  段定义,DECIHEX 段开始
ASSUME CS:DECIHEX

; MAIN PROC FAR: 主程序开始
MAIN PROC FAR
REPEAT1:
CALL DECIBIN ; 调用 DECIBIN 过程,将用户输入的十进制数字转换为二进制
CALL CRLF ; 调用 CRLF 过程,输出换行符
CALL BINIHEX ; 调用 BINIHEX 过程,将二进制数字转换为十六进制并输出
CALL CRLF ; 调用 CRLF 过程,输出换行符
JMP REPEAT1 ; 无条件跳转到 REPEAT1 标签,实现循环
MAIN ENDP ; 主程序结束

; DECIBIN PROC NEAR: 十进制转二进制过程开始
DECIBIN PROC NEAR
MOV BX,0 ; BX 寄存器清零,用于存储二进制结果
NEW:
MOV AH,01H ; 调用 DOS 功能 01H,从标准输入获取一个字符
INT 21H ; 中断 21H,调用 DOS 功能
SUB AL,30H ; 将 ASCII 字符转换为数字
JB EXIT ; 若小于 '0',跳转到 EXIT 标签,结束循环
CMP AL,9D ; 若大于 '9',跳转到 EXIT 标签,结束循环
JG EXIT
CBW ; 将 AL 扩展为 AX,处理负数情况
XCHG AX,BX ; 交换 AX 和 BX 寄存器的值
MOV CX,10D ; CX 寄存器存储 10,用于乘法
MUL CX ; 乘以 10,将结果保存在 AX 寄存器
XCHG AX,BX ; 交换 AX 和 BX 寄存器的值
ADD BX,AX ; 将新的数字加到 BX 寄存器中
JMP NEW ; 无条件跳转到 NEW 标签,继续循环
EXIT:
RET ; 返回调用点
DECIBIN ENDP ; 十进制转二进制过程结束

; BINIHEX PROC NEAR: 二进制转十六进制过程开始
BINIHEX PROC NEAR
MOV CH,4 ; CH 寄存器存储 4,用于循环处理每个四位二进制数
ROTATE:
MOV CL,4 ; CL 寄存器存储 4,用于循环左移 BX 寄存器
ROL BX,CL ; 将 BX 寄存器左移 4 位,相当于将四位二进制数移到最高位
MOV AL,BL ; 将 BX 寄存器的低字节(四位二进制数)复制到 AL 寄存器
AND AL,0FH ; 将 AL 寄存器的高四位清零
ADD AL,30H ; 将 AL 寄存器的值转换为 ASCII 码
CMP AL,3AH ; 比较 AL 寄存器的值是否大于等于 'A'
JL PRINTIT ; 若小于 'A',跳转到 PRINTIT 标签
ADD AL,7 ; 若大于等于 'A',加上偏移值,转换为正确的 ASCII 码
PRINTIT:
MOV DL,AL ; 将 AL 寄存器的值复制到 DL 寄存器
MOV AH,02H ; 调用 DOS 功能 02H,输出一个字符
INT 21H ; 中断 21H,调用 DOS 功能
DEC CH ; CH 寄存器减一
JNZ ROTATE ; 若 CH 寄存器不为零,跳转到 ROTATE 标签,继续循环
RET ; 返回调用点
BINIHEX ENDP ; 二进制转十六进制过程结束

; CRLF PROC NEAR: 输出换行符过程开始
CRLF PROC NEAR
MOV DL,0DH ; DL 寄存器存储回车符 ASCII 码
MOV AH,02H ; 调用 DOS 功能 02H,输出一个字符
INT 21H ; 中断 21H,调用 DOS 功能
MOV DL,0AH ; DL 寄存器存储换行符 ASCII 码
MOV AH,02H ; 调用 DOS 功能 02H,输出一个字符
INT 21H ; 中断 21H,调用 DOS 功能
RET ; 返回调用点
CRLF ENDP ; 输出换行符过程结束

DECIHEX ENDS ; DECIHEX 段结束
END MAIN ; 主程序结束

太难了,放弃

其它

从键盘输入连续的字符。当输入小写字母时,显示出对应的大写字母;当输入大写字母时,显示出对应的小写字母。当输入0到9的任何一个数字时,退出程序。

小写转大写

CSEG	SEGMENT
ASSUME CS:CSEG
START:
MOV AH,01H ; 调用21H中断以获取一个字符
INT 21H

CMP AL,'a'
JB EXIT ; 如果小于 'a',跳到退出标签
CMP AL,'z'
JA EXIT ; 如果大于 'z',跳到退出标签

SUB AL,20H ; 转换小写字母为大写字母,大小写的16进制相差20H

MOV DL,AL ; 将转换后的字符放入DL寄存器
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

JMP START ; 继续循环

EXIT:
MOV AX,4C00H ; 调用21H中断以程序结束
INT 21H

CSEG ENDS
END START

输入输出

DSEG SEGMENT
NEXT DB 13,10,'$' ; 定义一个包含回车、换行和'$'的字符串
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 指定代码段和数据段寄存器

START:
MOV AX,DSEG ; 将数据段地址加载到AX寄存器
MOV DS,AX ; 将AX中的值加载到DS寄存器

MOV AH,01H ; 调用21H中断以获取一个字符
INT 21H

MOV BL,AL ; 将输入的字符保存到BL寄存器

LEA DX,NEXT ; 将NEXT的地址加载到DX寄存器
MOV AH,09H ; 调用21H中断以显示字符串
INT 21H

MOV DL,BL ; 将之前保存在BL寄存器中的字符加载到DL寄存器
MOV AH,02H ; 调用21H中断以显示字符
INT 21H

MOV AX,4C00H ; 调用21H中断以结束程序
INT 21H
CSEG ENDS
END START

LEA 是 x86 汇编语言中的一条指令,用于加载有效地址(Load Effective Address)。它的语法如下:

LEA destination, source
  • LEA: 操作码,表示加载有效地址。
  • destination: 目标操作数,通常是一个寄存器,用于存储源操作数的地址。
  • source: 源操作数,通常是一个内存地址或者寄存器。

LEA 并不是用来加载实际的数据,而是用来加载源操作数的地址到目标寄存器中。这个指令在一些情况下用于进行地址计算,但并不执行内存访问。通常,LEA 的使用场景之一是计算数组元素的偏移地址。

在你提供的汇编代码中,有一条 LEA 指令:

LEA DX, NEXT

这条指令将 NEXT 的地址加载到 DX 寄存器中。在这个上下文中,它为后续的显示字符串操作提供了字符串的起始地址。

MAx

这段代码通过比较数组中的元素,找到最大值,并将其存储在MAX变量中。

DSEG SEGMENT
ARG DW 1001H,2002H,3003H ; 定义一个包含三个WORD类型元素的数组
MAX DW 0 ; 定义一个用于存储最大值的变量
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 指定代码段和数据段寄存器
START:
MOV AX,DSEG ; 将数据段地址加载到AX寄存器
MOV DS,AX ; 将AX中的值加载到DS寄存器

LEA SI,ARG ; 将数组ARG的地址加载到SI寄存器

MOV AX,[SI] ; 将数组的第一个元素加载到AX寄存器
MOV BX,[SI+2] ; 将数组的第二个元素加载到BX寄存器

CMP AX,BX ; 比较AX和BX的值
JAE MAX1 ; 如果AX大于等于BX,跳转到MAX1标签
XCHG AX,BX ; 交换AX和BX的值

MAX1:
MOV BX,[SI+4] ; 将数组的第三个元素加载到BX寄存器
CMP AX,BX ; 比较AX和BX的值
JAE MAX2 ; 如果AX大于等于BX,跳转到MAX2标签
XCHG AX,BX ; 交换AX和BX的值

MAX2:
MOV MAX,AX ; 将最大值存储到MAX变量中
MOV AX, 4C00H ; 调用21H中断以结束程序
INT 21H
CSEG ENDS
END START

这段代码将一个字节的数据拆分为两个四位的十六进制数字,并输出到屏幕上。

分段函数

DSEG SEGMENT
ARGX DB 20 ; 定义DB类型的变量ARGX,初始值为20
RLTY DB 0 ; 定义DB类型的变量RLTY,初始值为0
DSEG ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DSEG ; 指定代码段和数据段寄存器
START:
MOV AX,DSEG ; 将数据段首地址送入DS中
MOV DS,AX ; 将AX中的值加载到DS寄存器

MOV AL,ARGX ; 将变量ARGX的值加载到AL寄存器

AND AL,AL ; 检查AL是否为负数
JS A1 ; 如果是负数,跳转到标签A1
JZ SAVE ; 如果是零,跳转到标签SAVE

CMP AL,8 ; 比较AL是否小于等于8
JBE ONE ; 如果是,跳转到标签ONE

CMP AL,15 ; 比较AL是否大于等于15
JGE SAVE ; 如果是,跳转到标签SAVE

SAL AL,1 ; 算术左移一位(相当于乘以2)
SAL AL,1 ; 算术左移一位(相当于乘以2)
ADD AL,ARGX ; 加上ARGX的值
SUB AL,2 ; 减去2
JMP SAVE ; 无条件跳转到SAVE标签

ONE:
ADD AL,10 ; 加上10
JMP SAVE ; 无条件跳转到SAVE标签

A1:
NEG AL ; 取负值

SAVE:
MOV RLTY,AL ; 将AL的值保存到RLTY变量中

MOV DL,10 ; 将10加载到DL寄存器
CBW ; 将AL的值符号扩展到AX寄存器
DIV DL ; AX除以DL,结果保存在AL中,余数保存在AH中
MOV BL,AH ; 将余数AH保存到BL中

CMP AL,0 ; 比较AL是否为0
JZ TT ; 如果是,跳转到标签TT

CBW ; 将AL的值符号扩展到AX寄存器
DIV DL ; AX除以DL,结果保存在AL中,余数保存在AH中
MOV CL,AH ; 将余数AH保存到CL中
ADD CL,30H ; 加上ASCII值'0'
MOV DL,CL ; 将CL的值保存到DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

TT:
ADD BL,30H ; 加上ASCII值'0'
MOV DL,BL ; 将BL的值保存到DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

MOV AH,4CH ; 调用21H中断以结束程序
INT 21H
CSEG ENDS ; 代码段结束
END START ; 源程序结束

普通输出

STACKS  SEGMENT STACK  ; 定义堆栈段
DW 128 DUP(?) ; 分配128字节的堆栈空间
STACKS ENDS ; 堆栈段结束

DSGS SEGMENT ; 数据段定义
STRING DB 'WELCOME! Computer Science & Technology 2021-2 YangFengMei',13,10,'$' ; 定义字符串
DSGS ENDS ; 数据段结束

CSGS SEGMENT ; 代码段定义
ASSUME CS:CSGS,DS:DSGS ; 将段与段寄存器关联
START:
MOV AX,DSGS ; 将数据段首地址送入AX
MOV DS,AX ; 将AX中的值加载到DS寄存器
LEA DX,STRING ; 将字符串的地址加载到DX寄存器
MOV AH,9H ; 调用21H中断以显示字符串
INT 21H
MOV AH,4CH ; 调用21H中断以结束程序
INT 21H
CSGS ENDS ; 代码段结束
END START ; 源程序结束

三数排序(看前面实验)

DATA SEGMENT
FIRST DB 0 ; 存储第一个输入的数
SECOND DB 0 ; 存储第二个输入的数
THIRD DB 0 ; 存储第三个输入的数
MAX DB 0 ; 存储最大值
MID DB 0 ; 存储中间值
MIN DB 0 ; 存储最小值
DATA ENDS

CSEG SEGMENT
ASSUME CS:CSEG,DS:DATA ; 指定代码段和数据段寄存器
START:
MOV AX,DATA ; 将数据段首地址送入AX
MOV DS,AX ; 将AX中的值加载到DS寄存器,设置数据段地址

MOV AH,01H ; 待排序数输入
INT 21H
MOV FIRST,AL ; 将输入的第一个数存储到FIRST

MOV AH,01H
INT 21H
MOV SECOND,AL ; 将输入的第二个数存储到SECOND

MOV AH,01H
INT 21H
MOV THIRD,AL ; 将输入的第三个数存储到THIRD

MOV DL,0DH ; 换行
MOV AH,02H
INT 21H

MOV DL,0AH
MOV AH,02H
INT 21H

MOV AL,FIRST ; 比较前两个数并将大值放入AL,小值放BL
MOV BL,SECOND
CMP AL,BL
JAE UP
XCHG AL,BL

UP:
MOV CL,THIRD ; 取出第三个值准备比较
CMP AL,CL ; 将前一次比较中大值与第三个数比较
JAE GET
XCHG AL,CL ; 此时AL中为最大值

GET:
CMP BL,CL ; 将剩下两数相比较,最小放CL,大值放BL
JAE FINAL
XCHG BL,CL

FINAL:
MOV MAX,AL ; 将最大值存入MAX
MOV MID,BL ; 将中间值存入MID
MOV MIN,CL ; 将最小值存入MIN

MOV DL,MIN ; 待排序数输出
MOV AH,02H
INT 21H

MOV DL,MID
MOV AH,02H
INT 21H

MOV DL,MAX
MOV AH,02H
INT 21H

MOV AH,4CH ; 调用21H中断以结束程序
INT 21H

CSEG ENDS
END START ; 源程序结束

最大值输出(看前面实验)

DSEG SEGMENT
NUM DW 1001H,2002H,3003H ; 定义一个包含三个WORD类型元素的数组NUM
MAX DW 0 ; 定义一个用于存储最大值的变量MAX
DSEG ENDS

CSEG SEGMENT
ASSUME DS:DSEG,CS:CSEG ; 指定数据段和代码段寄存器
START:

MOV AX,DSEG ; 将数据段地址加载到AX寄存器
MOV DS,AX ; 将AX中的值加载到DS寄存器

LEA SI,NUM ; 将数组NUM的地址加载到SI寄存器
MOV AX,[SI] ; 将数组的第一个元素加载到AX寄存器
MOV BX,[SI+2] ; 将数组的第二个元素加载到BX寄存器

CMP AX,BX ; 比较数组的第一个和第二个元素
JAE MAX1 ; 如果第一个元素大于等于第二个元素,跳转到MAX1标签
XCHG AX,BX ; 交换AX和BX的值

MAX1:
MOV BX,[SI+4] ; 将数组的第三个元素加载到BX寄存器
CMP AX,BX ; 比较AX和BX的值
JAE MAX2 ; 如果AX大于等于BX,跳转到MAX2标签
XCHG AX,BX ; 交换AX和BX的值

MAX2:
MOV MAX,AX ; 将最大值存储到MAX变量中
MOV BL,AH ; 将AX的高字节(原数组的最后一个元素的高字节)存储到BL中
MOV AL,AH ; 复制AX的高字节到AL中
AND AL,0F0H ; 将AL中的高四位保留,低四位清零
MOV CL,4 ; 移动高四位到低四位的次数
ROR AL,CL ; 将高四位移到低四位
ADD AL,30H ; 转换为ASCII字符
MOV DL,AL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

AND BL,0FH ; 取出AX的高字节的低四位
ADD BL,30H ; 转换为ASCII字符
MOV DL,BL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

MOV AX,MAX ; 将最大值重新加载到AX中
MOV BL,AL ; 复制AX的低字节到BL中
AND AL,0F0H ; 将低四位清零
MOV CL,4 ; 移动高四位到低四位的次数
ROR AL,CL ; 将高四位移到低四位
ADD AL,30H ; 转换为ASCII字符
MOV DL,AL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

AND BL,0FH ; 取出AX的低字节的低四位
ADD BL,30H ; 转换为ASCII字符
MOV DL,BL ; 将要输出的字符放入DL中
MOV AH,02H ; 调用21H中断以输出字符
INT 21H

MOV AH,4CH ; 调用21H中断以结束程序
INT 21H
CSEG ENDS
END START ; 源程序结束

这段代码通过比较数组中的元素,找到最大值,并将其以ASCII字符的形式输出。