[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: ADC & SBC OpCodes Questions



David Empson wrote:

> Bryan Parkoff <BParkoff@satx.rr.com> wrote:
> 
> > ADC & SBC OpCodes Questions

> >     I understand why each emulator projects are required to write several
> > lines inside one function to emulate ADC and SBC opcodes properly through
> > C++.

Or C, or Modula, or PostScript, or pretty much any other virtual
machine that isn't a 6502.

I suppose Intercal would need even /more/ lines to code an ADC, since
it has no addition operator.  I remember now why I rejected that
choice of implementation language.  ;)

> > Please explain again one more time why it is necessary.  The simple
> > function is to accumulate one byte before adds or subtracts second byte into
> > final one byte.  For example, it should look like
> >
> > unsigned char A = 0x40;
> > unsigned char B = 0x4C;
> > unsigned char C = A + B;
> >
> >     Final one byte -- C should always displays 0x8C.

David Empson answered this very well.  I'll just add a few
observations that come from writing my own 8-bit Apple II emulator ...
in C++, as a matter of fact.

> That is correct, assuming:
> 
> 1. The carry flag was clear before the ADC instruction.  (If the carry
> flag is set, it is necessary to add 1 more.)
> 
> 2. The decimal mode flag is clear.

Decimal ADC and SBC are ugly to emulate, requiring an algorithm based
on partial sums of each nybble pair.  The algorithm can be streamlined
by using lookup tables to condense the pairs of nybble sums back to a
single number (or certainly other techniques are possible), but in any
case, decimal mode greatly complicates the discussion.

So for the rest of this article, I'll assume we're talking about
/binary/ ADC and SBC, which are much friendlier operations.

> 3. [...] There would also need to be code to derive the appropriate values for
> the N, V, Z and C flags after the ADC or SBC.

More generally, you can think of ADC as a function taking 18 bits of
input (the 8 bit addor, the 8 bit addend, and the states of the C and
D flags) and yielding 12 bits of output (the 8 bit sum, and the four
flags just mentioned).  SBC behaves similarly, though with different
vocabulary among other things: "subtractor", "subtractend", and of
course "difference" instead of "sum".

In ADC, the output N, Z, and C states can be derived easily from the
9-bit sum.  That's /nine/ bits, not eight.  These new flag states are:

  * C = bit #8 of the sum.
  * N = bit #7 of the sum.
  * Z answers the question, "Are the sum's lower 8 bits all zero?",
    with 0 and 1 meaning no and yes respectively.

The output V flag is more complicated.  It is a function of all three
numbers: addor, addend, and sum.  Or at least in C++ it becomes such a
function, because the addition's carry-out bits from columns 6 and 7
-- the bits the 6502 would really use -- are not available to you, the
C++ programmer.  

Therefore, you have to compute or "reverse-engineer" these carry-out
bits from the sum and two input numbers.  This can be done by
exclusive-oring and shifting, and probably other trickery as well.

All this applies to ADC, but SBC is almost the same, about which more
in a moment...

> SBC may also require some extra steps to deal with the inverted carry
> flag.  (I think the real 6502 does something like complement one of the
> inputs, do an ADC in the ALU, and then adjust the result.  This reduced
> the number of gates required in the ALU, as it only has to implement an
> add operation, not a subtract.)

Actually it's even simpler than that.  Binary SBC can be implemented
on top of binary ADC by simply one's-complementing the data byte
beforehand.  That's the input byte that /isn't/ the accumulator.  No
epilog computation is necessary, and everything else is identical.  To wit:

  case DoBinarySBC:  data = data ^ 0xFF;
                     // fall into ADC
  case DoBinaryADC:  nine_bit_sum = A + data + C;
                     // also compute new N, Z, C, and V
                     break; // done

In particular, you /don't/ need to change or inspect the the carry flag.

Thus, I would guess that a 6502's wiring of SBC simply feeds all 8
bits of the data byte through NOT gates before they get to the adder
unit.  But I'm not a chip designer, so that's just speculation.

> > Why ADC and SBC functions have to separate byte into two nibbles before they
> > can do operations.
> 
> If the CPU is operating in binary mode, I can't see any reason for doing
> that.

Agreed.  In binary mode there's no need to separate the nybbles, for
either operation.

> If the CPU is operating in decimal mode, then it is probably
> easier to add the two digits separately and then calculate the correct
> result from the two partial results.

In fact, I don't see any way around this -- which doesn't mean there
isn't one of course.  My own experimentation showed that there is no
way to derive the correct decimal-mode result, all 12 bits of it, from
the binary-mode result.  There is no lookup table that will work, for
example.  (I suspected this anyway before I began testing, but hey, it
was worth a shot.)

-- Colin K.