Jump to content
IGNORED

Part 6 - Console Detection


SpiceWare

Recommended Posts

Added routines to detect information about the console.

 

 

Source Code

 

Download and unzip this in your shared directory.

 

Collect3_20200120.zip

 

ROM for reference

 

collect3_20200120.bin

 

 

 

2600 / 7800 Detection

 

Back in 2005 @Nukey Shay asked if it was possible to detect if the program is running on a 2600 or 7800.  @batari figured out that the 128 bytes of Zero Page RAM is in a random state on the 2600, but on the 7800 the RAM ends up in a known state due to what the 7800's startup sequence does before it switches into 2600 mode. @batari posted a test program in reply #18 that checks if the contents of ZP RAM locations $D0 and $D1 came back as $2C and $A9 - if they do the console's a 7800, if they don't the console's a 2600.

 

What this is useful for is the pause button on a 7800 is wired to the same bit in SWCHB that the 2600's TV Type toggle is wired to, but the pause button is a momentary switch which behaves differently than a toggle. By knowing which console the program is on it can run different code to implement a pause feature monitoring the 2600's TV TYPE switch as well as the 7800's Pause button.

 

The menu on the Harmony checks $D0 and $D1 to detect the console type. When a game is loaded the 2600 needs to be idled, so an idle routine is copied into ZP RAM that overwrites the values in $D0 and $D1.  The idle routine is aware that some programs have console detection logic, so after the game is loaded the idle routine will put the values needed in $D0 and $D1 RAM for console detection.

 

The CDFJ driver has 2 different startup routines:

  • game launched via the Harmony Menu
  • game flashed onto Harmony/Melody


The CDFJ driver takes some time to initialize, so the 2600 must be idled for a brief period of time.  If the game was launched via the Harmony Menu then CDFJ just utilizes the Harmony Menu's idle routine that's already in RAM.  If flashed onto the Harmony/Melody board the CDFJ driver puts its own idle routine into ZP RAM.  Like the Harmony Menu the CDFJ idle routine detects console type, but instead of restoring the values in $D0 and $D1 it zeros out $D0 and $D1 and puts the detected console in $80.

 

So the console detection for CDFJ games does this:

if ZP RAM $D0 contains $2C and ZP RAM $D1 contains $A9
    console = 1 ; 7800
else if ZP RAM $D0 contains $00 and ZP RAM $D1 contains $00
    console = value found in ZP RAM $80
else
    console = 0 ; 2600

 

The InitSystem routine in the 6507 code now starts out with a console detection routine, the results of which end up in the Y register.  It then does the usual init, but passes the value in Y onto the C routine Initialize() which saves it in a new variable is_7800.

 

At the moment is_7800 is only used on the initial splash screen to show which console was detected. Eventually we'll use it for a pause feature.

 

2600 detected

2600.thumb.png.7747e6ef79867a556984d62098b82c3d.png

 

7800 detected

7800.thumb.png.20b48525fbb7dc08a86f87bfa2865cd8.png

 

 

 

NTSC / PAL / SECAM Detection

 

Back in 2015 @ZackAttack put forth the idea of using a timer in the ARM processor to detect if a console was NTSC or PAL, which would allow a game to auto-adjust for console differences such as the color palette.  A couple years later I followed up on that and created a NTSC vs PAL detection program.  Tests showed that while there was some variation in the results, the difference between NTSC and PAL was enough for it to work. While I was working on routines to emulate the ARM's timer in Stella, @alex_79 made me aware that clock for SECAM was also different.  Knowing that, I was able to expand the detection to include SECAM.

 

This detection occurs in function SplashVerticalBlank().  It works by starting the timer on frame 1, then stopping the timer on frame 2 and seeing how long it ran.  Expected times for each system are:

 

  • 11d329 for NTSC
  • 11e8ff for SECAM
  • 11fd2b for PAL 

 

To allow for minor hardware variations the detection uses the midpoint between SECAM & NTSC and the midpoint between PAL & SECAM:

 

        if (T1TC < (0x11e8ff + 0x11d329)/2)
            tv_type = NTSC;
        else if (T1TC > (0x11fd2b + 0x11e8ff)/2)
            tv_type = PAL;
        else
            tv_type = SECAM;

 

Once we know the TV Type the console is for, we can do on-the-fly conversions of NTSC color values to PAL or SECAM.  Check out function ColorConvert() in main.c.

 

 

By default Stella will autodetect the game as NTSC because of the 262 scanlines of output:

 

NTSC console detected, $44 used for RED

NTSC.thumb.png.e495bf9c8077d1c5bb08ba8764ae5df1.png

 

NTSC Player coloration

1170694847_ScreenShot2020-01-20at10_14_45AM.thumb.png.fbddb63dc6de0cb69a533bfa0670958c.png

 

You can tell the autodetect occurred because of the * after NTSC.

 

