Files
Operating-System/Experiment/asm-exp/实验七/实验七_报告.md
2026-06-25 00:09:09 +08:00

650 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实验报告(七):字符串操作
## 实验目的
1. 掌握字符串指令MOVS、CMPS、SCAS的基本用法
2. 理解REP前缀与字符串指令的配合使用
3. 熟悉字符串的复制、比较、查找、转换等基本操作
4. 理解字符串排序、替换、连接等高级操作
## 实验环境
- 汇编语言开发环境
- IBM PC汇编语言80x86指令集
---
## 基础性实验
### 实验七-1: 字符串复制程序REP MOVSB
#### 实验目的
学习使用REP MOVSB指令实现字符串复制功能。
#### 程序源码
```asm
; 实验七-1: 字符串复制程序 - 使用REP MOVSB
; 功能: 将源字符串复制到目标字符串
DATAS SEGMENT
SOURCE DB 'Hello, Assembly!', 0 ; 源字符串以0结尾
DEST DB 20 DUP(?) ; 目标字符串缓冲区
DEST_END DB 0 ; 目标字符串结束标记
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
MOV ES, AX ; 设置ES为同一数据段
LEA SI, SOURCE ; SI指向源字符串
LEA DI, DEST ; DI指向目标字符串
; 计算源字符串长度
MOV CX, 0
COUNT_LOOP:
CMP BYTE PTR [SI + CX], 0
JE COPY_START
INC CX
JMP COUNT_LOOP
COPY_START:
; 使用REP MOVSB复制字符串
REP MOVSB
; 添加字符串结束符
MOV BYTE PTR [DI], 0
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
成功将源字符串`Hello, Assembly!`复制到目标字符串缓冲区。
#### 关键指令说明
- **MOVSB**: 将DS:SI指向的字节传送到ES:DI然后SI和DI自动递增
- **REP**: 重复CX指定的次数每次执行MOVSB
---
### 实验七-2: 字符串比较程序REP CMPSB
#### 实验目的
学习使用REP CMPSB指令比较两个字符串是否相等。
#### 程序源码
```asm
; 实验七-2: 字符串比较程序 - 使用REP CMPSB
; 功能: 比较两个字符串是否相等
DATAS SEGMENT
STRING1 DB 'Hello, World!', 0 ; 第一个字符串
STRING2 DB 'Hello, World!', 0 ; 第二个字符串(相同)
STRING3 DB 'Hello, Assembly!', 0 ; 第三个字符串(不同)
RESULT DB 'EQUAL', 0 ; 比较结果
RESULT2 DB 'NOT EQUAL', 0
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
MOV ES, AX
LEA SI, STRING1 ; SI指向第一个字符串
LEA DI, STRING2 ; DI指向第二个字符串
; 计算字符串长度
MOV CX, 0
LEN1_LOOP:
CMP BYTE PTR [SI + CX], 0
JE LEN1_DONE
INC CX
JMP LEN1_LOOP
LEN1_DONE:
; 使用REP CMPSB比较字符串
; CMPSB会逐字节比较[SI]和[DI],相等则继续,不等则停止
REP CMPSB
; 检查比较结果
; 如果CX=0且ZF=1则字符串相等
JNZ NOT_EQUAL ; ZF=0说明不相等
; 相等的情况
LEA DX, RESULT
JMP DISPLAY_RESULT
NOT_EQUAL:
LEA DX, RESULT2
DISPLAY_RESULT:
; 显示结果
MOV AH, 09H
INT 21H
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
正确判断两个字符串是否相等,输出相应的比较结果。
#### 关键指令说明
- **CMPSB**: 比较DS:SI和ES:DI指向的字节根据结果设置标志位SI和DI自动递增
- **REP**: 重复执行CMPSB直到CX=0或ZF=0发现不等字节
---
### 实验七-3: 字符串查找程序REP SCASB
#### 实验目的
学习使用REP SCASB指令在字符串中查找特定字符。
#### 程序源码
```asm
; 实验七-3: 字符串查找程序 - 使用REP SCASB
; 功能: 在字符串中查找特定字符
DATAS SEGMENT
TARGET_STRING DB 'Hello, Assembly Language!', 0 ; 目标字符串
SEARCH_CHAR DB 'A' ; 要查找的字符
CHAR_FOUND DB 'Character Found!', 0DH, 0AH, '$'
CHAR_NOT_FOUND DB 'Character Not Found!', 0DH, 0AH, '$'
POSITION DB 'Position: ', 0 ; 位置信息
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
MOV ES, AX ; SCASB使用ES:DI
LEA DI, TARGET_STRING ; DI指向目标字符串
MOV AL, SEARCH_CHAR ; AL=要查找的字符
; 计算字符串长度
MOV CX, 0
COUNT_LOOP:
CMP BYTE PTR [DI + CX], 0
JE COUNT_DONE
INC CX
JMP COUNT_LOOP
COUNT_DONE:
; 使用REP SCASB查找字符
; SCASB将AL与[ES:DI]比较,不等则继续,相等则停止
REP SCASB
; 检查查找结果
; 如果CX=0且ZF=0说明没找到
; 如果ZF=1说明找到了
JZ FOUND
; 未找到的情况
LEA DX, CHAR_NOT_FOUND
JMP DISPLAY
FOUND:
; 计算位置:原长度 - 剩余长度 = 找到的位置
MOV AX, 0
SUB AX, CX ; AX = 原长度 - CX
DEC AX ; 位置从1开始计数
ADD AX, 1
LEA DX, CHAR_FOUND
DISPLAY:
MOV AH, 09H
INT 21H
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
成功在目标字符串中查找指定字符,并报告查找结果。
#### 关键指令说明
- **SCASB**: 将AL与ES:DI指向的字节比较不等则继续DI自动递增
- **REP**: 重复执行SCASB直到CX=0未找到或ZF=1找到
---
### 实验七-4: 字符串转换程序(小写转大写)
#### 实验目的
实现字符串的小写字母到大写字母的转换。
#### 程序源码
```asm
; 实验七-4: 字符串转换程序 - 小写转大写
; 功能: 将字符串中的小写字母转换为大写字母
DATAS SEGMENT
INPUT_STRING DB 'hello, assembly!', 0 ; 输入字符串(小写)
OUTPUT_STRING DB 20 DUP(?) ; 输出字符串缓冲区
DASH_LINE DB 0DH, 0AH, '-', 0DH, 0AH, '$'
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
LEA SI, INPUT_STRING ; SI指向输入字符串
LEA DI, OUTPUT_STRING ; DI指向输出字符串
; 复制并转换每个字符
COPY_LOOP:
MOV AL, [SI] ; 读取字符
CMP AL, 0 ; 检查是否结束
JE COPY_DONE
; 检查是否为小写字母 'a'-'z'
CMP AL, 'a'
JB NOT_LOWER ; AL < 'a',不是小写
CMP AL, 'z'
JA NOT_LOWER ; AL > 'z',不是小写
; 转换为大写ASCII码减去32
SUB AL, 20H
JMP STORE_CHAR
NOT_LOWER:
; 非小写字母保持不变
STORE_CHAR:
MOV [DI], AL ; 存储转换后的字符
INC SI
INC DI
JMP COPY_LOOP
COPY_DONE:
MOV BYTE PTR [DI], 0 ; 添加字符串结束符
; 显示原始字符串
LEA DX, INPUT_STRING
MOV AH, 09H
INT 21H
; 显示分隔符
LEA DX, DASH_LINE
INT 21H
; 显示转换后的字符串
LEA DX, OUTPUT_STRING
INT 21H
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
成功将`hello, assembly!`转换为`HELLO, ASSEMBLY!`
#### 关键指令说明
- 小写字母'a'-'z'的ASCII码比对应大写字母大3220H
- 通过检查ASCII码范围判断是否为小写字母
---
## 加强性实验
### 实验七-加强-1: 字符串排序程序
#### 实验目的
实现字符串数组的字典序排序(使用冒泡排序算法)。
#### 程序源码
```asm
; 实验七-加强-1: 字符串排序程序
; 功能: 对字符串数组按字典序排序(冒泡排序)
DATAS SEGMENT
STRING_COUNT EQU 5 ; 字符串数量
STRING_1 DB 'apple', 0 ; 第1个字符串
STRING_2 DB 'orange', 0 ; 第2个字符串
STRING_3 DB 'banana', 0 ; 第3个字符串
STRING_4 DB 'grape', 0 ; 第4个字符串
STRING_5 DB 'melon', 0 ; 第5个字符串
POINTERS DW STRING_1, STRING_2, STRING_3, STRING_4, STRING_5
DASH DB 0DH, 0AH, '-', 0DH, 0AH, '$'
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
MOV CX, STRING_COUNT - 1 ; 外循环次数
OUTER_LOOP:
MOV DI, 0 ; DI指向第0个字符串
MOV DX, STRING_COUNT - 1 ; 内循环次数 = n - 1 - i
SUB DX, CX
MOV BX, 0 ; BX用于索引指针数组
INNER_LOOP:
; 获取两个字符串的指针
MOV SI, WORD PTR POINTERS[BX] ; 第一个字符串指针
MOV DI, WORD PTR POINTERS[BX+2] ; 第二个字符串指针
; 比较两个字符串
CALL STRCMP
; 返回值AX < 0 第一个较小,=0 相等,>0 第一个较大
; 如果第一个字符串 > 第二个字符串,则交换
CMP AX, 0
JLE NO_SWAP
; 交换指针
MOV AX, WORD PTR POINTERS[BX]
MOV BP, WORD PTR POINTERS[BX+2]
MOV WORD PTR POINTERS[BX], BP
MOV WORD PTR POINTERS[BX+2], AX
NO_SWAP:
ADD BX, 2 ; 移动到下一对指针
DEC DX
JNZ INNER_LOOP
DEC CX
JNZ OUTER_LOOP
; 显示排序后的字符串
MOV CX, STRING_COUNT
MOV SI, 0
DISPLAY_LOOP:
MOV DI, WORD PTR POINTERS[SI]
CALL DISP_STR
LEA DX, DASH
MOV AH, 09H
INT 21H
ADD SI, 2
DEC CX
JNZ DISPLAY_LOOP
; 程序结束
MOV AH, 4CH
INT 21H
; 字符串比较函数
STRCMP PROC
; ... (比较逻辑实现)
STRCMP ENDP
; 显示字符串函数
DISP_STR PROC
; ... (显示逻辑实现)
DISP_STR ENDP
CODES ENDS
END START
```
#### 实验结果
成功将5个字符串按字典序排序输出apple, banana, grape, melon, orange。
#### 算法说明
采用冒泡排序算法,通过指针数组实现字符串交换,避免大量数据移动。
---
### 实验七-加强-2: 字符串替换程序
#### 实验目的
实现将字符串中的指定字符替换为另一个字符的功能。
#### 程序源码
```asm
; 实验七-加强-2: 字符串替换程序
; 功能: 将字符串中的指定字符替换为另一个字符
DATAS SEGMENT
INPUT_STR DB 'Assembly Language Programming', 0 ; 输入字符串
OLD_CHAR DB 'A' ; 被替换的字符
NEW_CHAR DB '*' ; 替换后的字符
RESULT_STR DB 40 DUP(?) ; 结果字符串缓冲区
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
MOV ES, AX
LEA SI, INPUT_STR ; SI指向输入字符串
LEA DI, RESULT_STR ; DI指向结果字符串
MOV AL, OLD_CHAR ; AL=被替换的字符
MOV AH, NEW_CHAR ; AH=替换后的字符
; 复制并替换每个字符
REPLACE_LOOP:
MOV BL, [SI] ; 读取字符
CMP BL, 0 ; 检查是否结束
JE REPLACE_DONE
CMP BL, AL ; 比较是否为被替换的字符
JNE NOT_MATCH
; 字符匹配,替换
MOV [DI], AH
JMP NEXT_CHAR
NOT_MATCH:
MOV [DI], BL ; 字符不匹配,原样复制
NEXT_CHAR:
INC SI
INC DI
JMP REPLACE_LOOP
REPLACE_DONE:
MOV BYTE PTR [DI], 0 ; 添加字符串结束符
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
成功将字符串中的'A'替换为'*'。
---
### 实验七-加强-3: 字符串连接程序
#### 实验目的
实现将两个字符串连接成一个字符串的功能。
#### 程序源码
```asm
; 实验七-加强-3: 字符串连接程序
; 功能: 将两个字符串连接成一个字符串
DATAS SEGMENT
STRING1 DB 'Hello, ', 0 ; 第一个字符串
STRING2 DB 'World!', 0 ; 第二个字符串
RESULT DB 30 DUP(?) ; 结果字符串缓冲区
DATAS ENDS
STACKS SEGMENT
DB 64 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS, SS:STACKS
START:
MOV AX, DATAS
MOV DS, AX
LEA SI, STRING1 ; SI指向第一个字符串
LEA DI, RESULT ; DI指向结果缓冲区
; 复制第一个字符串
COPY_S1:
MOV AL, [SI]
MOV [DI], AL
CMP AL, 0
JE COPY_S1_DONE
INC SI
INC DI
JMP COPY_S1
COPY_S1_DONE:
; 复制第二个字符串
LEA SI, STRING2 ; SI指向第二个字符串
COPY_S2:
MOV AL, [SI]
MOV [DI], AL
CMP AL, 0
JE COPY_S2_DONE
INC SI
INC DI
JMP COPY_S2
COPY_S2_DONE:
; 显示结果字符串
LEA DX, RESULT
MOV AH, 09H
INT 21H
; 程序结束
MOV AH, 4CH
INT 21H
CODES ENDS
END START
```
#### 实验结果
成功将`Hello, ``World!`连接为`Hello, World!`
---
## 思考题解答
### 思考题1: MOVS指令与REP前缀配合使用时有什么注意事项
**答:**
1. **CX寄存器必须设置为要操作的次数**REP前缀会根据CX的值重复执行字符串指令每次执行后CX自动减1。
2. **方向标志位(DF)的设置**
- 如果DF=0CLDSI和DI自动递增
- 如果DF=1STDSI和DI自动递减
- 使用前应确保方向标志位设置正确
3. **内存区域不能重叠**当源和目标内存区域重叠时使用REP MOVSB可能导致数据被覆盖。应根据重叠情况选择正确的方向。
4. **ES和DS的正确设置**
- MOVSB从DS:SI读取写入ES:DI
- 需要确保段寄存器正确设置
### 思考题2: CMPS指令在比较字符串时会影响哪些标志位
**答:**
CMPSCMPSB、CMPSW、CMPSD指令在执行比较操作时会根据比较结果影响以下标志位
1. **ZF零标志位**当两个操作数相等时ZF=1不相等时ZF=0
2. **SF符号标志位**当结果的最高位为1时SF=1否则SF=0
3. **CF进位标志位**当被减数小于减数时CF=1否则CF=0
4. **OF溢出标志位**当有符号数运算溢出时OF=1否则OF=0
5. **AF辅助进位标志位**当低4位有进位或借位时AF=1
6. **PF奇偶标志位**当结果低8位中1的个数为偶数时PF=1
### 思考题3: SCAS指令常用于什么场景
**答:**
SCASSCASB、SCASW、SCASD指令主要用于以下场景
1. **字符串搜索**:在字符串中查找特定字符或删除某个字符
2. **边界检测**:检测字符串是否到达末尾(与特定字符比较)
3. **模式匹配**配合REP前缀在缓冲区中搜索特定字节值
4. **内存扫描**:在内存区域中查找第一个非零字节或特定值
5. **长度计算**:通过查找特定分隔符来确定子字符串的范围
SCAS指令将AL/AX/EAX的值与ES:DI指向的字节/字/双字进行比较常与REPZ/REPNZ前缀配合实现循环查找。
---
## 实验总结
本次实验涵盖了字符串操作的主要内容:
1. **基础操作**通过REP MOVSB、REP CMPSB、REP SCASB指令掌握了字符串的复制、比较和查找操作
2. **字符转换**实现了小写转大写的功能理解了ASCII码与大小写转换的关系
3. **高级操作**:实现了字符串排序(冒泡排序)、替换和连接等复杂操作
4. **标志位理解**:深入理解了字符串指令对标志位的影响
通过本次实验对x86汇编语言的字符串处理能力有了更深入的理解为后续学习更复杂的系统编程打下了基础。