6502仿真实现ADC和SBC的正确方法

时间:2015-03-22 10:33:35

标签: javascript emulation 6502

我一直在为MOS 6502设计模拟器,但我似乎无法让ADC和SBC正常工作。我在模拟内存中加载了AllSuiteA program的0x4000测试我的模拟器,而对于test09,我目前的ADC和SBC实现都没有获得正确的标志。我已经尝试过无数次更改算法,但每次进位标志和溢出标志都足够重要,导致测试分支/不分支。

Both of my functions are based off this.

内存[0x10000]是累加器。它存储在存储器范围之外,因此我可以使用单独的寻址开关语句。

这是我对这些功能的一种实现:

case "ADC":
    var t = memory[0x10000] + memory[address] + getFlag(flag_carry);
    (memory[0x10000] & 0x80) != (t & 0x80) ? setFlag(flag_overflow) : clearFlag(flag_overflow);
    signCalc(memory[0x10000]);
    zeroCalc(t);

    t > 255 ? setFlag(flag_carry) : clearFlag(flag_carry);

    memory[0x10000] = t & 0xFF;
break;

case "SBC":
    var t = memory[0x10000] - memory[address] - (!getFlag(flag_carry));
    (t > 127 || t < -128) ? setFlag(flag_overflow) : clearFlag(flag_overflow);

    t >= 0 ? setFlag(flag_carry) : clearFlag(flag_carry);
    signCalc(t);
    zeroCalc(t);

    memory[0x10000] = t & 0xFF;
break;

此时我已经完成了所有想法,but I did also run into the same problem with the data offered here.所以这不是一个实施计划让我失败了。

3 个答案:

答案 0 :(得分:12)

(我忘记了十进制模式 - 这是NES的6502所缺乏的 - 在写下面的答案时。无论如何我都会离开它,因为它对编写NES模拟器的人来说可能有用。 )

模拟器SBC后,

ADC很容易实现。您需要做的就是反转参数的位并将其传递给ADC实现。为了直观地了解其工作原理,请注意反转arg的所有位会在两个补码中生成-arg - 1,并解决carry标志为SBC时的情况。并且没有设定。

这是我模拟器中static void sbc(uint8_t arg) { adc(~arg); /* -arg - 1 */ } 的完整来源。所有标志也将正确设置。

ADC

