Jump to content
rbairos

pixel clock vs scan cycle start phase

Recommended Posts

Hello, I know the TIA runs three steps for every one cycle of the 6507, but my question is:
At the start of each line, does pixel 0 always coincide with the first cycle of an instruction?

Trying to force a counter example, but having difficulties.

Specifically looking at the stella code:

void TIA::onHalt()
{
  mySubClock += (TIAConstants::H_CLOCKS - myHctr) % TIAConstants::H_CLOCKS;
  mySystem->incrementCycles(mySubClock / TIAConstants::CYCLE_CLOCKS);
  mySubClock %= TIAConstants::CYCLE_CLOCKS;
}

I can never get mySubClock to exit that function with a non-zero value.
That is, myHctr is always an example multiple of 3?


Thanks,
Ro.

 

Share this post


Link to post
Share on other sites

I don't know the intricacies but since the 6507 clock is derived from the TIA clock via a divide-by-3, I would assume that the circuitry simply only sends a new 6507 clock "tick" whenever the TIA hits a "0" so to speak. That would be my total guess.

 

Share this post


Link to post
Share on other sites

Thanks, that what I assume as well.
I'll drop a note with the Stella people for clarification.
Cheers,
Rob.

Share this post


Link to post
Share on other sites
57 minutes ago, rbairos said:

At the start of each line, does pixel 0 always coincide with the first cycle of an instruction?

I think that should read:

At the start of each line, does pixel 0 always coincide with the start of a  cpu cycle? (though it could be the start of cycle 2 or cycle 3 etc)


 

Share this post


Link to post
Share on other sites

(Short answer for anyone interested:  Most likely pixel clock and cycle are always aligned for the 2600, but 6502 emulator was left more general for historical reasons.)

  • Like 1

Share this post


Link to post
Share on other sites

The long explanation that I just gave via PM:
 

The reason for the presence of "subClock" is mostly historical. 6502.ts (from which I ported the TIA core) works differently from Stella in that it has a loop at its center that runs once per color clocks and sends "tick" events to all emulated hardware on each iteration. Stella works differently: the CPU runs for a bunch of cycles (usually until a new frame is generated), increments the system clock counter on every memory access and at certain "barriers", the emulated hardware catches up. This synchronization is not correlated with scanlines --- it is triggered before a register is accessed, and at various other points.

 

I implemented this "catching up" by spinning the new TIA core for the necessary number of cycles in a loop. As the granularity of the system clock is more coarse than that of the color clock, I added "subClock" in order to track any remainder. I suspected that there were no legal ways to actually enter or leave Tia::update with a nonzero subClock, but I was not sure. Now, I know that (at least with the current emulation), this is impossible, and I know no indication that it is possible to alter this "phase" between TIA and CPU on hardware.

 

So, to answer your question, yes, in principle subClock could be removed. However, its presence keeps onHalt clear from any assumption from where in the emulation it is called, that's why I didn't remove it. If we were to remove it, I'd like to leave an assertion that this quantity is indeed zero.

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, DirtyHairy said:

The long explanation that I just gave via PM:
 

The reason for the presence of "subClock" is mostly historical. 6502.ts (from which I ported the TIA core) works differently from Stella in that it has a loop at its center that runs once per color clocks and sends "tick" events to all emulated hardware on each iteration. Stella works differently: the CPU runs for a bunch of cycles (usually until a new frame is generated), increments the system clock counter on every memory access and at certain "barriers", the emulated hardware catches up. This synchronization is not correlated with scanlines --- it is triggered before a register is accessed, and at various other points.

 

I implemented this "catching up" by spinning the new TIA core for the necessary number of cycles in a loop. As the granularity of the system clock is more coarse than that of the color clock, I added "subClock" in order to track any remainder. I suspected that there were no legal ways to actually enter or leave Tia::update with a nonzero subClock, but I was not sure. Now, I know that (at least with the current emulation), this is impossible, and I know no indication that it is possible to alter this "phase" between TIA and CPU on hardware.

 