We can override the game properties and set the Display Format to PAL60 or SECAM60.  After doing that just reload the ROM for the change to take effect.

 

Change Game properties Display Format to PAL60

608418061_PAL1.thumb.png.f1ec13c8c7358020cd0f328bbc8f4b29.png

 

 

Collect 3 now uses $64 for RED for PAL

1197597254_PAL2.thumb.png.398edc3b182a5c0f87fe122b36f52f28.png

 

PAL Player coloration

1828624724_ScreenShot2020-01-20at10_10_30AM.thumb.png.8cd4a1b790b2927a850370c8b662b947.png

 

Change Game properties Display Format to SECAM60

74876953_SECAM1.thumb.png.e4d7a54b9abc36c96bbc15b833366d12.png

 

Collect 3 now uses $04 for RED for SECAM

461437727_SECAM2.thumb.png.abe73590fe5d9bcdaa91f1ca6c0c84be.png

 

SECAM Player coloration

241491262_ScreenShot2020-01-20at10_10_50AM.thumb.png.0eeb664a34852971c9a1bbfaca8d7db7.png

  • Like 3
Link to comment
Share on other sites

2 hours ago, Lillapojkenpåön said:

Wasn't there talk about implementing detection in Stella as well, by looking for strings in the filename like NTSC, SECAM, PAL60 etc.

I hope that's still on some to do list somewhere because that was a great idea.

I just created a Stella issue for this.

 

2 hours ago, Lillapojkenpåön said:

Why aren't we converting the players to?

???

  • Like 1
Link to comment
Share on other sites

7 hours ago, Thomas Jentzsch said:

???

 

I forgot to use ColorConvert for the game screen, so instead of the blue and green accent colors seen for the players in NTSC:

1825472544_ScreenShot2020-01-20at10_14_45AM.thumb.png.67dddd38f27d3541a827071671236d14.png

 

 

the players looked like this for PAL:

279618581_ScreenShot2020-01-20at10_09_11AM.thumb.png.0824a2ddd434417f5fca17df1ad6881c.png

 

and this for SECAM:

1104967754_ScreenShot2020-01-20at10_09_58AM.thumb.png.bea641963ba4660d52cd35308a6e2c66.png

 

I've revised the routines, replaced the source and ROM, and added some screenshots of the players on the game screen.

  • Thanks 1
Link to comment
Share on other sites

Now that I'm starting to get how DPC+ and CDFJ works I thought of a thing, if you have both player 0 and player 1 virtual sprites, and two player 1's get to close, couldn't you put one in player 0's buffer (if that position is free) and position in that stream? A fluid system where the buffers and GRP0 and GRP1 are used to draw as much as possible before flickering? That is probably what everybody is doing allready now that I think of it ?

 

 

Link to comment
Share on other sites

Correct, the virtual sprites are not tied to a specific player. Blog entry It's full of stars! goes into some detail on how its done.

 

If you play Draconian in Stella with Fixed Debug Colors mode turned on you can see it in action.  In this sequence of frames I drew a white box in an area that contains a mine, the player's ship, and an asteroid.  Over 3 frames those objects are drawn like this:

 

player0 - asteroid

player1 - player's ship

draconian_20171013_RC6_dbg_cdb94f1c.thumb.png.99a4e4255869974ceb4f38b030a59ee0.png

 

 

player0 - mine

player1 - asteroid

draconian_20171013_RC6_dbg_cdb95e8a.thumb.png.e201cdf10d4efe20c8d0070907ff4276.png

 

player0 - player's ship

player1 - mine

 

draconian_20171013_RC6_dbg_cdb96fcb.thumb.png.a029133319a654d80bbc72ad7e2c04d5.png

 

 

  • Thanks 1
Link to comment
Share on other sites

Thanks Darrell, this NTSC/PAL/SECAM detection is really useful!

In your games that implement this detection technique (e.g. Draconian) I see that you still offer the option to change the TV type on the menu screen (with the detected TV type pre-selected).

Is this because the detection isn't 100%, or just to give people the option to change it if they want to?

Edited by Dionoid
  • Like 1
Link to comment
Share on other sites

1 hour ago, Dionoid said:

Is this because the detection isn't 100%, or just to give people the option to change it if they want to?

 

The option was because Draconian was the first time we used it in a released game, and I didn't know if the detection was 100% or not. Would probably be worth doing a poll at some point to see how well it's worked for people.

 

Instead of a menu option, an override could be implemented by checking the joystick state when the 2600's powered up:

  • FIRE for NTSC
  • UP for PAL60
  • DOWN for SECAM60

 

To do that I'd change SplashVerticalBlank() to this:

