简介:微机原理与接口技术课程中的循环程序设计实验报告,面向需要掌握8086汇编循环结构、重复控制指令与串操作指令的计算机专业学生,尤其适合正在完成“实验三”课程作业或备战接口技术考试的学习者。报告以完整实验报告书形式呈现,涵盖实验目的、验证题与设计题的源程序清单、调试过程及结果分析。验证题通过两段源程序比较BUF1与BUF2数据区是否相同,设计题则要求统计BUF字数据区第一个零数据后的正负数个数,并分别使用重复控制指令和串操作指令实现,有助于读者深入理解LOOPZ、REPZ、CMPSB等指令的格式与特点。资源为单个doc文档,大小约224KB,内容包含详细的源程序注释与实验分析,重点辨析了例1和例2中SI、DI初值设置上的差异,并比较了循环控制方式的优劣,可作为课程实验报告参考模板,也可用于考前梳理循环搭建思路。目前已有2187人学习下载。
1. 循环程序设计:先把“出口条件”写清楚再动手
第一次拿到实验三,题目看着不难:两个等长数据区逐个比较,相同就把 FLAG 置 0,不同就置 -1。结果我在这道题上耗了一下午,原因后来回想只有一个——我一直在盯循环体里的取数、比较、指针自增,却始终没把“循环到底会在什么条件下走出来”写在纸上。调试器里 ZF 标志位一会儿 0 一会儿 1,像在跟我玩玄学。
这篇就是从这份实验报告出发,把循环程序设计最核心的东西拆开讲:验证题里同一个“判断两个数据区是否相同”的功能,用重复控制指令 LOOPZ 和串操作指令 REPZ CMPSB 各写一遍,对比 SI、DI 初值为什么不一样;设计题里“先找第一个零,再统计其后正负数”的两段循环怎么衔接,以及计数控制、条件控制分别在什么场合用。适合正在做实验三但总觉得“程序跑通了却说不清为什么”的同学,也适合想把 LOOP 系列和串指令系列一次理顺的人。
直接说结论:这两个验证程序的功能完全相同,但 SI、DI 的初值一个指向数据区首元素的前一个单元,一个直接指向首元素。这不是谁写错了,而是两种循环机制对“先比较还是先调整指针”的处理顺序不一样。把这个差别吃透,后面用 REPNE SCASB 找零数据时,就不会因为停下来的指针位置不对而翻车。
2. 验证题两种写法:手动循环与串指令的初值差别
2.1 写法一:LOOPZ 配合“初值减一、循环内先加”
先看第一段可运行代码,我把原报告里最后一个字节的写法统一成了 36H,只是为了十六进制习惯看起来一致,语义没动:
DSEG SEGMENT BUF1 DB 21H, 3AH, 36H BUF2 DB 31H, 0C7H, 36H COUNT EQU $-BUF2 FLAG DB 0 DSEG ENDS SSEG SEGMENT STACK DB 80H DUP(0) SSEG ENDS CSEG SEGMENT ASSUME DS:DSEG, SS:SSEG, CS:CSEG START: MOV AX, DSEG MOV DS, AX MOV SI, OFFSET BUF1-1 MOV DI, OFFSET BUF2-1 MOV CX, COUNT NEXT: INC SI INC DI MOV AL, [SI] CMP AL, [DI] LOOPZ NEXT JZ OK MOV FLAG, -1 OK: MOV AH, 4CH INT 21H CSEG ENDS END STARTBUF1 的三个字节是 21H、3AH、36H,BUF2 是 31H、0C7H、36H。前两个字节都不同,第三字节相同,所以程序最终应该把 FLAG 置成 -1。这里的 COUNT EQU $-BUF2 是汇编时算出来的常量:$ 表示当前位置,减去 BUF2 的起始地址,正好得到 3,也就是数据区长度。
OFFSET BUF1-1 这个“-1”是整段代码最值得琢磨的地方。循环体一进来就先 INC SI、INC DI,然后才 MOV AL,[SI] 比较。如果初值写成 OFFSET BUF1,第一次取数就会取到 BUF1+1 的内容,首元素被跳过。所以初值先退一个字节,让第一次 INC 之后正好落在 BUF1[0] 上。DI 同理。这个“初值减一、体内先加”的结构,本质是把地址调整放在取数之前,让循环体从第一个元素开始处理。
LOOPZ NEXT 的语义要记准:它先把 CX 减一,然后判断 ZF=1 且 CX≠0 才跳回 NEXT。所以这个循环只有两种正常出口:要么 CX 数到 0,并且整个过程中没有出现不相等;要么某次 CMP 之后 ZF=0,循环立刻停下来。落到 LOOPZ 下面那条 JZ OK 就是用来区分这两种情况的——ZF=1 说明是“全部相等”正常退出,跳去 OK 保留 FLAG=0;ZF=0 说明中途遇到不等,顺序执行 MOV FLAG,-1。
这种手动循环的好处是逻辑全摊在明面上,SI、DI、CX 每轮怎么变都能单步盯住,非常适合第一次写循环程序的人。坏处是初值和循环体的先后顺序一旦记混,就漏首元素或者多跑一轮,而且这类问题在数据量小的时候很难被发现。
2.2 写法二:REPZ CMPSB 一行代码完成循环
第二段代码看起来更短,短到很多人反而不敢用:
DSEG SEGMENT BUF1 DB 21H, 3AH, 36H BUF2 DB 31H, 0C7H, 36H COUNT EQU $-BUF2 FLAG DB 0 DSEG ENDS SSEG SEGMENT STACK DB 80H DUP(0) SSEG ENDS CSEG SEGMENT ASSUME DS:DSEG, ES:DSEG, SS:SSEG, CS:CSEG START: MOV AX, DSEG MOV DS, AX MOV ES, AX MOV SI, OFFSET BUF1 MOV DI, OFFSET BUF2 MOV CX, COUNT CLD REPZ CMPSB JZ OK MOV FLAG, -1 OK: MOV AH, 4CH INT 21H CSEG ENDS END START这段代码里 SI、DI 直接指向 BUF1 和 BUF2 的首地址,没有再减一。原因是 REPZ CMPSB 的硬件执行顺序和手动循环正好相反:它先比较一次 [SI] 与 [DI],比较完成之后才按 DF 方向调整 SI、DI,然后 CX 减一,再判断是否继续重复。既然第一次比较发生在地址调整之前,初值就必须是真正的首元素地址,不能再写 OFFSET BUF1-1。
另一个容易忽略的点是 ES 段。CMPSB 的第二个操作数固定隐含在 ES:[DI] 里,所以即使 BUF1 和 BUF2 都在 DSEG 段内,也必须 MOV ES,AX 把 ES 指向同一个数据段。漏掉这一句,ES 还停在程序加载前的某个残留值,比较的对象就从 BUF2 变成了内存里其他区域的数据。这种错非常隐蔽:如果残留段恰好处在一个可读位置,程序不会崩溃,比较结果也像是正常的,只有换一台机器或换种启动方式才暴露。
CLD 的作用是把 DF 清零,让 REPZ CMPSB 每轮比较完自动把 SI、DI 向高地址方向推进。验证题里如果忘了 CLD,而 DF 恰好为 1,两个指针就会反向跑,要么比较顺序全乱,要么直接越界访问。手动循环版不需要 CLD,因为它用 INC SI、INC DI 手动调整地址,根本不用理会 DF;这也是两段代码无法直接互相替换的原因之一。
串指令停止后的状态和手动版一样:ZF=1 表示正常比较完且全部相等,ZF=0 表示中途遇到了不相等。所以下面的 JZ OK 判断逻辑和写法一完全一致,只是循环体本身从代码里消失了。
2.3 两个版本的优劣判断:用一张表收口
| 对比维度 | LOOPZ 手动循环 | REPZ CMPSB 串操作 |
|---|---|---|
| 地址初值 | OFFSET BUFx-1,循环内先加 | OFFSET BUFx,硬件先比较后调整 |
| 段寄存器 | 只用 DS | 需要 DS 与 ES,ES 不能省 |
| 方向标志 | 不需要管 DF | 必须先 CLD 或 STD |
| 循环出口 | ZF=0 或 CX=0 | ZF=0 或 CX=0 |
| 可读性 | 循环体可见,适合教学 | 一条指令封装全部动作 |
| 执行效率 | 每轮取指、回跳,开销大 | 硬件重复前缀,少指令取指,更快 |
| 调试难度 | 可单步看每轮 SI/DI/CX | 黑匣子,只能查结束状态 |
我的结论是:这两个程序都能正确完成验证题。如果只是交作业、跑结果,哪个都行;但如果数据量拉长到几百字节以上,REPZ CMPSB 的优势就很明显,它把循环回跳的取指开销省掉了,执行速度基本取决于内存访问本身。反过来,如果比较过程中要做额外记录,比如记下第一次不相等的下标位置,手动循环可以在循环体里顺手保存 SI,而 REPZ CMPSB 停下来之后还得从初值重新推算位置,反而绕远路。
考试或者答辩时被问到“两个程序的 SI、DI 初值设置有什么区别”,标准答案就是:例 1 的初值比首地址少 1,循环体内先自增再比较;例 2 的初值就是首地址,因为串指令先比较再自动调整指针。这句话能说清楚,验证题就算真正吃透了。
3. 设计题:先找第一个零,再统计正负数
3.1 数据区定义里的“单位”问题
设计题的数据区来自原报告,我按可读性整理了一下:
DATA SEGMENT BUF DB 23H, 04H, 11H, 00H, 0A3H, 15H, 6BH, 0A7H, 0C8H NUM EQU $-BUF POSI DB 0 NEGA DB 0 DATA ENDSBUF 一共 9 个字节,第 4 个字节是 00H。第一个零后面的字节依次是 0A3H、15H、6BH、0A7H、0C8H,其中正数是 15H 和 6BH 两个,负数 0A3H、0A7H、0C8H 三个,这是题目期待的结果。这里我用了 NUM EQU $-BUF 而不是原报告里的 $-BUF+1,因为后面给出的循环写法是“先比较、后调整、再计数”,不再需要那个 +1 的余量;原报告的 +1 是配合它自己的先 dec 再判断的循环习惯,两种写法都能跑通,但边界的思考方式不同,这个细节在第 4 章避坑部分还会提到。
动手写代码之前,先把“单位”定死:BUF 是 DB,每个元素占 1 字节,指针步长是 1,CX 计数的单位是“元素个数”而不是“字节数”。如果题目说的是字数据区,那么步长要改成 2,比较指令要写成 CMP WORD PTR [SI],0,CX 要放字的个数而不是字节个数。这三处必须同时改,只改一处就会出现正数个数翻倍之类的离谱结果。
3.2 用重复控制指令搜索并统计
先看一个可以直接跑的版本,搜索和统计都用显式循环:
ASSUME DS:DATA, CS:CODE CODE SEGMENT START: MOV AX, DATA MOV DS, AX MOV SI, OFFSET BUF MOV CX, NUM SEARCH: CMP BYTE PTR [SI], 0 JZ FOUND INC SI LOOP SEARCH JMP EXIT ; 整个数据区都没有零 FOUND: INC SI ; SI 指向零后的第一个数据 DEC CX ; 已经扫过一个零,剩余次数减一 XOR BL, BL ; BL 存正数个数 XOR AL, AL ; AL 存负数个数 COUNT: CMP CX, 0 JZ STORE CMP BYTE PTR [SI], 0 JL NEG_CNT JZ ZERO_SKIP INC BL JMP ADJUST NEG_CNT: INC AL ZERO_SKIP: ADJUST: INC SI DEC CX JMP COUNT STORE: MOV POSI, BL MOV NEGA, AL EXIT: MOV AH, 4CH INT 21H CODE ENDS END STARTSEARCH 循环是典型的条件控制:它不知道零在第几个位置,只能每轮比较一次 [SI] 和 0,命中就跳出,没命中就 INC SI 继续。如果 CX 耗尽还没找到,说明数据区里没有零,这种情况在题目给的用例里不会出现,但程序必须留这个出口,否则 CX 减到 0 之后还会继续跑下去,越界风险就在这里。
找到零之后要做两件事:INC SI 把指针移到零后面的第一个数据;DEC CX 把已经消耗掉的查找次数从总次数里扣掉。这两步的顺序不能反,因为 CX 此时代表“还没进入统计阶段的数据个数”,它必须和 SI 指向的位置严格对应。统计阶段每处理一个元素就 INC SI 一次、DEC CX 一次,直到 CX 归零。
统计循环里有一个特别容易踩的坑:判断正负数要用带符号数。CMP BYTE PTR [SI],0 之后,JL NEG_CNT 是带符号的小于判断,所以 0A3H、0A7H、0C8H 这些大于 127 的值会被正确识别成负数。如果误用 JB,它会按无符号数比较,把 0A3H 当成 163 处理,结果负数全部归到正数里去。这个错误在数据全是正数的时候暴露不出来,只要混进一个高位为 1 的字节就立刻翻车。
还有一点,程序把“再次遇到 0”的情况处理成跳过不计数。题目只要求统计第一个零之后的正负数,并没有说后面不能再有零,所以 ZERO_SKIP 这条分支是有意义的,不能省。
3.3 用串指令找零,统计部分复用
题目要求“搜索第一个零数据时分别采用重复控制指令和串操作指令”,实现方式就是只换搜索段,统计段完全复用。串指令版可以这样写:
MOV AL, 0 ; 要搜索的数据是 0 MOV DI, OFFSET BUF MOV CX, NUM CLD REPNE SCASB ; 从 BUF 开始扫描,找到 0 或 CX 耗尽时停止 JNZ EXIT ; ZF=0 说明没找到,直接退出 MOV SI, DI ; DI 已经指向零后面的一个字节 ; 从这里开始复用统计循环 ; 剩余次数直接用 CX,不要再重新赋初值REPNE SCASB 的执行过程要拆开看:SCASB 把 AL 和 ES:[DI] 比较,然后按 DF 方向调整 DI,CX 减一,最后判断是否重复。REPNE 的含义是“只要不等就继续”,所以 ZF=0 且 CX≠0 时会一直扫下去;一旦某个字节等于 AL,ZF=1,前缀立即停止。
这里最关键的注释就是“DI 已经指向零后面的一个字节”。很多同学第一次写这段代码,潜意识里觉得 SCASB 停止时应该停在零的那个地址上,于是统计前又补了一条 INC DI,结果把 0 后面的第一个数据跳过,输出结果正好少一个。原因就是没有理解“先比较、后调整地址”这个执行顺序:比较完成之后,DI 的调整动作已经发生了,停止时的 DI 天然指向匹配字节的后一个位置。
剩余次数的处理也和手动版不同。手动版在 FOUND 处做了一次 DEC CX,因为它的 CX 还是“搜索前的总个数”;串指令版里 CX 已经被 REPNE SCASB 自动扣减过了,停止时 CX 恰好就是“找到零之后剩余的元素个数”。所以统计阶段直接用 CX 作为计数器,不能重新 MOV CX,NUM,否则会把前面已经扫过的次数加回来,统计循环多跑好几轮,轻则多计数,重则越界。
原报告里还附带了一段求三个字数据平均值并逐位输出二进制的程序,核心就是 MOV CX,16 控制移位次数,一次循环转出一个位。它和设计题正好互补:前者是死次数循环,后者是查数据找停止点,两种循环程序的典型形态就在这一份实验里凑齐了。
4. 避坑:循环程序调试中的五个翻车点
4.1 坑一:ES 没设对,串指令拿旧内存当数据
现象:验证题 REPZ CMPSB 版,代码看着和参考一模一样,但运行结果时对时错。单步执行逻辑又挑不出毛病,换个启动环境结果就变了。
原因:CMPSB 的第二个操作数隐含在 ES:[DI] 段,不是普通内存段。初学者往往只写了 MOV DS,AX,把 MOV ES,AX 漏了,ES 保留的是程序运行前系统残留的段值。如果残留段恰好落在可读区域,程序就在一个错误的内存地址上做比较,结果看起来像“对的”;一旦 ES 指向不可读区域,程序马上崩溃。它不是每次必炸,而是看环境给不给面子,排查起来非常难受。
解决:凡是写串操作指令,程序开头直接固定三连:MOV AX,DSEG、MOV DS,AX、MOV ES,AX。ASSUME 语句里也写上 ES:DSEG。哪怕两个数据区本来就同段,这句也不能省,这是串指令程序的肌肉记忆。
4.2 坑二:DF 方向没管,第二次扫描就偏移
现象:用 REPNE SCASB 搜索 0,第一次运行结果正常,清掉内存重新加载数据后结果变乱;或者搜索到的零总是落在数据区末尾往后一点的位置。
原因:串指令每轮执行后都按 DF 调整地址。DF 为 0 时地址增大,DF 为 1 时地址减小。程序如果没写 CLD 也没写 STD,DF 的值取决于启动环境残留,不是一个可靠状态。DF=1 时,REPNE SCASB 从 BUF 首地址出发会向低地址方向扫描,扫到的是 BUF 之前的内存,自然找不到数据区里的零,或者乱找一气。
解决:所有用到串指令的程序段,进入后马上 CLD。需要从尾部向前比较时再显式 STD,不需要时不要留着上一次调用留下的方向标志。不要依赖“这段代码之前有人 CLD 过”,那是最不靠谱的隐式约定。
4.3 坑三:首元素被跳过,统计结果总是少一个
现象:验证题手动循环偶尔比较结果和手算不一致;设计题统计正负数之后,两个计数值加起来比零后面的元素总数少一个,或者多一个。
原因:绝大多数情况是初值和循环体的先后顺序配错了。手动版写成 OFFSET BUF1 却保留“循环内先 INC 再取数”的结构,第一次比较就落在 BUF1+1 上,首元素被跳过;串版反过来写成 OFFSET BUF1-1,第一次比较发生在地址调整之前,直接从首元素前一个单元开始错起。还有的人是只改了 SI 初值,DI 忘了对应调整,导致两个指针相对偏移一个字节。
解决:写任何一个循环扫描,先在代码注释里写三行:初值、步长、出口条件。手动版写“SI=BUF1-1,每轮 +1,CX 次或 ZF=0 时出”;串版写“SI=BUF1,每轮 +1,硬件先比较再调整”。两行注释写出来不一致,说明你把两种写法混用了,先改注释,再改代码。
4.4 坑四:REPNE SCASB 停在哪里,理解反了
现象:设计题用串指令找零后,统计阶段的第一个有效数据被跳过;POSI 和 NEGA 加起来,正好比预期少一个。
原因:把 REPNE SCASB 记成了“停在匹配点”的指令。实际上它比较完成后调整 DI,然后才判断是否重复,停止时 DI 已经指向零的下一个单元。如果统计时又从 DI-1 开始,等于把零自己处理了一遍;如果又在开头补一条 INC DI,则多跳过一个元素。两种做法都会让统计结果刚好少一个。
解决:把“停止时 DI 指向零后第一个数据”这个结论当成死记硬背也要背下来。上机验证时,在 REPNE SCASB 后面看一眼 DI 的值,和 OFFSET BUF 做差,确认等于“零的下标加一”,再写统计循环。剩余次数直接用停止时的 CX,不要再重新赋值。
4.5 坑五:字节与字混用,步长没有改成 2
现象:把字节版循环程序原样搬到 DW 定义的数据区上,正数和负数的统计结果接近正常值两倍,或者负数全部丢失。
原因:INC SI 在 DB 数组里移动一个元素,在 DW 数组里只移动一个字节。CMP BYTE PTR [SI],0 只比较一个字的低字节,符号位在高字节里根本没有参与判断。步长和比较宽度都不对,循环却照样跑完,结果自然错得离谱。
解决:数组单位从字节改成字时,必须同时改三处:指针步长改成 ADD SI,2;比较指令改成 CMP WORD PTR [SI],0;CX 设置为元素个数而不是字节数。改完先算一笔账:长度、个数、步长三者相乘是否等于数据区结尾地址,不等就坚决不上机。
5. 循环结构到底怎么选:计数控制、条件控制与重复前缀
5.1 计数控制:循环次数是死的
计数控制的适用条件很明确:循环次数在进入循环之前就能确定,而且中途不需要因为数据内容提前退出。原报告里那一段逐位输出二进制的程序就是典型例子:
MOV CX, 16 DISP: MOV DL, '0' RCL BX, 1 JNC NEXT MOV DL, '1' NEXT: MOV AH, 2 INT 21H LOOP DISP16 次循环,一次不多一次不少,输出完整个 BX 的二进制形态。这类结构的写法口诀只有一条:次数先给 CX,循环体末尾交给 LOOP。不要在循环体里给 CX 赋值,也不要在循环体里调用任何会修改 CX 的子程序,否则次数就作废了。统计正负数的程序如果统计子程序内部也用 CX 做其他事,主循环的剩余次数在返回时已经被覆盖,这就是很多“大循环套小循环就乱掉”的根源。
判断依据就两条:次数是否已知,动作是否可以在进入前编号。条件不满足,就不要硬套计数控制。
5.2 条件控制:循环次数看数据说话
条件控制用在另一种场景:循环是否继续,由比较结果决定。验证题的 LOOPZ、设计题的搜索段,都属于这类。零出现在第几个位置是数据决定的,程序无法提前算出该跑几轮,只能每轮比较一次,命中就停。
写条件控制之前,先回答三个问题。第一,循环体靠什么判断出口,是用 ZF、CX,还是两者组合。第二,指针调整放在比较前还是比较后,这直接决定初值怎么写。第三,如果循环次数多于数据长度,有没有越界风险。手动 SEARCH 循环在 CX 耗尽时会退出,REPNE SCASB 在 CX=0 时也会停止,这两个边界都必须提前想好,而不是跑起来再看。
判断依据同样简单:凡是“找到某个元素就停”的需求,优先条件控制;“从第几个处理到第几个”的需求,优先计数控制。更复杂的程序里两种结构可以分层嵌套,也可以先后衔接,设计题就是先条件后计数的组合。
5.3 重复控制指令 vs 串操作指令:我一般这么比
| 比较点 | 重复控制指令(LOOPZ/LOOPNZ) | 串操作指令加前缀(REPZ/REPNE) |
|---|---|---|
| 控制逻辑 | 循环体里自己写比较和跳转 | 指令本身执行比较与重复 |
| 地址维护 | 自己写 INC 或 ADD | 硬件自动按 DF 调整 |
| 段基址 | 操作数都在 DS 里,段关系简单 | CMPS 固定 DS:[SI] 与 ES:[DI] 配合 |
| 中途插动作 | 可以边循环边记录位置 | 结束后只能拿最终状态 |
| 代码量 | 长,动作透明 | 短,语义集中 |
| 常见错误模式 | 初值写错、循环体误改 CX | ES、DF、停止位置理解错 |
我的选择习惯是这样的:实验阶段、数据量小、想留调试余地,先用 LOOPZ 手动循环,SI、DI 的初值注释写清楚,跑通之后再改成串指令版优化。数据量大、想追求速度的场合,直接 REPZ CMPSB 或 REPNE SCASB,ES 初始化和 CLD 固定写在一起。如果只是判断“两个区域是否相等”,用 REPZ CMPSB;如果要“找到第一个满足条件的元素”,用 REPNE SCASB。
REPZ 和 REPNE 的后缀恰好和停止条件相反,这是新手翻车最多的地方:REPZ 在“等于时继续”,REPNE 在“不等于时继续”。先把这一条写在笔记本上,其他都好办。写答案或者交报告时,如果允许选择,优先串指令版,代码短,边界条件集中在一条指令里,阅卷的人不容易揪出问题;但旁边一定要写一句“已设 ES、已 CLD”,这句话比代码本身更能体现你理解了串操作指令的运行前提。
6. 一个技巧贯穿整个实验:先填“三步量表”再写代码
6.1 三步量表的写法
这个实验做完之后,我把循环程序的写法固定成了一套流程:动手写代码之前,先在纸上填三行。第一行是初值,哪个寄存器放什么、指针指向哪里;第二行是步长,每轮加一还是加二,单位是字节还是字;第三行是出口,哪一个标志位、哪一个地址、停止时指针指向什么位置。
对照这次实验的三个版本,量表长这样:
| 版本 | 初值 | 步长 | 出口条件 | 停止时状态 |
|---|---|---|---|---|
| LOOPZ 手动循环 | SI=OFFSET BUF1-1,DI=OFFSET BUF2-1 | 每轮 SI、DI 各 +1 | ZF=0 或 CX=0 | JZ 判断是哪种出口 |
| REPZ CMPSB | SI=OFFSET BUF1,DI=OFFSET BUF2 | 每轮 SI、DI 各 +1 | ZF=0 或 CX=0 | 全部相等则 FLAG=0 |
| REPNE SCASB | DI=OFFSET BUF,AL=0 | DI 每轮 +1 | 找到 0 时 ZF=1,或 CX=0 | DI 指向零后一个字节 |
这张表的价值在于,三个版本功能彼此等价,但初值、出口、停止位置没有一项是完全相同的。你可以任意抽一行去核对代码:验证题 LOOPZ 版的初值有没有减一,串指令版的 CLD 写没写,搜索段的统计循环用的 CX 是哪个阶段的 CX。全部填完再碰键盘,基本不需要靠断点乱试。
从那以后,我每次写循环程序,都会强迫自己先填这张表。三行注释,几分钟的事,但省下来的调试时间能抵得上半个下午。这份实验报告里我最想留住的不是哪个指令格式,就是这个习惯——它让我后来写串指令、写条件循环,几乎不再犯初值和出口混用的错。希望帮到你。
本文还有配套的精品资源,点击获取