So, to answer your question, yes, in principle subClock could be removed. However, its presence keeps onHalt clear from any assumption from where in the emulation it is called, that's why I didn't remove it. If we were to remove it, I'd like to leave an assertion that this quantity is indeed zero.

It's interesting that Stella can let the CPU run for many cycles before the rest of the VCS "catches up". Am I reading that correctly? The approach I've taken in gopher2600 is a very strict three TIA ticks for every CPU tick, as and when the CPU tick happens. I'm a little surprised it can work any other way TBH. Are there any advantages to Stella's approach?

  • Like 1

Share this post


Link to post
Share on other sites
16 minutes ago, JetSetIlly said:

It's interesting that Stella can let the CPU run for many cycles before the rest of the VCS "catches up". Am I reading that correctly? The approach I've taken in gopher2600 is a very strict three TIA ticks for every CPU tick, as and when the CPU tick happens. I'm a little surprised it can work any other way TBH. Are there any advantages to Stella's approach?

Yes, that's correct. For every frame, Stella cycles the CPU until a frame is generated or a predefined maximum of instructions has been emulated. On every memory access, the system clock is incremented. Whenever a TIA register is read (or one of several other conditions is met), the TIA emulation runs and propagates the hardware state to the present. The same holds true for the RIOT.

 

6502.ts works differently, too, it has an inner loop that spins at the color clock and ticks the hardware on every iteration.

 

I think the approach taken in Stella has the potential for better performance (there's less dispatch overhead, and cache locality should be better), and it makes emulating the exact access sequence of the CPU much simpler --- the code basically flows linearly through the steps taken by the CPU for a given instruction, while I have a rather complicated state machine in 6502.ts that tracks the execution state of each individual instruction and advances by one step every three ticks. On the other hand, it needs some care to make sure the hardware is always up to date when necessary, and we have tripped over this more than once since I joined the project a few years ago. Especially the interaction with the debugger is a little tricky.

Edited by DirtyHairy
  • Like 1

Share this post


Link to post
Share on other sites

Stella originally ran on a fast 486, so these optimizations were absolutely necessary back then.  While they may not be as necessary now, I wouldn't want to change it; it would essentially mean rewriting large parts of the code for potentially slower emulation.  Not worth it at this point.

  • Like 1

Share this post


Link to post
Share on other sites
12 hours ago, DirtyHairy said:

I know no indication that it is possible to alter this "phase" between TIA and CPU on hardware.

It is theoretically possible in a 7800, but the bios takes care of it:

 

In the 7800, the cpu runs at 1.79 MHz in 7800 mode and 1.19 MHz in 2600 mode, while the TIA and RIOT are always clocked at 1.19 MHz (and the cpu slows down the clock when accessing them in 7800 mode). Because of this, when switching to 2600 mode, the cpu and TIA might end up out of phase.

 

When the 7800 bios detects a 2600 cart in the slot, a routine is executed from  RIOT ram (therefore at 1.19 Mhz, no matter which mode the console is in) that checks the phase using sprite collisions. If necessary, the phase is adjusted by putting the console in 7800 mode and doing memory accesses outside the RIOT ram, which cause temporary CPU clock speed-ups. Only then it locks in 2600 mode and jumps to the cart reset vector.

 

For anyone interested, the routine is in the file "MARIAOS.S", lines 277 to 378, in the archive "7800 Base OS - NTSC" that you can find here:
http://www.atarimuseum.com/videogames/consoles/7800/games/

  • Like 3

Share this post


Link to post
Share on other sites
13 hours ago, alex_79 said:

It is theoretically possible in a 7800, but the bios takes care of it:

Interesting. I have a latent desire to look at 7800 emulation one day, so I have stumbled over the possibility of a phase shift between CPU and TIA before. If I ever find time to actually work on this, I may very well come back with a few questions 😏

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   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...