cpu speed

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
cpu speed
by on (#2518)
The NES cpu runs at (236250000/11)/12 hz, that is 1789772.7272727272hz. NTSC refresh rate is 60/1.001 hz, that is 59.94005994005994005994005994005994hz. 3 PPU cycles take 1 CPU cycle, there are 341 PPU cycles in one scanline, and 262 scanlines in one frame.

Now, if I calculate the NES cpu speed, based on PPU cycles, which most emulators base their timing on, I get a wrong result: ((262*341)*(60/1.001))/3=1785054.945055hz. Or does the NES perhaps use a funky refresh rate ?: ((236250000/11)/12)/((262*341)/3)=60.098478hz.

I've tried decreasing the 3:1 ratio, but that's causing huge problems.

*edit* Or does the NES use interlacing ? That would mean there are 263 lines every odd frame, and it would change the ratio to 1364:455.

by on (#2522)
It's the latter; the NES PPU uses the funky refresh rate you calculated of ~60.098Hz. An audio recording timed by NMIs proves this, as it will not synchronize properly with a recording timed at 59.94Hz or even 60Hz.

Evidently, the PPU's odd refresh rate is close enough to NTSC that television sets don't seem to mind and can display it anyways.

by on (#2523)
About the frame sequencer:

I won't blame anyone on errors, since this post is quite old: http://nesdev.com/cgi-bin/wwwt ... =#Post1171

"Assuming $4017 is written with $00 at the beginning of the VBL handler. The VBL interrupt occurs slightly less often than every 1/60 second, so the fourth step occurs slightly before the next VBL interrupt."

In this case, the frame IRQ wouldn't be fired at all then ? Since the PPU refresh rate is faster than the frame sequencer.

by on (#2524)
NTSC Frame IRQs, as it has been determined, occur every 29830 CPU cycles, which works out to almost exactly 60Hz.

Also, there is no reason why both Frame IRQs and PPU NMIs cannot occur simultaneously, since they are from entirely independent sources.

by on (#2525)
Well, NMI's occur every (262*341)/3=~29781 CPU cycles, so if you'd reset the frame sequencer, and enable frame IRQ's right at the start of the NMI handler by writing 0 to 0x4017, the frame IRQ would never happen, since it would start counting down again from 29830, and never reach zero. Right ?

by on (#2526)
If you're going to use Frame IRQs, you write 0 to $4017 once and never write to it again so it stays timed properly.

by on (#2528)
As far I know, the only game that uses frame IRQ is Dragon Quest. Dragon Warrior uses NMIs to time the sound code, and you'll notice that no difference at all is audible between thoose two.
But, does someone knows at wich rate are frame IRQ fired on a PAL system ? Is it also 60Hz, or would it be slowed to 50 Hz like the refresh rate ? If it's 60Hz, it could prevent the music of a game to run slower if it uses frame IRQ.

by on (#2536)
Given all the other differences between PAL and NTSC timing, including the timing for any raster effects, it's probably best to make two separate builds of a game and just scale the BPM differently on PAL.

by on (#2540)
Not only Dragon Quest is using it. In my emulator, these games had problems before emulation of frame IRQ's, and are working fine now:

Qix: locked up when starting a game
Door Door: locked up when dying
Shin 4 Nin Uchi Mahjong: locked up at the title screen

by on (#2641)
The Maj Jong game you mentioned uses Frame IRQs for music generation (rather than being NMI driven). I remember because before I had frame IRQs working right the tempo of the music was off.

by on (#2642)
Q said that FireHawk just requires correct FrameIRQ emulation in order to get it working. I can hear music in both games you mentioned. So, what's up? :|

by on (#2643)
Fire Hawk does not use frame IRQs - it uses DPCM IRQs. There's a rather significant difference.

by on (#2645)
Quietust wrote:
Fire Hawk does not use frame IRQs - it uses DPCM IRQs. There's a rather significant difference.


Ah... a misunderstanding I suppose. OKay, it makes the things a lot easier. ;)

by on (#2758)
Has Stars SE Demo (PD) been developed (or tested afterwards) on a real NES ? APU docs say frame IRQs are enabled on boot, but if I do this in my emulator, that demo will just hang with a black screen. If I don't enable frame IRQs on boot, it will work.

by on (#2785)
I ran the following code, which supports the assertion that the APU frame interrupt is enabled ($4017=$00) on power-up. When run it beeps. If the frame interrupt is disabled, it doesn't beep.

Code:
reset:
      lda   #250        ; delay 1/4 second
      jsr   delay_msec
     
      ;lda  #$40        ; disable apu frame interrupt
      ;sta  $4017
     
      lda   $4015       ; clear any pending frame interrupt
                        ; (in case it's set at power-up but not set afterwards)
     
      cli               ; unmask irq
     
forever:
      jmp   forever
     
irq: 
      lda   #$82        ; play beep
      sta   $4000
      lda   #$01
      sta   $4001
      sta   $4002
      sta   $4015
      sta   $4003
     
      jmp   forever

nmi:
      rti

      .org $fffa
      dw    nmi
      dw    reset
      dw    irq

by on (#2788)
Thanks.

To 'fix' Stars SE, change 0x58 at offset 0x17 to 0x78.

by on (#2796)
Sonic 3D Blast 6 doesn't work with $4017 set to 0 on power-up, presumably because it's a hacked dump. Other than that, no other game seem to cause any trouble when default-enabling frame IRQ's.

by on (#3179)
I'm having a bit of trouble getting Ironsword (W&W2) and Cobra Triangle to run with frame IRQs enabled on boot.
At boot, after 1 frame, the frame IRQ line is set, then it writes 0 to the frame sequencer, and one frame later, it writes 0x80 to it. It never acknowledges the frame IRQ.

Ironsword locks up at the titlescreen. That happens because it does a PLP that sets the status register to 0. The frame IRQ is finally let through, and it gets stuck in an RTI/frame IRQ loop.

Cobra Triangle locks up in-game, I don't know yet what it does, but the boot-sequence is quite the same as in Ironsword, so I guess I'm doing something wrong at boot, but I have no clues.

Image
"Better watch it bro, for I wield legendary imaginary IRONSWOR!"

by on (#3181)
Frame IRQs ONLY occur in the 4-step frame counter sequence (i.e. when you write $00 to $4017); they do NOT occur during the 5-step sequence ($80 -> $4017).

by on (#3186)
Yes, the frame IRQ happened when 0x4017 was 0 (1 frame after powerup).

The IRQ line stays hot until it's acknowledged, right ? eg.:
- pending frame irq and pending mapper irq
- acknowledge mapper irq
- don't acknowledge frame irq
- rti
- pending frame irq again

(loop)

Writing 0x80 to 0x4017 won't acknowledge the pending IRQ (0x4017.6 does that).

by on (#3213)
Hap is correct. The only way (I've found) to clear the frame IRQ flag is to read $4015 or write to $4017 with bit 6 set (i.e. $40 or $c0). Writing $00 and $80 to $4017 never clears the flag.

Darn it, I'm almost done with a first round of APU test ROMs, and this is one of the things it checks. I'm going to have to break the frame sequencer tests into two releases since I've got a great set ready (the complexity due to the delay when changing modes is really overwhelming me).

What are you initializing the NES RAM to on power-up? Perhaps you're using 0 and the plp is getting that. I recently re-ran the test and got the same result: RAM filled with $ff (except four bytes in zero-page that have different values).

by on (#3240)
RAM set to 0 or 0xff has no effect on it (I have it set to 0xff).

I saw wrong, my debugger is kind of confusing: the PLP gets 0x90, not 0 (the 0x90 is from a PHA a few instructions earlier, stupid game bug I guess). I'm almost certain the clearing of the 'I' flag is correct behaviour, and I've got a bug somewhere related to IRQ handling.

by on (#3255)
Problem solved. On powerup, I started at the dummy scanline right after vblank. Starting at vblank instead, fixed this frame IRQ bug.

Something I thought of while searching for the cause of this bug:
If an IRQ is pending, and you do a CLI, or PLP or RTI that clears the 'I' flag, does the IRQ happen after this instruction, or after the next one ?

Doing it the 2nd-method-way fixes Stars SE.

by on (#3260)
If the IRQ line is already asserted and cli is executed, the next instruction is executed before taking the IRQ. I just ran the following:

Code:
     lda   #1
     cli
     lda   #2
     lda   #3
     sei
irq: jsr print_a ; prints 2


EDIT: plp behaves the same as cli, but rti does not execute the next instruction:

Code:
      ; addr and $00 pushed on stack
      lda   #1
      rti
      lda   #2
addr: lda   #3
      lda   #4
      sei
irq:  jsr   print_a ; prints 1


This means that if you don't clear the source in an IRQ handler and it keeps getting called, the interrupted code won't get executed at all, rather than one instruction between each IRQ invocation.

EDIT: ...except in the curious case of cli sei, where the IRQ is acknowledged but the I flag is set in the saved status:

Code:
     ; IRQ already asserted
     cli
     sei
     ; IRQ occurs here
forever:
     jmp forever

irq: rti   ; executed only once

by on (#3268)
Interesting. But too bad for Stars SE:

(pending frame IRQ)
...
CLI
LDA Immediate 0x07
something1
something2
something3
irq: RTI

this translates as:
CLI
LDA..
RTI, RTI, RTI, RTI, etc.

while this would fix Stars SE:
CLI
LDA..
RTI
something1
RTI
something2
RTI, etc.

(Yeah, I know, kind of dumb to use Stars SE as a test case, it might not have been tested on a real NES)


*edit*
Days of Thunder (U), had what I thought to be a feature where the dashboard shaked vertically when the engine was revved high. That doesn't happen anymore after implementing this irq flag 'edge trigger' behaviour. Other glitches still happen though, like 2 displaced scanlines on the textbox at the gameover screen.

(the screenshaking problem in Ys III is still there)

*edit2*
Days of Thunder (U) behaves the same as before after fixing a bug, so ignore the comment about it above.
It might be important to know that whenever Ys III does a shake, a pending IRQ, caused by a CLI, is always involved.

by on (#3336)
hap wrote:
Problem solved. On powerup, I started at the dummy scanline right after vblank. Starting at vblank instead, fixed this frame IRQ bug.


As I kinda suspected, this broke some other games (ppu statusregister being 0x80 on boot). Could it be something undocumented is happening ? Like the pending frame IRQ fading away somehow.

If it's not too much work, can it be tested?:
- set interrupt flag
- wait 2 frames, so the apu frame irq flag gets set
- write 0x80 to the framesequencer
- wait a few seconds (in w&w2 the interrupt flag is cleared after about 3 seconds, in cobra triangle when you start the game)
- clear interrupt flag
- frame irq happens ?

by on (#3341)
Things like you suggest to test are the exact thing I'm looking for since they might uncover new details of operation.

I ran the test you suggested and don't find evidence of the frame IRQ flag "fading":

Code:
reset:
      sei
     
      lda   #$00        ; mode 0, frame irq enabled
      sta   $4017
     
      lda   #34         ; 34 msec (a little over 2 frames)
      jsr   delay_msec
     
      lda   #$80        ; mode 0, frame irq unaffected
      sta   $4017
     
      ldx   #30         ; 3.0 seconds
:     lda   #100
      jsr   delay_msec
      dex
      bne   -
     
      cli
      jmp   forever     ; would print nothing if no irq
     
irq:  lda   $4015
      jsr   debug_byte  ; prints $40


Since you mentioned VBL and NMI, I ran some other tests on their power-up and reset timing.

At power-up, the VBL flag ($2002.7) was set on each of several test runs. On reset, the VBL flag was usually clear. It was next set and readable by the CPU 27386 clocks later (sometimes one clock earlier).

Code:
reset:
      lda   $2002       ; 4 clear VBL flag
      ldy   #60         ; 27379 delay
      lda   #90         
      jsr   delay_ya2
      lda   $2002       ; 4 read $2002 at 27386
      and   #$80
      jsr   debug_byte  ; consistently prints $80


When timing the first NMI, I found that writing $80 to $2002 was ignored until 29659 clocks after reset (though usually just 29658):

Code:
reset:
      ldy   #32         ; 29654 delay
      lda   #184       
      jsr   delay_ya5
      lda   #$80        ; 2 enable nmi
      sta   $2000       ; 4 write at 29659
                        ; any earlier is somtimes ignored


The first NMI occurs by 57168 (sometimes one clock earlier) after reset (I wasn't getting a consistent result after power-up and don't have the energy to run any more tests right now):

Code:
reset:
      ldy   #32         ; 29654 delay
      lda   #184       
      jsr   delay_ya5
      lda   #$80        ; 2 enable nmi
      sta   $2000       ; 4 write at 29659
     
      ldy   #182        ; 27503 delay
      lda   #29         
      jsr   delay_ya4
     
      lda   #1          ; 2
      lda   #2          ; 2
                        ; NMI occurs here...
      lda   #3          ; 2
                        ; ...or here
      lda   #4          ; 2
     
nmi:  jsr   debug_byte  ; prints 2 or 3

by on (#3348)
Quote:
At power-up, the VBL flag ($2002.7) was set on each of several test runs. On reset, the VBL flag was usually clear. It was next set and readable by the CPU 27386 clocks later (sometimes one clock earlier).


This fixes Ironsword/Cobra Triangle, while not causing extra problems (eg. Downtown Special, that Japanese Samurai version of River City Ransom, had screenshaking problems when booting in vblank). 27386, that means at boot it's out of vblank, what about the other 0x2002 bits then ?

Quote:
When timing the first NMI, I found that writing $80 to $2002 was ignored until 29659 clocks after reset (though usually just 29658):


Are early writes to other PPU register(bits) also ignored ?

by on (#3369)
Times are in clocks since reset or power-up. For example, the following code executed at reset writes $00 to $2000 at time 5:

lda #$00
sta $2000

Writes to $2006 before 29658 were ignored (sometimes only before 1911 clocks after power-up).

Writes to $2007 were not ignored just after reset (I haven't run a test to figure out what the $2006 address is set to on power-up).

The address in $2006 was unchanged after reset.

Writes to $2003 and $2004 were not ignored just after reset.

Reads of $2007 before 29657 put $00 into the internal buffer; at that time and after reads worked normally.

I'll have to run more tests another time (2.5 hours of testing seems to be my limit).

by on (#3448)
blargg wrote:
Reads of $2007 before 29657 put $00 into the internal buffer; at that time and after reads worked normally.

Kamen Rider Club (J) doesn't like that (locks up).

I've not encountered any problems yet with the NMI delay, or with 0x2002.7 being set at boot.

by on (#3456)
Well, about the VBlank flag, it might take conclusions about its effect on emulation - like if the ppu frame cannot start on VINT, OR in the scanline right before the scanline #0. I've read about starting at VINT, then starting at scanline -1... and that it doesn't even matter... ^_^;; This would require a verification on real hardware.

The NES has a lot of quirks that I'm impressed...

by on (#3457)
hap wrote:
Kamen Rider Club (J) doesn't like that (locks up).


Either that or it doesn't like something else, but was content when this wasn't emulated correctly? Or maybe it's different on the Famicom.

One thing that would help with reverse-engineering efforts is data on what games actually do at power-up. In this case, if any of them try reading before the noted time (obviously Kamen Rider Club (J) does).

Also, the only way to really be sure an emulator is implementing things as specified is to write test ROMs for a given feature. Maybe I'll do that tonight for these power-up features.

by on (#3458)
Kamen Rider Club (J):
- disables nmi, reads 0x2002.7 (set at boot)
- reads 0x2002.7 again (set at vblank)
- initialize (banking and such)
- while still in vblank:
Code:
cpu prgbank3read ad: ef51(f51) data: a9
cpu prgbank3read ad: ef52(f52) data: c
a9 LDA Immediate PC:ef51 A:c X:ff Y:f P:001B0100 ad:ef52

cpu prgbank3read ad: ef53(f53) data: 8d
cpu prgbank3read ad: ef54(f54) data: 6
cpu prgbank3read ad: ef55(f55) data: 20
8d STA Absolute PC:ef53 A:c X:ff Y:f P:001B0100 ad:2006

cpu prgbank3read ad: ef56(f56) data: a9
cpu prgbank3read ad: ef57(f57) data: 45
a9 LDA Immediate PC:ef56 A:45 X:ff Y:f P:001B0100 ad:ef57

cpu prgbank3read ad: ef58(f58) data: 8d
cpu prgbank3read ad: ef59(f59) data: 6
cpu prgbank3read ad: ef5a(f5a) data: 20
8d STA Absolute PC:ef58 A:45 X:ff Y:f P:001B0100 ad:2006

cpu prgbank3read ad: ef5b(f5b) data: ad
cpu prgbank3read ad: ef5c(f5c) data: 7
cpu prgbank3read ad: ef5d(f5d) data: 20
ad LDA Absolute PC:ef5b A:0 X:ff Y:f P:001B0110 ad:2007

cpu prgbank3read ad: ef5e(f5e) data: ad
cpu prgbank3read ad: ef5f(f5f) data: 7
cpu prgbank3read ad: ef60(f60) data: 20
ad LDA Absolute PC:ef5e A:0 X:ff Y:f P:001B0110 ad:2007

cpu prgbank3read ad: ef61(f61) data: d9
cpu prgbank3read ad: ef62(f62) data: 76
cpu prgbank3read ad: ef63(f63) data: ef
cpu prgbank3read ad: ef85(f85) data: 33
d9 CMP Indexed Absolute (Y) PC:ef61 A:0 X:ff Y:f P:101B0100 ad:ef85

cpu prgbank3read ad: ef64(f64) data: d0
cpu prgbank3read ad: ef65(f65) data: 5
d0 BNE Relative PC:ef64 A:0 X:ff Y:f P:101B0100 ad:ef6b

(does the JMP forever)
cpu prgbank3read ad: ef6b(f6b) data: 4c
cpu prgbank3read ad: ef6c(f6c) data: 6b
cpu prgbank3read ad: ef6d(f6d) data: ef
4c JMP Absolute PC:ef6b A:0 X:ff Y:f P:101B0100 ad:ef6b