stella
stella copied to clipboard
DPC+/CDFJ programming quirks (caused by 6507 silent fetches) not emulated in Stella
I've attached a zip file containing the CDFJ sources for a music-playing example. It also contains two binaries:
- cdf1_music_OK.bin - plays fine on both Stella and on a Harmony cart + real '2600
- cdf1_music_NOK.bin - plays fine on Stella, but crashes on a Harmony cart after a few seconds (please ignore the fact that both ROMs use 263 scanlines, as that is not related to the issue)
It seems that if I use an instruction to read the 6507 stack right before doing a lda #AMPLITUDE
, this eventually crashes the CDFJ rom execution. Actually this is a problem for all CDFJ 'fast fetch' instructions, so not only for fetching the music amplitude.
Note that lda #AMPLITUDE
is a way in CDFJ to get the sound amplitude used for digitized sound playback.
I contacted cd-w about this, and he explained that the problem is that PLP is one of the 6502 instructions which actually silently fetches the following LDA instruction, causing the amplitude fetch to be triggered.
PLA, PLP
# address R/W description
--- ------- --- -----------------------------------------------
1 PC R fetch opcode, increment PC
2 PC R read next instruction byte (and throw it away)
3 $0100,S R increment S
4 $0100,S R pull register from stack
The fetch on cycle 2 is the problem. CDFJ can't distinguish between this silent fetch and the real fetch, causing the crash that you see. Stella simply ignores the silent fetch, which is why the issue is not triggered in emulation.
Note that the extra request of the amplitude itself ( lda #AMPLITUDE
) should not be a problem. However, what cd-w thinks is happening is that the amplitude value is placed on the bus by CDFJ at the same time that the 6502 is updating the stack pointer (in cycle 3), causing the stack pointer to become corrupted, eventually causing the crash. He thinks it may also be that the amplitude value stays on the bus and is used as the next 6502 instruction. The ARM is asynchronous to the 6502, so we would need to use a logic analyzer to work out exactly what is happening ...
The CDFJ-team has previously discussed ways to address this in CDFJ, but the fix would be complex. They think it might be better to extend DASM with warnings for this scenario (there are lots of instructions that can cause this to be triggered).
I think it would be great if Stella would also emulate silent fetches (if possible), so I would have encountered this issue already during development using Stella, and not when playing the ROM on a Harmony cart and a real '2600.
Actually there are many opcodes which do such dummy fetches, including nop
. So I suppose the stack pointer plays a major role here. What happens if you reverse php
and plp
?
Since the CartCDF
class has been programmed by @SpiceWare and @chrisdwalton, I hope they find the time to have a look.
All my 2600 projects are on hold for the time being, though I do recall there's a check in the PEEK implementation to make sure the LDA # override only occurs when the operand's address peeked.
uInt8 CartridgeCDF::peek(uInt16 address)
{
...
if(FAST_FETCH_ON
&& myLDAimmediateOperandAddress == address
&& peekvalue <= myAmplitudeStream)
Looking a the DPC+ implementation I see this:
// Check if we're in Fast Fetch mode and the prior byte was an A9 (LDA #value)
if(myFastFetch && myLDAimmediate)
{
if(peekvalue < 0x0028)
// if #value is a read-register then we want to use that as the address
address = peekvalue;
}
So I suggest changing the CDF implementation to this
if(FAST_FETCH_ON && peekvalue <= myAmplitudeStream)
Edit: don't know what's up with the code blocks. They look correct in edit mode, but not when posted.
Fixed the code blocks for you. 😄
Unfortunately the fix you suggested causes an immediate crash in both ROMs. Also, how should removing a check (myLDAimmediateOperandAddress == address
) improve the emulation?
Thanks! I seem to recall you had to fix them last time I posted code. I clicked the <> button, a pair of single quotes were output with the cursor ending up between them, I then pasted the code block. Hmm, maybe the sequence should be reversed...
uInt8 CartridgeCDF::peek(uInt16 address)
{
...
if(FAST_FETCH_ON
&& myLDAimmediateOperandAddress == address
&& peekvalue <= myAmplitudeStream
Aha, that's it! Paste first, select the code, then click <>. That results in three single quotes on either side of the code block, not one single quote on either side.
That's a bummer. DPC+ and CDFJ both support the LDA # Fast Fetch feature, so I would have expected them both work the same. Since DPC+ doesn't have that check I figured that'd be the first thing to try. Perhaps DPC+ is doing that check a different way, but figuring that out will be more involved than I have time for at the moment. I'll take a look in a month or two if nobody's figured out before then.
I've tentatively assigned @SpiceWare to this, since I don't think anyone else is really familiar with that code right now.
Pinging @SpiceWare, to see if there's any progress on this.
Sorry, RL has kept me away from Atari projects for so long I'd forgotten about this and #707
Looks like I was further along with the BUS demos, so I'll try to resume work on those this weekend.