This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Detecting for NTSC or PAL

Detecting for NTSC or PAL
by on (#32820)
Is there simple code that can detect which version of the NES someone has during bootup? I wanted to make my game have adjustments for PAL. The NSF I use can be done easily enough, though timed events are slower.

by on (#32821)
Once you have NMI turned on, count the time between one NMI and the next. On NTSC, your loop should run about 341*262/3 = 29780 cycles; on PAL it should run 341*312/3.2 = 33247 cycles. So if a frame is less than about 31,500 cycles, it's NTSC (or PAL/M which uses the same timings as NTSC); otherwise, it's PAL.
Re: Detecting for NTSC or PAL
by on (#32943)
If you want to avoid messing with NMIs for this you can simply wait for the beginning of a frame by polling ppu_status instead, just like most reset code does (in fact the detection loop is a prime candidate for execution during the dummy initialization frame).
Code:
         ;; first we need to wait for the start of vertical blank
         bit ppu_status
.stabilize:
         bit ppu_status
         bpl .stabilize

         ;; count the number of cycles in the frame
         lda #$00
         tax
.detect:
         cpx #$ff       ;; -.
         inx            ;;  |
         adc #$00       ;;  | 15 cycles per iteration
         bit ppu_status ;;  | (bmi/jmp avoids branch penalties)
         bmi .done      ;;  |
         jmp .detect    ;; -'

.done:

.ntsc    = 29780
.pal     = 33247
.limit   = (.ntsc+.pal)/(15*2)

         cpx #<.limit
         sbc #>.limit

         ;; carry clear => NTSC
         ;; carry set   => PAL

Also what's up with the tabs in code boxes? I don't know what they're doing but they sure as hell don't line things up..
Re: Detecting for NTSC or PAL
by on (#32944)
Except that sometimes the VBlank flag goes undetected, if you happen to read it while it's being set by the PPU. If this happens in this case, you'll probably get a bad result. I'd use NMIs. Polling $2002 should never be done when timing is important. If you absolutelly *need* to wait for VBlank the old way, have the NMI set a flag and have the waiting code check this flag instead.

doynax wrote:
Also what's up with the tabs in code boxes? I don't know what they're doing but they sure as hell don't line things up..

Well, each editor uses a different number of spaces to represent TABs (Notepad's for example is ridiculously large), so if this board (or your browser, I don't know which one is converting TABs into spaces) uses something different than the editor where you wrote the code, it will look wrong. Note that some editors will let you configure that spacing though, and even convert the TABs into actual spaces if you choose to.

That's kinda why I mostly gave up TABbed comments (although that seems to be some kind of standard in assembly)... They look pretty weird accross different editors, and some times I do use different editors. You know, those times when you have an urge to code and all that's available is Notepad! :D
Re: Detecting for NTSC or PAL
by on (#32945)
tokumaru wrote:
Except that sometimes the VBlank flag goes undetected, if you happen to read it while it's being set by the PPU. If this happens in this case, you'll probably get a bad result. I'd use NMIs. Polling $2002 should never be done when timing is important. If you absolutelly *need* to wait for VBlank the old way, have the NMI set a flag and have the waiting code check this flag instead.
My bad, I had no idea this was the case (and for once its even mentioned in the wiki!). Bloody hardware bugs.
Oh well, time to go back and fix some code..

Quote:
doynax wrote:
Also what's up with the tabs in code boxes? I don't know what they're doing but they sure as hell don't line things up..
Well, each editor uses a different number of spaces to represent TABs (Notepad's for example is ridiculously large), so if this board (or your browser, I don't know which one is converting TABs into spaces) uses something different than the editor where you wrote the code, it will look wrong.
It's not the number of spaces (it seems to use three), which I can live with. The problem is that rather then moving to the next tab stop (the next column evenly divisible three) it simply jumps three characters ahead regardless of the current position, which is amazingly fucked up.

Just look at these examples, where a single tab precedes the comments:
Code:
   ;0
a   ;1
ab   ;2
abc   ;3
abcd   ;4


Quote:
That's kinda why I mostly gave up TABbed comments (although that seems to be some kind of standard in assembly)... They look pretty weird accross different editors, and some times I do use different editors. You know, those times when you have an urge to code and all that's available is Notepad! :D
I tend to use 8-character tabs for assembler code so its not really an issue as every editor known to man supports it (based on some old ancient VT terminal I suppose), and any sane code editor ought to be customizable for each file type.

The only reason I can see to use a shorter tab size would be for indentation. And while I've tried in assembly code occasionally it has never really worked out. Perhaps if someone wrote an assembler with a excellent set of scoped flow control macros. But with every such system I've tested to date I've just ended up with a bunch of regular labels and branches intermixed with the high-level macros, making the scopes useless as visual cues for the program flow.
Re: Detecting for NTSC or PAL
by on (#32946)
doynax wrote:
Just look at these examples, where a single tab precedes the comments:
Code:
   ;0
a   ;1
ab   ;2
abc   ;3
abcd   ;4

Oh, I never noticed that! Yeah, this really is weird. Having your editor convert TABs into spaces should show the correct spacing here then...

Quote:
The only reason I can see to use a shorter tab size would be for indentation. And while I've tried in assembly code occasionally it has never really worked out.

I once felt like doing this too, but couldn't see it working out.

by on (#32949)
It's okay to spin on the PPU status register to test vblank when the NES is still in its initial boot phase and hasn't warmed up yet. If you miss one frame at that point, it's no big deal.

by on (#32951)
Dwedit wrote:
It's okay to spin on the PPU status register to test vblank when the NES is still in its initial boot phase and hasn't warmed up yet. If you miss one frame at that point, it's no big deal.
Well, probably. Except if you somehow manage to write a test loop which keeps hitting the status bug *every* frame.

Now according to the wiki you'd have to poll the status bit on exactly the right PPU clock for it to be missed, and as far as I can tell the only time a frame is an whole number of CPU cycles long is on odd NTSC frames when the display is enabled. And in any event a 7-cycle BIT/BPL spin loop isn't even close to being a factor in either standards' timing (though if you get a branch penalty the resulting 8-cycle loop might be).

But then I'm far from certain about any of this and the PAL details seem a bit sketchy so I think I'd rather take my chances with a proper NMI and avoid all polling altogether.

by on (#32953)
There is no guarantee that the NMI will work as exepted for the first few frames, so it's better not rely on it.

by on (#32957)
doynax wrote:
Well, probably. Except if you somehow manage to write a test loop which keeps hitting the status bug *every* frame.


Not possible unless you intentially wait at least 1 full frame between $2002 polls. The glitch only happens if you read on a specific PPU cycle, and since the PPU is 3x the speed of the CPU, and there isn't an integer number of CPU cycles per frame, it'd be impossible to hit the bug on two consecutive frames.

Quote:
Now according to the wiki you'd have to poll the status bit on exactly the right PPU clock for it to be missed, and as far as I can tell the only time a frame is an whole number of CPU cycles long is on odd NTSC frames when the display is enabled.


1 frame = 262 scanlines
1 scanline = 341 dots (PPU cycles)
1 CPU cycle = 3 dots

therefore 1 frame = 262*341/3 = 29780 + 2/3 CPU cycles

1 dot is missing on the pre-render scanline on odd frames and if the PPU is on, so this could be 29780 + 1/3 cycles instead -- however that still isn't a whole number.

PAL numbers:

1 frame = 312 scanlines
1 scanline = 341 dots
1 CPU cycle = 3.2 dots

therefore 1 frame = 312*341/3.2 = 33247 + 1/2 CPU cycles

Note PAL does not skip that 1 dot on odd frames like NTSC does.

by on (#32958)
Dwedit wrote:
It's okay to spin on the PPU status register to test vblank when the NES is still in its initial boot phase and hasn't warmed up yet. If you miss one frame at that point, it's no big deal.

tokumaru wrote:
Polling $2002 should never be done when timing is important.

Yeah, there are a few cases when missing a frame is no big deal. In this particular case though, during PPU warm-up, even NMIs can't be 100% trusted, as Bregalad said.

by on (#32959)
I don't understand the issue. Just wait for VBL, delay 29800 clocks, then read VBL flag. If set, NTSC, otherwise you're on PAL. If the wait VBL takes a couple of frames due to the pathological behavior, that won't mess up the check.
Code:
        lda $2007 ; clear VBL flag
wvbl:   lda $2007
        bpl wvbl
       
        ldx #52         ; 29799 delay
        ldy #24
dly:    dex
        bne dly
        dey
        bne dly
       
        lda $2007
        sta ntsc_if_neg

by on (#32961)
blargg wrote:
I don't understand the issue. Just wait for VBL, delay 29800 clocks, then read VBL flag. If set, NTSC, otherwise you're on PAL. If the wait VBL takes a couple of frames due to the pathological behavior, that won't mess up the check.
Duh.. Why didn't I think of that?

Oh, and (once again) watch out for those branch penalties.. ;)

by on (#33059)
blargg wrote:
Code:
         
        lda $2007
        sta ntsc_if_neg


Sorry for sounding like a noob, but what exactly would the value be in $2007 at this point if it was PAL? 0? I'd prefer to do a CMP and store a number in a variable.

I should also have this check at the beginning of my NMI, yes?

Thanks.

by on (#33063)
NMI has nothing to do with it.

Code:
reset:  ; Reset vector points here
        ; standard NES init
        ...
       
        ; Determine whether system is NTSC or PAL
        lda $2002       ; clear VBL flag
wvbl:   lda $2002
        bpl wvbl

        ldx #52         ; 29799 delay
        ldy #24
dly:    dex
        bne dly
        dey
        bne dly
       
        lda $2002       ; high bit set if NTSC, clear if PAL
        sta ntsc_if_neg
       
        ...
       
        ; Later, where you need to do different things for NTSC and PAL:
        bit ntsc_if_neg
        bmi ntsc
pal:    ...

ntsc:   ...


EDIT: fixed totally broken code

by on (#33064)
Shouldn't that be $2002 (PPUSTATUS), not $2007 (PPUDATA)?

Sivak wrote:
blargg wrote:
Code:
         
        lda $2007
        sta ntsc_if_neg

Sorry for sounding like a noob, but what exactly would the value be in $2007 at this point if it was PAL? 0?

The state of $2002 after this many cycles of PAL could be any number from 0 to 127, depending on the state of the sprite hardware and various internal PPU buffers, but bit 7 will be clear. You can 'bpl' to test this number: 'bpl' is taken if PAL (was "branch if PLus" now "branch if PaL"), 'bmi' if NTSC.

by on (#33152)
I saw the revised code. Thanks for the assistance, PAL emulation on 3 different emus produces the new results.

I originally had this configurable in the option screen, but now this test works. Kinda cool that my game is "region-free". 8)