学8086/8088微机原理FLAGS寄存器是绕不开的一道坎。它不像AX、BX那样可以直接搬数也不像IP那样决定下一条指令的地址却像一块贴在运算单元边上的记分牌每次加法、减法、移位、逻辑运算跑完处理器自动把结果的各种特征记在FLAGS里。你要做的是读懂这些记录让程序据此决定下一步往哪走。这个16位的状态标志寄存器考试爱考、调试靠它、条件跳转也全靠它值得花一整篇把它彻底讲透。1. FLAGS寄存器整体认知一张16位的“运算成绩单”1.1 哪些位是有效的九位高低分布8086/8088的FLAGS是一个16位寄存器但真正定义使用的只有9位。其他位没有定义读出来无意义写进去也不起作用属于“保留区”。有效位的分布如下位序标志名称英文缩写类别置1的含义bit0进位标志CF状态标志无符号运算最高位有进位/借位bit2奇偶标志PF状态标志结果低8位中1的个数为偶数bit4辅助进位标志AF状态标志低4位向高4位有进位/借位bit6零标志ZF状态标志运算结果为零bit7符号标志SF状态标志运算结果最高位为1即负数bit8陷阱标志TF控制标志进入单步调试模式bit9中断允许标志IF控制标志允许响应可屏蔽中断bit10方向标志DF控制标志串操作指令地址递减bit11溢出标志OF状态标志有符号运算结果超出表示范围注意看这张表bit1、bit3、bit5、bit12到bit15都是空的。很多同学画寄存器图时把16位全部画满那是不对的。8088和8086在这点上完全一致虽然8088的外部数据总线是8位内部FLAGS结构没有差别。1.2 状态标志与控制标志两类完全不同的“灯”这9个标志内部其实分两拨。状态标志CF、PF、AF、ZF、SF、OF是“结果指示灯”由运算指令自动点亮或熄灭记录的是刚才这次运算结果的数学特征。控制标志TF、IF、DF是“模式开关”它们不由加减运算决定而是由程序员用专门指令去开或关控制CPU的执行模式。把这个区分想清楚很多困惑就解开了。比如你执行MOV AX, 5FLAGS纹丝不动因为传送指令不做运算执行CMP AL, BL虽然没有保存差值的寄存器但六个状态标志已经把比较结果全部记录在案。这就是状态标志的本质——运算特征的影子。1.3 硬件自动写入标志位不是程序员手动算的有个新手容易犯迷糊这些标志是谁写的答案是执行单元的硬件电路。比如做加法时最高位加法器的进位输出会直接触发CF触发器两个符号位经过异或逻辑就得到了OF。程序员不能直接赋值给CF但可以通过分支指令去读它们或者用后面要讲到的标志操作指令去改个别位。我见过有人写程序试图用MOV指令去修改FLAGS这是完全行不通的。对FLAGS的读写有一套专用通道LAHF/SAHF处理低字节PUSHF/POPF处理整个寄存器。这些指令在后面的3.4小节会专门展开。2. 六个状态标志逐个拆解从含义到实例2.1 CF进位标志无符号数世界的“进位灯”CF是最直观的一个标志。做加法时最高位bit7或bit15向外产生了进位CF置1做减法时最高位发生了借位CF也置1。换句话说CF记录的是无符号数运算是否超出表示范围。看个经典例子MOV AL, 0FFH ADD AL, 01H0FFH加1结果应该是100H但AL只有8位低8位变00H那个“1”跑到哪里去了跑到CF里。所以执行完后AL00HCF1ZF1。255加1超出了无符号8位数能表示的最大值255CF就是无符号数世界里的“溢出报警灯”。CF最实用的场景是多字节加法。8086加法指令只做16位加16位要做32位加法怎么办靠ADC带进位加; 假设DX:AX 加上 CX:BX结果存回 DX:AX ADD AX, BX ; 先加低16位 ADC DX, CX ; 再加高16位同时把刚才低16位的进位加进来第二句的ADC会把CF自动加到DX上。如果低16位加法产生了进位那高16位必须多算1。这就是CF的连锁反应价值——没有它多字节运算根本做不干净。2.2 OF溢出标志有符号数的“临界警报”OF看的是有符号数。两个有符号数相加或相减后结果超出了当前位数能表示的有符号数范围OF就置1。8位有符号数范围是-128到127。比如MOV AL, 7FH ; 127 ADD AL, 01H ; 1结果变成10000000B7FH加1等于80H。如果按无符号数理解128没超范围CF0但按有符号数理解1271128超过127产生溢出OF1。这就是同一个运算结果在两种解读下的差异。OF的硬件判断规则不是“最高位有没有进位”而是“两个同符号数相加后结果符号是否改变”。用二进制看正数加正数应该还是正数结果最高位如果变1成了负数那肯定溢出了反过来两个负数相加变成正数同样溢出。电路实现上OF等于最高位进位与次高位进位的异或。2.3 CF与OF的核心区别一张对照表很多题目就是拿CF和OF做文章因为两者太容易被搞混。我平时给学生讲就强调一句话CF是为无符号数服务的OF是为有符号数服务的一次运算两个灯可能同时亮、可能只亮一个、也可能都不亮。对比点CFOF服务对象无符号数有符号数补码加法置位条件最高位产生进位两个同号数相加结果符号翻转减法置位条件最高位产生借位结果超出有符号范围经典例子0FFH 01H → CF17FH 01H → OF1指令助记符分支JC / JNCJO / JNO看个同时置位的例子0FFH加80H。无符号看255128383溢出CF1有符号看-1加-128等于-129也超出-128下限OF1。这种CF和OF同时为1的情况恰恰说明一个二进制运算可以同时携带两种维度的信息。程序里要根据把操作数当成有符号还是无符号去选择查看哪个标志。2.4 ZF零标志结果清零了吗ZF最简单运算结果为0就置1。注意几个坑一是它只管结果不管过程中有没有中间进位。比如0FFH加01H结果是00HZF1可CF也是1别觉得“结果是零那就没进位”。二是CMP指令经常靠ZF判断相等因为相减结果为零意味着两数相等。MOV AL, 05H CMP AL, 05H ; 5-50ZF12.5 SF符号标志最高位的“投影像”SF直接等于运算结果的最高位。最高位是1SF1最高位是0SF0。有符号数解读下SF表示结果为负。它是ALU把结果的最高位复制了一份放进FLAGS所以叫“符号标志”名副其实。但SF要与OF配合使用才能正确判断有符号数大小关系单独看SF有时候会误判。这个在4.3小节有专门例子。2.6 PF奇偶标志串行通信时代留下的遗产PF统计结果低8位里1的个数偶数个1时置1。注意它只统计低8位16位结果只关心低字节。这个标志是上世纪串行通信做奇偶校验留下的设计现在用得少但考试爱考。这里有个高频误区PF不是说“结果这个数是偶数”。看例子MOV AL, 02H02H的二进制是00000010B这个数值本身是偶数但里面只有1个1奇数个所以PF0。PF判断的是“二进制位中1的个数”的奇偶不是数值的奇偶。再比如0FFH8个1PF1。拿数值奇偶去套PF十有八九要出错。2.7 AF辅助进位标志BCD运算的专属搭档AF检查低4位bit3向高4位bit4有没有进位或借位。8位无符号数的低4位对应一个BCD十进制位所以DAA、DAS这类BCD调整指令会专门读AF。MOV AL, 09H ADD AL, 08H ; 09H08H低四位9817产生进位AF1AF平时用得少但强烈建议在调试器里观察一次加法和减法后的AF变化因为这个位不像CF、ZF那么直观不做实验容易凭空想象错。3. 三个控制标志与标志位操作指令3.1 TF陷阱标志单步调试的开关TF置1后CPU执行完每一条指令都会自动产生一次类型1内部中断相当于每走一步都停下来让你检查现场。这就是当年调试器的单步功能。8086没有专用的“置TF”或“清TF”指令需要借助PUSHF/POPF曲线救国PUSHF ; 把FLAGS压栈 POP AX ; 弹到AX OR AX, 0100H ; 把bit8TF置1 PUSH AX POPF ; 写回FLAGS现在的集成开发环境调试器内部也是类似原理。这个知识考试可能出选择题理解机制比背指令更管用。3.2 IF中断允许标志系统级中断的闸门IF控制CPU是否响应可屏蔽中断INTR引脚来的中断请求。IF1允许响应IF0屏蔽。用STI置1CLI清零。需要注意的是不可屏蔽中断NMI和内部中断除零、单步等不受IF管制。IF只针对可屏蔽中断这一路。写中断服务程序时进入后通常先CLI防止打断现场保护退出前STI恢复这是标准套路。3.3 DF方向标志串操作指令的指针走向DF决定串操作指令MOVS、CMPS、SCAS、LODS、STOS处理字符串时地址的走向。DF0SI和DI自动递增DF1自动递减。CLD清DFSTD置DF。CLD MOV SI, OFFSET src LEA DI, [dst] MOV CX, 100 REP MOVSB ; 从低地址向高地址复制100字节如果先STD再执行那同样代码就是往低地址方向复制。方向错了可能导致覆盖或越界这是串操作最常见的运行时事故。3.4 标志位操作指令速查表指令功能影响的标志CLC清CFCF0STC置CFCF1CMC取反CFCF翻转CLI清IFIF0STI置IFIF1CLD清DFDF0STD置DFDF1LAHF将FLAGS低字节装入AH不改变标志SAHF将AH写回FLAGS低字节SF、ZF、AF、PF、CF被修改PUSHFFLAGS整体压栈不改变标志POPF弹栈写入FLAGS全部可能被修改LAHF和SAHF的位对应关系是考点AH的bit7对应SF、bit6对应ZF、bit4对应AF、bit2对应PF、bit0对应CF。也就是说AH并不是简单把FLAGS低8位照搬中间有几个保留位是空的。你写程序时绝不能假设AH里那些保留位是什么值。4. 标志位如何驱动条件转移程序控制的核心4.1 条件转移指令与标志位的对应关系标志位最大的用途是喂给条件转移指令。8086的条件转移指令会检查一个或多个标志满足条件就跳转。下面是常见的对应关系标志条件指令助记符跳转条件ZFJZ / JEZF1结果为零或相等ZFJNZ / JNEZF0结果非零或不相等CFJC / JBCF1有进位/借位CFJNCCF0SFJSSF1结果为负SFJNSSF0OFJOOF1有溢出OFJNOOF0PFJP / JPEPF11的个数为偶PFJNP / JPOPF0请注意8086条件转移指令的跳转范围只有-128到127字节这是经典考点。超出范围要用反逻辑跳转加无条件跳转绕过去或者改用386以后的32位条件跳转后者不在8086范围讨论内。4.2 无符号数比较的完整推理无符号数比大小看CF和ZF的组合就行。设比较指令CMP AL, BL内部做的是AL减BL相等ZF1AL BL无借位且不相等即CF0且ZF0AL BL有借位CF1AL ≥ BLCF0无借位包括相等AL ≤ BLCF1或ZF1对应的指令是JA/JB/JAE/JBE一族。这一族专门服务无符号比较语义上是above/below而不是greater/less。写程序时别混用。4.3 有符号数比较的坑与解法有符号数比较不能用CF因为CF记的是无符号借位。比如-1减-2从无符号角度看是255减254够减CF0从有符号角度看-1-(-2)1结果没问题但这不能说明大小关系。有符号比较的正确姿势是看SF和OF的关系两数相减没溢出时结果符号SF直接说明谁大谁小一旦溢出SF可能反过来。统一的判断是AL BLZF0 且 SFOFAL BLSF≠OFAL ≥ BLSFOFAL ≤ BLZF1 或 SF≠OF硬件上CPU内部就是算SF⊕OF的结果来做JG/JL的判断不需要你手工算。但笔试会让你手推标志位结果这个逻辑必须掌握。举一个容易误判的例子MOV AL, 80H ; -128 CMP AL, 01H ; 比较-128和180H减01H等于7FH差值为正SF0。可实际上-128小于1。问题出在哪-128-1-129超出了8位有符号数范围OF1。此时SF0、OF1SF≠OF判断为“小于”完全正确。如果只盯着SF看就会得出“结果正数所以前者大”的错误结论。5. 实战在调试器里观察FLAGS变化5.1 DEBUG的-R命令标志缩写总表老牌DEBUG工具的-R命令显示寄存器时FLAGS会用一组双字母缩写表示考试偶尔也考。常见对应如下显示含义显示含义OVOF1有溢出NVOF0无溢出DNDF1递减UPDF0递增EIIF1允许中断DIIF0禁止中断NGSF1负PLSF0正ZRZF1零NZZF0非零ACAF1有辅助进位NAAF0无辅助进位PEPF1偶校验POPF0奇校验CYCF1有进位NCCF0无进位显示顺序固定为OV NV / DN UP / EI DI / NG ZR / AC NA / PE PO / CY NC。看到一行类似“NV UP DI PL ZR NA PO NC”的字符串要能马上翻译出无溢出、方向递增、中断关、结果正、结果为零、无辅助进位、奇数个1、无进位。5.2 一次加法指令的标志位“现场记录”拿最综合的一道经典题演练一遍。假设执行前AL3FH执行ADD AL, 41H3FH是00111111B41H是01000001B加完得80H也就是10000000B。逐个分析CF最高位没有进位CF0OF两个正数相加结果符号变负OF1SF结果最高位是1SF1ZF结果不是0ZF0PF80H中只有1个1奇数个PF0AF低4位F加1等于10H产生进位AF1这个题几乎把六个状态标志考全了。做题顺序建议是先算二进制结果再看最高位定CF看符号翻转定OF再顺次填写其余标志。别跳着猜容易漏。5.3 用标志位写一个8位有符号比较子程序理论落到代码上。写一个子程序比较AL和BL的有符号大小ALBL时CL返回1否则返回0; 入口AL、BL均视为有符号8位数 ; 出口CL1表示ALBLCL0表示ALBL CMP AL, BL JG greater ; 有符号大于判断 MOV CL, 00H RET greater: MOV CL, 01H RET这里JG内部自动处理了ZF和SF⊕OF的逻辑。如果换成无符号数比较就得把JG改成JA。两种看似相近的指令实际服务完全不同的数值解释这就是为什么“读懂FLAGS”是编写正确分支逻辑的前提。6. 常见误区与考试/面试高频考点6.1 七个高频误区一览我这些年批改作业和答疑发现以下问题反复出现误区正确理解把CF当成有符号溢出CF是无符号溢出/借位OF才管有符号溢出用INC/DEC后看CF判断进位8086设计INC/DEC不影响CF循环计数时不会破坏多字节加法链以为所有指令都影响标志MOV、PUSH、POP、LEA、JMP等不影响标志逻辑运算会把CF和OF清零用SF单独判断有符号大小必须结合OF判断公式是SF⊕OF把PF理解成“数值奇偶”PF统计低8位中1的个数02H是偶数但PF0CMP和SUB搞混CMP和SUB对标志的影响相同但CMP不保存差值只留标志忘记逻辑运算清零CF/OFAND、OR、XOR、TEST都会把CF和OF置0这在多字节程序里能当技巧用关于“INC/DEC不影响CF”多说一句这个设计是刻意的。INC常用于循环指针自增如果它随意改动CF循环体里原本串起来的多字节进位链就会被破坏。理解这个设计意图比死记结论印象深刻得多。6.2 变式例题随便来两道题一AX0FFH执行INC AX后列出AX和标志变化。答案要点AX变成0100H。CF不变因为INC不碰CF前提是执行前CF是多少就是多少可以是0也可以是1。SF0结果最高位为0ZF0结果非0PF10100H低8位为00H0个1是偶数AF1低四位F加1进位。题二AL80H执行SUB AL, 01H分析CF和OF。80H减01H得7FH。无符号看128-1127够减CF0有符号看-128-1-129溢出OF1。这又是CF和OF分道扬镳的典型。注意结果7FH的符号位是0SF0但因为OF1所以有符号判断时结果是错的不能以此认定-128大于1。6.3 给学习者的最后提醒个人经验之谈学FLAGS寄存器光看教材表格是不够的一定要在调试器里做实验。把DEBUG的-R那行缩写当成通关密码每次执行完一条运算指令都回看一眼CY、ZR、NG这些标记怎么变。踩过几次“以为跳转没问题结果走错分支”的坑之后你会发现这些标志位就像骑自行车——刚开始要刻意记骑顺了就变成条件反射了。8086/8088的FLAGS是理解整个指令系统的一面镜子。它记录的不是数据本身而是数据的“味道”是正是负、是零是非零、进位没进位、溢出没溢出。把这张记分牌读懂了后面学汇编程序设计、接口技术、中断系统都会顺很多。接下来可以接着研究8255A、8253这类接口芯片但在此之前先把这九个标志和它们的指令搭档练到滚瓜烂熟。