Question about detecting a PAL or NTSC environment
Moderator: Moderators
Question about detecting a PAL or NTSC environment
Just wondering if this is the generally accepted approach to detecting if you are in a PAL or an NTSC system.
- In the NMI handler clear a counter and set a flag variable and RTI
- In the reset handler infinite loop, if the flag is set increment a counter over and over (probably needs to be 16 bit)
- When the next NMI handler fires, un-set the flag (or change states in the state machine), and determine what mode we are in based on that counter. I can look at the values in a debugger and so I can see which value I get on PAL and which on NTSC, and base my mode on those values.
Is this how you guys do it? I want to make sure I approach this correctly.
Thanks,
Al
- In the NMI handler clear a counter and set a flag variable and RTI
- In the reset handler infinite loop, if the flag is set increment a counter over and over (probably needs to be 16 bit)
- When the next NMI handler fires, un-set the flag (or change states in the state machine), and determine what mode we are in based on that counter. I can look at the values in a debugger and so I can see which value I get on PAL and which on NTSC, and base my mode on those values.
Is this how you guys do it? I want to make sure I approach this correctly.
Thanks,
Al
A simpler way would be to start a two notes on the APU with lengths 1 and 2, poll $4015 until the first one stops, then poll $4015 until the second one stops and count the number of iterations. The count will tell whether it's NTSC or PAL. When I get a spare moment I'll try implementing this and testing it on both types of NES.
If you write to $4017 to reset the frame sequencer immediately before running the test, then you'll only need one note (of length 1) to determine the timing. This method is probably the simplest since it does not depend on interrupt code timing (which can vary as you extend the capabilities of your NMI handler). However, it might not work well in emulators that don't properly implement APU timing (especially for PAL). Using NMI timing is likely to be more emulation-safe.
"Last version was better," says Floyd. "More bugs. Bugs make game fun."
Hey, I'm getting a bit outtopic, but I also trough of this method once, when brainstorming about how a MMC5 cart could detect if it has it's sound hardware implemented or not. Just start a note with a run time-lenght, and poll $5015 for a while. If it's set forever (or clear forever), then the MMC5 doesn't have sound. Unfortunately, carts without sound still have it implemented. This could still detect bad emulators or simplified MMC5s faked by other circuits.
Useless, lumbering half-wits don't scare us.
Now that I look at Years Behind, it seems I actually used the buggy polling of $2002 to wait for the next frame when doing the PAL/NTSC detection. This should mean that with enough resets, you could in theory get the cart to run on NTSC with a lucky shot. The demo would crash later on though.
Gotta try this sometime. :)
Gotta try this sometime. :)
I got it working. I reset the frame counter, emit a sound, and poll 4015 until the sounds completes. However I dont really know why the sound that I chose works. As far as I can tell, its a zero length sound. Is the reason it worked because I disabled the length counter, or because a sound cannot have a length of zero.
Anyways here's the code.
Even though it works, am I actually doing this right?
(edit- make the code readable)
BTW: REGION_CHECK_LOW and REGION_CHECK_HIGH are variables on zero page.
I found that in FCEU the high gets a value of 0x22 on NTSC and 0x26 on PAL (so I just say its PAL if the value exceeds 0x24) Seems to work find in Nintendulator as well (although I'm having troubles viewing the memory). Real hardware (NTSC) is also fine.
Al
Anyways here's the code.
Code: Select all
; Reset the audio frame counter
LDA #$40 ; same as #%01000000
STA $4017
; Make a sound on rect1
LDA #$00
STA $4000
STA $4001
STA $4002
STA $4003
; Poll 4015 (lowest bit is rect1) until the sound goes away
TEST_REGION:
INC REGION_CHECK_LOW
BNE :+
INC REGION_CHECK_HIGH
:
LDA $4015
AND #$01
BNE TEST_REGION
(edit- make the code readable)
BTW: REGION_CHECK_LOW and REGION_CHECK_HIGH are variables on zero page.
I found that in FCEU the high gets a value of 0x22 on NTSC and 0x26 on PAL (so I just say its PAL if the value exceeds 0x24) Seems to work find in Nintendulator as well (although I'm having troubles viewing the memory). Real hardware (NTSC) is also fine.
Al
OK, you've persuaded me to write some code and run it on my NTSC and PAL NES motherboards. As for zero length, remember that the value you write to $4003 in the upper 5 bits is an index into the length table, not the length itself. As I remember, $00 gives a length of $0A. If you used $18 (corrected), you'd get a length of $02, the shortest available. Same goes for writing zero to $4000; this doesn't give zero volume, but rather a quick envelope. The reason you get a silent square is because you used a period of 0 ($4002 and $4003).
EDIT: made correction, should have been length index $18, not $08.
EDIT: made correction, should have been length index $18, not $08.
Last edited by blargg on Thu Apr 12, 2007 12:50 pm, edited 1 time in total.
Here's some code tested on NTSC and PAL NES boards. The routine doesn't modify X or Y and doesn't need any zero-page locations. I put the decision point mid-way between NTSC and PAL, so an emulator with slightly different timing should still work. To use, run the routine then examine the Z flag or see if A is zero or not:
Code: Select all
jsr is_pal_nes
sta is_pal_flag
bne is_pal
is_ntsc:
...
is_pal:
...
; Deteremines whether code is running on an NTSC or PAL NES
; For NTSC, A = 0. For PAL, A != 0. Z flag also set.
; Chanes APU frame sequencer mode and $4015 enables.
; Takes about 1/60 second to complete.
; Preserved: X, Y
is_pal_nes:
; Start tone
lda #$40 ; reset frame sequencer, IRQ off
sta $4017
lda #$01 ; enable square 0
sta $4015
lda #$00 ; pitch = 0 (silent)
sta $4002
lda #$18 ; load length with $02
sta $4003
; Count time until tone stops
lda #5 ; makes NTSC and PAL be equal
sec ; distances from $80 after loop
is_pal_nes_loop:
adc #0 ; iter counter
pha
lda #$D0 ; internal delay loop
is_pal_nes_delay:
adc #1
bne is_pal_nes_delay
lda $4015 ; C set if tone is running
lsr a
pla ; doesn't modify C, but does set N flag
bcs is_pal_nes_loop
; A=$79 for NTSC
; A=$86 for PAL
bmi is_pal ; PAL if count >= $80
lda #0 ; clear to 0 if NTSC
is_pal:
rtsOn real hardware (front loading NES) this code seems to work the first time but not after the reset button is pressed. Runs fine in emulators I have checked, but they will likely do something different at reset. According to microbug is_pal_nes_loop is never exited. Using microbug shouldn't be a problem because it is waiting for the audio to not be playing? Any ideas?
LJ65 (formerly Tetramino) currently has separate builds for NTSC and PAL. I lock out users of the wrong build because they have different delays in them, and I don't want people cheating by claiming scores from the NTSC version run on a PAL machine. LJ65's init code just counts cycles between two NMIs.
Famicom, NTSC NES, PAL/M NES, PlayChoice: 262*341/3 ~= 29780 c
PAL NES: 312*341/3.2 ~= 33247 c
Dendy is thought to use PAL scanline count but an NTSC compatible divider, which would give 312*341/3 ~= 35464 c. But does anyone on this board have access to a Dendy unit and a devcart to run tests?
Famicom, NTSC NES, PAL/M NES, PlayChoice: 262*341/3 ~= 29780 c
PAL NES: 312*341/3.2 ~= 33247 c
Dendy is thought to use PAL scanline count but an NTSC compatible divider, which would give 312*341/3 ~= 35464 c. But does anyone on this board have access to a Dendy unit and a devcart to run tests?