void SplashVerticalBlank()
{

    ...
      
    else if (frame == 2)
    {
        T1TCR = 0;          // turn off timer after 1 frame
        
        // first check if the user overrode the detection
        // if they did not then use the value in T1TC to see
        // how long it took the 2600 to draw 262 scanlines
        if (JOY0_FIRE)
            tv_type = NTSC;
        else if (JOY0_UP)
            tv_type = PAL;
        else if (JOY0_DOWN)
            tv_type = SECAM;
        else if (T1TC < (0x11e8ff + 0x11d329)/2)
            tv_type = NTSC;
        else if (T1TC > (0x11fd2b + 0x11e8ff)/2)
            tv_type = PAL;
        else
            tv_type = SECAM;
    }    
  
    ...

  }

 

Link to comment
Share on other sites

the 2600 must output 262 scanlines for those times to be valid.

 

If you look at the 6507 code before the ARM runs:

VerticalSync:
        ldy #2
        ldx #VB_TIM64T
        sty WSYNC
        sty VSYNC
        stx TIM64T
        sty WSYNC
        sty WSYNC
        ldy #0          ; 2  2 - zero out some TIA registers while
        sty GRP0        ; 3  5   we have some free time
        sty GRP1        ; 3  8
        sty WSYNC
        sty VSYNC
            
        ; figure out which ARM Vertical Blank routine to run
        lda Mode            ; $00 = splash, $01 = menu, $80 = game
        bmi vbgame
        beq vbsplash
        ldy #_FN_MENU_VB     ; going to run function MenuVerticalBlank()
        .byte $0c           ; NOP ABSOLUTE, skips over ldy #_FN_SPLASH_VB 
        
vbsplash:
        ldy #_FN_SPLASH_VB  ; going to run function SplashVerticalBlank()
        .byte $0c           ; NOP ABSOLUTE, skips over ldy #_FN_GAME_VB
vbgame:        
        ldy #_FN_GAME_VB    ; going to run function GameVerticalBlank()
        jsr CallArmCode

 

you'll see that both frame 1 and frame 2 will follow the exact same path, thus take the same number of 6507 cycles, resulting in exactly 262 scanlines worth of time transpiring between ARM calls.  You can add more 6507 code in there as long as the path taken for frame 1 and frame 2 is identical.

 

Likewise in the C code both frames 1 and 2 will take the same path until they get to the IF/ELSE IF block of code in SplashVerticalBlank().

 

void SplashVerticalBlank()
{
    int i;
    int j;
    int color;
    int console;
    
    color = 0;  // default to black

    // used to show if the console is a 2600 or 7800
    console = ((is_7800 ? _SPLASH_78 : _SPLASH_26 ) & 0xfff) + 0x6000;
    
    if (frame == 1)         // frame is incremented in SplashOverScan()
    {
        T1TC = 0;           // make sure timer starts at 0
        T1TCR = 1;          // turn on timer
    }
    else if (frame == 2)
    {
        T1TCR = 0;          // turn off timer after 1 frame
        
        // the time it takes to output 262 scanlines is different for
        // NTSC, PAL, and SECAM consoles, so we can use that to detect
        // which one and adjust the color values.
        if (T1TC < (0x11e8ff + 0x11d329)/2)
            tv_type = NTSC;
        else if (T1TC > (0x11fd2b + 0x11e8ff)/2)
            tv_type = PAL;
        else
            tv_type = SECAM;
    }

 

The value in T1TC stops changing as soon as T1TCR is set to 0, so the extra lines of code added for joystick override will have no impact on the values being compared against.

  • Thanks 1
Link to comment
Share on other sites

  • 2 years later...
On 1/20/2020 at 12:43 AM, SpiceWare said:

Expected times for each system are:

  • 11d329 for NTSC
  • 11e8ff for SECAM
  • 11fd2b for PAL 

These are the only the 6507 cycles, and do not include the extra cycles which the Thumb code takes after starting (~2250), between the two updates (~300)  and before stopping the counter (~100), right?

 

Since the emulators may or may not count the Thumb cycles, it might be a good idea to:

  • reduce the Thumbs cycles (especially after starting the counter)
  • adjusting the test values

I am not 100% sure, but it seems that the auto detect current only works on real hardware because the gaps between NTSC, SECAM and PAL are large enough (~5400 cycles).

Link to comment
Share on other sites

8 hours ago, Thomas Jentzsch said:

it seems that the auto detect current only works on real hardware because the gaps between NTSC, SECAM and PAL are large enough (~5400 cycles)

 

We did tests on real hardware to validate the ARM timer values.

  • theoretical 11d329 for NTSC, saw 11d311 thru 11d32e
  • theoretical 11e8ff for SECAM, saw 11e8f7 thru 11e8fd
  • theoretical 11fd2b for PAL, saw 11fd00 thru 11fd35

Also conducted a poll in 2020 to see if anybody had incorrect detection.

Link to comment
Share on other sites

I remember that and your values are correct. But I think the emulation has some flaws. We are adding fixed numbers of 6507 cycles to the counter. These cycles assume that the Thumb code executes in no time. I think we have to differentiate here when we emulate Thumb cycles.

  • Like 1
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...