实现overflow时最棘手的部分是计算溢出标志。它被设置的条件是结果有错误&#34;标志。由于范围如何计算,事实证明这只能在两种情况下发生:

  1. 添加了两个正数,结果为负数。
  2. 添加了两个负数,结果为正数。
  3. (1)和(2)可以简化为以下条件:

    • 添加了两个具有相同符号的数字,结果的符号不同。

    使用一些XOR技巧,这允许设置ADC标志,如下面的代码(来自我的模拟器的完整static void adc(uint8_t arg) { unsigned const sum = a + arg + carry; carry = sum > 0xFF; // The overflow flag is set when the sign of the addends is the same and // differs from the sign of the sum overflow = ~(a ^ arg) & (a ^ sum) & 0x80; zn = a /* (uint8_t) */ = sum; } 实现):

    (a ^ arg)
    如果0x80寄存器和a符号不同,

    arg会在符号位位置提供~。如果0x80a具有相同的符号,arg会翻转这些位,以便您获得overflow = <'a' and 'arg' have the same sign> & <the sign of 'a' and 'sum' differs> & <extract sign bit> 。在简洁的英语中,可以写出条件

    ADC

    zero实现(以及许多其他指令)也使用技巧将negative和{{1}}标记存储在一起。

    顺便说一句,我的CPU实现(来自NES仿真器)可以找到here。搜索&#34;核心指令逻辑&#34;将为您提供所有指令的简单实现(包括非官方指令)。

    我通过大量的测试ROM运行它没有失败(NES仿真的一个好处就是有很多很棒的测试ROM可用),我认为它应该是非常无bug的此时(除了一些非常模糊的内容,例如在某些情况下涉及开放总线值)。

答案 1 :(得分:10)

欢迎,勇敢的冒险家,到6502的神秘大厅,添加&#39;和&#39;减去&#39;命令!许多人已经走过了这些步骤,尽管很少有人完成了等待你的各种试验。坚强的心!

好的,戏剧结束了。简而言之,ADC和SBC是最难以模拟的6502指令,主要是因为它们具有令人难以置信的复杂和复杂的逻辑小块。它们处理进位,溢出和小数模式,当然实际上依赖于可以被认为是隐藏的&#39;伪寄存器工作存储器。

让事情变得更糟的是,关于这些指令已经写了很多,并且很多文献都有错误。我在2008年解决了这个问题,花了很多时间研究并将小麦从谷壳中分离出来。结果是我在这里重现了一些C#代码:

case 105: // ADC Immediate
_memTemp = _mem[++_PC.Contents];
_TR.Contents = _AC.Contents + _memTemp + _SR[_BIT0_SR_CARRY];
if (_SR[_BIT3_SR_DECIMAL] == 1)
{
  if (((_AC.Contents ^ _memTemp ^ _TR.Contents) & 0x10) == 0x10)
  {
    _TR.Contents += 0x06;
  }
  if ((_TR.Contents & 0xf0) > 0x90)
  {
    _TR.Contents += 0x60;
  }
}
_SR[_BIT6_SR_OVERFLOW] = ((_AC.Contents ^ _TR.Contents) & (_memTemp ^ _TR.Contents) & 0x80) == 0x80 ? 1 : 0;
_SR[_BIT0_SR_CARRY] = (_TR.Contents & 0x100) == 0x100 ? 1 : 0;
_SR[_BIT1_SR_ZERO] = _TR.Contents == 0 ? 1 : 0;
_SR[_BIT7_SR_NEGATIVE] = _TR[_BIT7_SR_NEGATIVE];
_AC.Contents = _TR.Contents & 0xff;
break;

答案 2 :(得分:3)

以下是我的C64,VIC 20和Atari 2600仿真器(http://www.z64k.com)的6502核心代码片段,它实现了十进制模式并通过了所有Lorenzes,bclark和asap测试。如果您需要我解释任何问题,请告诉我。我还有旧代码仍然通过所有测试程序,但将指令和指令的十进制模式分成不同的类。如果你想破译它,可能更容易理解我的旧代码。我的所有仿真器都使用附加的代码来实现ADC,SBC和ARR(不包括ARR的完整代码)指令。

public ALU ADC=new ALU(9,1,-1);
public ALU SBC=new ALU(15,-1,0);
public ALU ARR=new ALU(5,1,-1){
    protected void setSB(){AC.ror.execute();SB=AC.value;}
    protected void fixlo(){SB=(SB&0xf0)|((SB+c0)&0x0f);}
    protected void setVC(){V.set(((AC.value^(SB>>1))&0x20)==0x20);C.set((SB&0x40)==0x40);if((P&8)==8){Dhi(hb);}}
};
public class ALU{
protected final int base,s,m,c0,c1,c2;
protected int lb,hb;
public ALU(int base,int s,int m){this.base=base;this.s=s;this.m=m;c0=6*s;c1=0x10*s;c2=c0<<4;}
public void execute(int c){// c= P&1 for ADC and ARR, c=(~P)&1 for SBC, P=status register
    lb=(AC.value&0x0f)+(((DL.value&0x0f)+c)*s);
    hb=(AC.value&0xf0)+((DL.value&0xf0)*s);
    setSB();
    if(((P&8)==8)&&(lb&0x1f)>base){fixlo();}//((P&8)==8)=Decimal mode
    N.set((SB&0x80)==0x80);
    setVC();
    AC.value=SB&0xff;
}
protected void setSB(){SB=hb+lb;Z.set((SB&0xff)==0);}
protected void fixlo(){SB=(hb+c1)|((SB+c0)&0x0f);}
protected void Dhi(int a){if((a&0x1f0)>base<<4){SB+=c2;C.set(s==1);}}
protected void setVC(){V.set(((AC.value^SB)&(AC.value^DL.value^m)&0x80)==0x80);C.set(SB>=(0x100&m));if((P&8)==8){Dhi(SB);}}

}