Date : Thu, 22 Jul 2010 18:27:29 +0200
From : rick@... (Rick Murray)
Subject: Spitting expletives
OMG.
Well... THAT has been an odyssey into the unknown.
"What?", you might ask (if you care... ;-) ).
Simple. An accurate emulation of the SBC instruction.
For the non-microcode bunch-of-transistors that is the 6502, the
behaviour of the flags in SBC is actually fairly complicated.
I have finally (hopefully correctly!) arrived at:
--8<--------
Case &HE1, &HE5, &HE9, &HED, &HF1, &HF2, &HF5, &HF9, &HFD:
' SBC SuBtract memory with acc with Carry
' A,fC = A - mem - (!fC)
' Affects CZVN
If (CPU.PS.D = False) Then
' Calculate to a temporary place
CPU.Addr = CPU.A - CPU.Temp - _
(IIf((CPU.PS.C = True), 0&, 1&))
' NOTE that carry set = no subtract, else -1
If ((CPU.Addr) < 0) Then
' less than zero? flag a carry has occurred, then
' correct the result to fit into a byte
SBCCarry = True
CPU.Addr = 256 + CPU.Addr ' casting is 'painful' in VB
Else
SBCCarry = False
End If
' Carry is CLEAR if a borrow occurred, love the 6502! :-)
CPU.PS.C = SBCCarry Xor True
' Overflow?
If ((CPU.A And &H80) <> (CPU.Addr And &H80)) Then
' bit 7 is different between orig/final so sign has changed...
CPU.PS.V = True
' ...except when the result is less than zero (a borrow)...
If (SBCCarry = True) Then CPU.PS.V = False
Else
CPU.PS.V = False
End If
' Assign the result
CPU.A = CByte(CPU.Addr And 255&)
' And the last two...
CPU.PS.N = (CPU.A > 127)
CPU.PS.Z = (CPU.A = 0)
' Else
' ...no support for BCD maths mode...
End If
--8<--------
BTW, you can, no doubt, optimise this a *lot*, but I am coding for
(extreme) clarity, not to be clever. [*]
To give an example, the above in B-Em is:
--8<--------
tempw=a-(temp+(p.c?0:1)); \
tempv=(short)a-(short)(temp+(p.c?0:1)); \
p.v=((a^(temp+(p.c?0:1)))&(a^(unsigned char)tempv)&0x80); \
p.c=tempv>=0;\
a=tempw&0xFF; \
setzn(a); \
--8<--------
Which is small, tight, and implemented as a macro (!). It is also a PITA
to take apart and relate to other things to figure out what exactly is
going on, so I ignored it and wrote my own.
But... hey... It's pretty complex, this SBC instruction. For instance:
--8<--------
10DIM CODE% 256
15P% = CODE%
20[
25 OPT 0
30 LDA #0
35 SEC
40 SBC #1
45 RTS
50]
55R = USR(CODE%)
60PRINT ~R
>RUN
197F
B00000FF
>
--8<--------
All well and proper, until you look at the flags:
C = Unset (a borrow occurred)
N = Set as 255 is a signed negative
V = Unset - the sign of the result is not wrong?
0 (b7 clear) -> 255 (b7 set) is not a sign change!?
Z = Unset, ain't zero!
Oh, and 'B' is returned as set. Is this a bug in BeebEm, or does BASIC
do something pretty weird when returning? I was under the impression
that the BRK flag only really existed in the value pushed to the stack.
Well... In various tests, my emulator appears to match BeebEm and 6502
Simulator. *Finally*.
Best wishes,
Rick.
* - I am thinking of adding support for Rob's TCP/IP<>Econet extensions
into the ADLC module, so theoretically my emulator will, eventually, be
able to function as a working server emulator. However it was never
intended to work in that way, per se (there are enough competent Beeb
emulators around already); it was specifically written to permit
revisions to the firmware (cf. mass storage on SD cards, etc) to be
prototyped virtually without the grief of
erasing/blowing/fitting/testing EPROMs and a real FileStore. And, let's
face it, also because I felt like writing an emulator in VisualBasic
because it was a pretty insane thing to do. ;-) But, hey, when it is all
working, at least it will *feel* like using a real FileStore (ie, quick
as treacle). Dah-duh, authenticity guaranteed! :-) :-) :-)
--
Rick Murray, eeePC901 & ADSL WiFI'd into it, all ETLAs!
BBC B: DNFS, 2 x 5.25" floppies, EPROM prog, Acorn TTX
E01S FileStore, A3000/A5000/RiscPC/various PCs/blahblah...