写工程日记有很多好处,一个额外的好处是你能时不时想起多年以前你在做什么,会想到那些人,那些项目,以及那些糟糕的衣服和发型。
最近在写nes模拟器,其中SBC减法指令的模拟给我造成了不小的麻烦。尤其是减法计算对Pstatus寄存器中O和C位的影响。本文是如何模拟加法(adc)指令和减法(sbc)指令的总结。
众所周知,6502 CPU有一个Pstatus寄存器,简写为P。该寄存器大小只有单个字节,8位。
首先,这8位代表的含义分别如下:
位数 | 标志 | 名称 | 作用 |
---|---|---|---|
第7位 | N | negative | 正负 |
第6位 | V | overflow | 借位/进位 |
第5、4位 | B | break | 用于指示当前是否在处理中断以及是何种中断(php, brk或是nmi, irq) |
第3位 | D | decimal | 置位后,算术操作为BCD09+01=10 ,清除则二为补码 |
第2位 | I | 禁中断interrupt disable | 置位,则禁中断 |
第1位 | Z | zero | 结果为0 |
第0位 | C | carry | 进位 |
接着说一下模拟加法指令和减法指令时要如何处理这两位。
计算Carry位
处理Carry位时,要牢记下面几点:
- 硬件没有减法,只有加法
- 硬件没有负数,如果是减法则用2的补码进行加法运算
- carry位只有一个含义即加法运算最高位是否会溢出
因此
- 对于加法指令ADC,相对简单,只用比较结果是否超过255(UByte/U8的最大值)即可。
- 对于减法指令SBC,4-3其实是4+0b1111_1101会溢出,而减法中的溢出表示的是没有borrow。而3-4其实是3+0b1111_1100,蛤不会溢出。所以减法计算是否减一的判断依据是C位是否为0,若为0则减一,反之不减。所以和加法相同,只用比较结果是否超过255即可。需要注意的是:Carry in的减一至少是可以用补码的加一抵消的,不需要对这个1用补码计算!
计算Overflow位
对于Overflow位的计算在第一篇参考的文章中通过枚举给出了计算公式,但是这个计算公式是针对加法的。公式如下:
- 加法的两个操作数符号相同
- 结果和任一操作数的符号不同
但是对于减法:
- 减法的两个操作数符号不同。但是计算结果时要将减数按位取反末尾加一(如果C位为0,正好抵消)
- 结果和第一个操作数不同
注:无论是加法还是减法考虑第一点的时候我们不用考虑C位(即使第一点只是一个必要条件),C位的影响已经体现在2中了(加上第二点就是充要条件了)。
实现
1 |
|