marc.hull, on Thu Mar 4, 2010 2:06 PM, said:
When my main loop detected a COINC I deleted one sprite and immediately did another CALL COINC to determine if the earlier collision was with a specific sprite. This would occasionally give a false positive even though there was no possible collision as only one sprite was in the area. This was a problem like I said until I added some instructions between the delay and the second CALL COINC.... I don't know the exact internals but I do suspect that XB was outrunning the console interrupt routine. If is walks like a duck.....
You're assuming only one possible scenario, but I can give you the same "false" positive with the direct access mechanism:
The problem space is:
a. CALL COINC - detects the collision
b. Your code responds by moving one of the sprites
c. CALL COINC - reads the VDP again, still sees a collision
Your assumption is based on this process occurring:
1. VDP interrupt occurs, console copies status register to scratchpad (Clears VDP status)
2. CALL COINC reads from scratch pad, detects collision
3. Your code responds by moving one of the sprites
4. CALL COINC reads from scratch pad again, STILL detects collision
5. VDP Interrupt occurs, console copies status register to scratchpad (Clears VDP status)
... and your goal with the delay fix was to move step 4 to after step 5.
This is what I think happened:
1. CALL COINC reads from VDP, detects collision (clears VDP status)
2. Your code detects the collision...
3. VDP draws the screen again, sees collision, sets status again
4. ...your code moves one of the sprites - status bit stays set until it's read!
5. CALL COINC reads from VDP, detects collision again
6. VDP interrupt occurs, console copies status register to scratchpad (clears VDP status if it's still set)
And your fix, adding a delay, simply moves step 5 after step 6, similar to the other scenario.
Remember that unlike in today's emulators, the VDP sets the status register the instant it detects a collision, not at the bottom of the frame (which is when Win994A and MESS set it), and Classic99's setting of that bit is completely detached from real time right now, even though it can do it separate from the frame. So if it was in emulation you saw it, I wouldn't count it. Also remember that Extended BASIC can keep interrupts disabled for longer than 1/60th of a second -- if interrupts don't run, then the interrupt routine can't clear the VDP status bit. (Of course, this same argument /also/ works for your theory, too, since it shows why XB can appear to outrun the console interrupt. It's not that XB is that fast, it's that it blocks it.) Finally, the collision bit remains set until it's read, no matter how many times the screen has been drawn and regardless as to whether the sprites are still colliding.
I thought Ben had previously mentioned that he'd had CALL COINC miss obvious collisions, and that I'd reproduced that. That supports the theory of it racing with the console interrupt routine. I tried this short piece of test code just now:
10 CALL CHAR(42,RPT$("F",16))
20 CALL SPRITE(#1,42,2,100,100)
30 CALL SPRITE(#2,42,4,104,104)
40 CALL COINC(ALL,C)
50 PRINT C
60 GOTO 40
The sprites are /always/ touching so C should always return a collision, but in fact it frequently returns no collision. If you change it to a CALL PEEK() of the location that the register is copied to: CALL PEEK(-31877,C) -- this alternates between 160 and 128. 128 is the interrupt bit, and we would expect it to always be set (and it is). The other bit is value 32, which is the sprite collision bit. Bizarrely, it's not always set either in Classic99, meaning we're either racing the chip again (but we are in vblank when it is copied, so we shouldn't be), or something else is ALSO reading the register. I'm inclined to be suspicious of those results, though, and will try them on the real machine (unless someone beats me to it.)
I'll work out the right answer when I get some time at home, but it's not so cut and dried.. you can have those symptoms either way. I think we should find out.