Jump to content
  • entries
    657
  • comments
    2,692
  • views
    898,349

Jitter 3, Recovery Time


SpiceWare

2,394 views

Last time around I added jitter emulation to Stella, which became part of the official build with version 4.6.5. The jitter emulation basically moves the screen up/down in response to inconsistent scanline counts, similar to that which occurs on a real TV. I added it because people without an Atari would often write games with variable scanline counts, but not realize it until others play-tested the game on real hardware.

One thing it didn't emulate was a recovery time - if the difference in scanline counts is large a real TV will take multiple frames to recover. Instead, the original implementation would recover in just a single frame - blink and you might miss it.

TIA.hxx
Added a variable for recovery time:
 

// large jitter values will take multiple frames to recover from
Int32 myJitterRecovery;
 


TIA.cxx
Added a constant for the threshold for triggering recovery time. I went with 5, but it can easily be changed.

 

 

 

#define JITTER_RECOVERY 5
 


Initialize the new variable:

 

void TIA::initialize()
{
    ...
    myNextFrameJitter = myCurrentFrameJitter = myJitterRecovery = 0;
    ...
}
 


Update the jitter logic to use the new variable and constant:

 

inline void TIA::endFrame()
{
    ...
    // Account for frame jitter, skipping the first few frames
    if(myJitterEnabled && myFrameCounter > 3)
    {
        // Set the jitter amount for the current frame
        myCurrentFrameJitter = (myNextFrameJitter + myJitterRecovery*JITTER_RECOVERY)* 160;
        if (myJitterRecovery < 0)
            myJitterRecovery++;
        else if (myJitterRecovery > 0)
            myJitterRecovery--;
            
        // Calculate the jitter amount for the next frame.
        // Jitter amount of a frame depends upon the difference
        // between the scanline counts of the prior two frames.
        myNextFrameJitter = myScanlineCountForLastFrame - previousCount;
        if(myNextFrameJitter < -1)
        {
              if (myNextFrameJitter/JITTER_RECOVERY < myJitterRecovery)
              {
                myJitterRecovery = myNextFrameJitter/JITTER_RECOVERY;
                myNextFrameJitter = 0;
                // Make sure currentFrameBuffer() doesn't return a pointer that
                // results in memory being accessed outside of the 160*320 bytes
                // allocated for the frame buffer
                if(myJitterRecovery*JITTER_RECOVERY < -Int32(myFrameYStart))
                  myJitterRecovery = myFrameYStart/JITTER_RECOVERY;
              }
          else
          {
            myNextFrameJitter = (myNextFrameJitter-1) / 2;
            // Make sure currentFrameBuffer() doesn't return a pointer that
            // results in memory being accessed outside of the 160*320 bytes
            // allocated for the frame buffer
            if(myNextFrameJitter + myJitterRecovery*JITTER_RECOVERY < -Int32(myFrameYStart))
              myNextFrameJitter = myFrameYStart;
          }
        }
      else if(myNextFrameJitter > 1)
      {
        if (myNextFrameJitter/JITTER_RECOVERY > myJitterRecovery)
        {
          myJitterRecovery = myNextFrameJitter/JITTER_RECOVERY;
          myNextFrameJitter = 0;
          // Make sure currentFrameBuffer() doesn't return a pointer that
          // results in memory being accessed outside of the 160*320 bytes
          // allocated for the frame buffer
          if(myJitterRecovery*JITTER_RECOVERY > 320 - Int32(myFrameYStart) - Int32(myFrameHeight))
            myJitterRecovery = (320 - myFrameYStart - myFrameHeight)/JITTER_RECOVERY;
        }
        else
        {
          myNextFrameJitter = (myNextFrameJitter+1) / 2;
          // Make sure currentFrameBuffer() doesn't return a pointer that
          // results in memory being accessed outside of the 160*320 bytes
          // allocated for the frame buffer
          if(myNextFrameJitter + myJitterRecovery*JITTER_RECOVERY > 320 - Int32(myFrameYStart) - Int32(myFrameHeight))
            myNextFrameJitter = 320 - myFrameYStart - myFrameHeight;
        }
      }
      else
        myNextFrameJitter = 0;
    }
  ...
  }
 

 


How to Test
To test it, I built a new program called Jitter. The rainbow section defaults to 128 scanlines:
blogentry-3056-0-16380200-1454164533_thumb.png

Use joystick up/down/left/right to change the displayed count:
blogentry-3056-0-73225800-1454164541_thumb.png

Hold down FIRE to activate that count:
blogentry-3056-0-20429900-1454164549_thumb.png

Release FIRE and the rainbow section will revert back to 128 scanlines. The displayed count will stay whatever you set it to, it only becomes active when FIRE is held. Hit GAME RESET if you wish to reset the count back to 128.

Additionally you can use the TV Type switch to specify NTSC(Color) or PAL(B&W) screen size. If you change this be sure to use CONTROL-F to set Stella to display using the correct TV standard.

Test build of Stella for OS X. I changed the version to display 4.7 dgs so you can easily compare the difference with the release version 4.7:

Stella Jitter Improvement.app.zip

 

 

The above build uses 5 for JITTER_RECOVERY, this build uses 10:
Stella Jitter Improvement 10.app.zip

 

I'm not set up to build Stella for other platforms, so here's the source changes for the TIA files. Not included is the version, you can change that in common/Version.hxx:
TIA source.zip

 


Jitter ROM:
jitter.bin

 

Jitter Source:
Jitter.zip

If this looks good to y'all I'll submit the changes to stephena.

  • Like 4

8 Comments


Recommended Comments

I think it looks good, but the jitter recovery time definitely needs to be variable, and able to be set within the emulation. I'll see about getting a few Windows builds, one with a recovery time of 5 and another of 10.

Link to comment

I can take care of making it variable, since it requires changes in a few places (UI, extra methods in TIA class, documentation, etc). If you can just make sure that the jitter recovery code itself is working to your satisfaction, I will integrate it and take care of the rest.

Link to comment

If you can just make sure that the jitter recovery code itself is working to your satisfaction, I will integrate it and take care of the rest.

It works well enough for me. Do you need me to send the diff, or are the TIA files you downloaded from here good enough?

 

Thanks!

Link to comment

OK, I'm about to add code to the GUI to configure this dynamically. Problem is, I don't really know what to call it. I plan to have a range from 1 - 20, with 10 as the default. But 1 results in slower movement, and 20 in faster 'movement'. So what should we call this: jitter_threshold, jitter_recovery, jitter_delay, etc? Ideally I'd like to name the config variable to actually describe what it is accomplishing.

Link to comment

Hmm, the one value changes all of those things :ponder:

 

I'd probably go with jitter_recovery_factor or jitter_recovery if you need it to be shorter.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

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