Hi
I get Error #4 (Pattern B--B- (one even, one odd) should skip 1 clock)
I can only about two things that it may depend on
1. What iam doing is in wrong order...
This is what my emulate-sequence look like:
1. Call NMI (if enabled)
2. Run 6820 PPU cycles (I don't have a CPU cycle counter, only PPU)
3. Clear Status flags (vbl, spritehit, overflow)
4. Add one cycle to the PPU clock if its an odd frame and BG enabled
5. Set up sprites for the frame
6. Run up to 89342 cycles
2. if the cycle counter overflow I call the instruction anyway, is it important to first find out how many cycles the instruction will take, then call it if its enough time left?
What happens if a 6 CPU cycle instruction appears at 89340, is it executed after NMI? In order to pass the timing tests is this important?
thanks
looks like you got the idea. you might have to explain more on how your ppu work, and how the cpu is run.
matt
Well.. What I need to know is:
1. is NMI called in the beginning of a frame or at the end.
2. is all instructions performed at its very last cycle, and like my earlier example, if half an instruction is executed before NMI is its "action" made directly after NMI in this case? is this true for all opcodes, CLI is different huh? but what about the others...
Since I always let the instruction to be executed even if it should be right after NMI, may this cause errors in timing?
n6 wrote:
1. is NMI called in the beginning of a frame or at the end.
the term frame is kinda misleading. what do you mean by frame? i assume its the 20 scanlines of vblank, 1 pre line, 240 drawing lines, and 1 post line. nmi would happen inbetween the last cycle of the post line and the first cycle of vblank. so the answer is yes to your OR question. it happens at the begining and at the end because they are the same.
n6 wrote:
2. is all instructions performed at its very last cycle, and like my earlier example, if half an instruction is executed before NMI is its "action" made directly after NMI in this case? is this true for all opcodes, CLI is different huh? but what about the others...
i dont understand what you are asking. well, maybe... blargg just posted something about this, when the cpu checks the nmi or irq, might look at recent post
matt
Quote:
the term frame is kinda misleading. what do you mean by frame? i assume its the 20 scanlines of vblank, 1 pre line, 240 drawing lines, and 1 post line. nmi would happen inbetween the last cycle of the post line and the first cycle of vblank. so the answer is yes to your OR question. it happens at the begining and at the end because they are the same.
yeah thats what I wondered, thanks!
Quote:
i dont understand what you are asking. well, maybe... blargg just posted something about this, when the cpu checks the nmi or irq, might look at recent post
some psuedo code might help.
My way:
do
{
RunNextOp();
PPUClock += OpCycles();
} while (PPUClock < 89xxx)
probably the right way:
while (1)
{
PPUClock += OpCycles();
if (PPUClock < 89xxx)
RunNextOp();
else
{
// Find out how many cycles that are left of the current instruction and
// run it after NMI
break;
}
}
you might have a problem with the ppu cycle in the cpu instruction. since the ppu is (ntsc) 3 times faster then the cpu, and the average cpu instruction is about 4 cycles long, that leaves 3 * 4 = 12 possible values to be read from if the cpu was reading a ppu register. so are you rounding your reads and writes to the intruction boundary? or updating on the exact ppu cycle ?
i have a cpu clock and a ppu clock. i run the cpu till predicted nmi or irq. and catch up the ppu where needed. this works, i might change it alittle, but i think it will always be a catch-up. and i dont lock mine to render frames because i might use the sound call back some day for timing. and the sound call back might never line up with the render frame.
also, i posted a while ago about save states. i took that into consideration too. so i can pause my emulation on a save frame.
sucks that search is STILL broke, but there is alot of info in the old posts.
matt
Since the first two sub tests of 3.even_odd_frames.nes passed, it suggests something more subtle is going on. Test 2 keeps BG disabled the whole time, and passes. Test 3 enables BG for two consecutive frames, then disables it, and expects that only one PPU clock was skipped. Test 4, which fails, enabled BG for one frame, disables it for two, then enables it for one more frame, also expecting only one PPU clock to be skipped.
Oh, wait, maybe I'm assuming too much again. Have you verified that 1.frame_basics.nes and 2.vbl_timing.nes both pass? Ugh.
Yeah. both pass! But isnt it possible for example that you read the status register at PPU clock 89341 and I get error cause this read is finished before NMI or something like that.
btw, do all instructions do their thing at its last cycle? if NMI is triggered inside an instruction, where is the programcounter? pointing to the instruction, the parameter, the next instruction or does it depend on how far the instruction have come (in cycles)?
n6 wrote:
do all instructions do their thing at its last cycle?
what do you mean by your thing ? i think you mean when does the read or write occur? if so then that is what i was trying to explain in my last post. you need to determine the exact ppu cycle a ppu register is read on. you can not round that to the end of the instruction.
matt
Yeah but what I mean is... LDA $2002 for example, when do the read occur? it takes 12 CPU Cycles to run but at which cycle is the value catched? isnt this the same even if its WRAM or mapper or whatever?
LDA $2002:
1: Read opcode
2: Read low byte of address
3: Read high byte of address
4: Read from $2002
There's also a fifth cycle, but this is overlapped with the opcode fetch for the next instruction so it's usually ignored. The NMI and IRQ lines are checked during cycle 3 in the above example. If either is set, then the interrupt will occur after the instruction finishes, and the program counter saved on the stack will be for the next instruction.
I see interesting.. so if it's less then 3, the opcode is executed after NMI? and the program counter points to?
since the opcode is finished in the 3 or above case, do this mean that the opcode restarts if its less then 3?
Is there a complete table of where NMI/IRQ checks are made for each op code?
thanks
All interrupts occur between completely executed instructions; an instruction is never interrupted before completion. The saved program counter on the stack is where execution should continue once the interrupt has been handled. My tests indicate that it's always the second from last cycle of an instruction that the IRQ/NMI inputs are examined, or the next-to-last cycle if you're ignoring the final cycle that's overlapped with the opcode fetch of the next instruction. This checking is effectively two cycles before it takes effect, so if IRQ/NMI is asserted in the third cycle of LDA in the example above, then an interrupt will occur after LDA completes.
Okey.. last question...
if it just one cycle left until NMI when an LDA $2002 appears, is it ignored? and just executed after NMI?
opcodes that take different time like branches.. are NMI/IRQ checked at different places depending on if it takes 2 or 4 cycles?
Let me "teach" something cool. I checked the PC address where the test begins, plus the LDA instruction (yes, the exact moment). Once LDA #$03 (for test 3, as example) is triggered, so I start my CPU log (disassembling and a few things, as IRQs, NMIs and PPU timing are logged). You can add log to a few PPU rendering events, plus log its registers (2000,2002,etc). Man... it was wonderful. I could check WHY my emu wasn't OK, in which TIME it fails, what things are being triggered or not, and when. Asking for "theory only" might fail after all, because you WON'T understand the internal thing in deep meaning. ^_^;;
If an interrupt (IRQ or NMI) occurs during the last cycle of an instruction, the CPU will execute one more instruction, then process the interrupt. If an interrupt occurs during any other cycle of an instruction (from the first cycle through the second-to-last), the CPU will finish executing that instruction, then process the interrupt.
LDA #1
LDA #2
If an interrupt occurs during the first cycle of LDA #1, the accumulator will contain a 1 when the interrupt code begins executing, and the PC on the stack will point to the start of LDA #2. If, however, an interrupt occurs during the second cycle of LDA #1 (this instruction is 2 cycles long), the accumulator will contain a 2 when the interrupt code begins execution, and the PC on the stack will point to the first instruction after LDA #2. You can think of it as a 1-cycle latency (where one cycle passes after an interrupt occurs before the CPU sees it).
oh its bit hard to understand..but is like this: I should check when the last instruction is executed if its overflow the 'num of cycles per frame' by only one CPU cycle, if this is true, the next opcode should be executed as well?
Quote:
If an interrupt (IRQ or NMI) occurs during the last cycle of an instruction, the CPU will execute one more instruction, then process the interrupt. If an interrupt occurs during any other cycle of an instruction (from the first cycle through the second-to-last), the CPU will finish executing that instruction, then process the interrupt.
Quite interesting. This might fix a few things here, thanks.
By "last", you mean e.g. the second cycle of INX or the fifth cycle of LSR $30, not the (pipelined) writeback + next instruction fetch, right?
tepples wrote:
By "last", you mean e.g. the second cycle of INX or the fifth cycle of LSR $30, not the (pipelined) writeback + next instruction fetch, right?
Ugh, yes, this issue has been growing in my mind lately. We really need to clarify the conceptual model on this aspect. In reality, execution of INX occurs during three cycles, but only increases total execution time by two cycles. What do we do? Currently it seems we say that INX takes two cycles, but then confusingly refer to the third cycle of the instruction
and call the second cycle the last cycle?!?
Could we say last visible cycle of an instruction? The last non-overlapped cycle? Seems we need to upgrade the shared conceptual model and terminology, or these things will remain confusing due to our inability to refer to things without long phrases.
I'd say use whatever terminology MOS Technology and Western Design Center use.