16 KiB
实验报告(七):字符串操作
实验目的
- 掌握字符串指令MOVS、CMPS、SCAS的基本用法
- 理解REP前缀与字符串指令的配合使用
- 熟悉字符串的复制、比较、查找、转换等基本操作
- 理解字符串排序、替换、连接等高级操作
实验环境
- 汇编语言开发环境
- IBM PC汇编语言(80x86指令集)
基础性实验
实验七-1: 字符串复制程序(REP MOVSB)
实验目的
学习使用REP MOVSB指令实现字符串复制功能。
程序源码
; 实验七-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指令比较两个字符串是否相等。
程序源码
; 实验七-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指令在字符串中查找特定字符。
程序源码
; 实验七-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: 字符串转换程序(小写转大写)
实验目的
实现字符串的小写字母到大写字母的转换。
程序源码
; 实验七-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码比对应大写字母大32(20H)
- 通过检查ASCII码范围判断是否为小写字母
加强性实验
实验七-加强-1: 字符串排序程序
实验目的
实现字符串数组的字典序排序(使用冒泡排序算法)。
程序源码
; 实验七-加强-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: 字符串替换程序
实验目的
实现将字符串中的指定字符替换为另一个字符的功能。
程序源码
; 实验七-加强-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: 字符串连接程序
实验目的
实现将两个字符串连接成一个字符串的功能。
程序源码
; 实验七-加强-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前缀配合使用时,有什么注意事项?
答:
-
CX寄存器必须设置为要操作的次数:REP前缀会根据CX的值重复执行字符串指令,每次执行后CX自动减1。
-
方向标志位(DF)的设置:
- 如果DF=0(CLD),SI和DI自动递增
- 如果DF=1(STD),SI和DI自动递减
- 使用前应确保方向标志位设置正确
-
内存区域不能重叠:当源和目标内存区域重叠时,使用REP MOVSB可能导致数据被覆盖。应根据重叠情况选择正确的方向。
-
ES和DS的正确设置:
- MOVSB从DS:SI读取,写入ES:DI
- 需要确保段寄存器正确设置
思考题2: CMPS指令在比较字符串时会影响哪些标志位?
答:
CMPS(CMPSB、CMPSW、CMPSD)指令在执行比较操作时会根据比较结果影响以下标志位:
- ZF(零标志位):当两个操作数相等时,ZF=1;不相等时,ZF=0
- SF(符号标志位):当结果的最高位为1时,SF=1;否则SF=0
- CF(进位标志位):当被减数小于减数时,CF=1;否则CF=0
- OF(溢出标志位):当有符号数运算溢出时,OF=1;否则OF=0
- AF(辅助进位标志位):当低4位有进位或借位时,AF=1
- PF(奇偶标志位):当结果低8位中1的个数为偶数时,PF=1
思考题3: SCAS指令常用于什么场景?
答:
SCAS(SCASB、SCASW、SCASD)指令主要用于以下场景:
- 字符串搜索:在字符串中查找特定字符或删除某个字符
- 边界检测:检测字符串是否到达末尾(与特定字符比较)
- 模式匹配:配合REP前缀在缓冲区中搜索特定字节值
- 内存扫描:在内存区域中查找第一个非零字节或特定值
- 长度计算:通过查找特定分隔符来确定子字符串的范围
SCAS指令将AL/AX/EAX的值与ES:DI指向的字节/字/双字进行比较,常与REPZ/REPNZ前缀配合实现循环查找。
实验总结
本次实验涵盖了字符串操作的主要内容:
- 基础操作:通过REP MOVSB、REP CMPSB、REP SCASB指令掌握了字符串的复制、比较和查找操作
- 字符转换:实现了小写转大写的功能,理解了ASCII码与大小写转换的关系
- 高级操作:实现了字符串排序(冒泡排序)、替换和连接等复杂操作
- 标志位理解:深入理解了字符串指令对标志位的影响
通过本次实验,对x86汇编语言的字符串处理能力有了更深入的理解,为后续学习更复杂的系统编程打下了基础。