Re: [stella] TIA latch timing

Subject: Re: [stella] TIA latch timing
From: Kevin Horton <khorton@xxxxxxxxxxx>
Date: Thu, 18 Oct 2001 04:00:04 -0500
At 08:31 10/17/01 -0400, you wrote:

Thanks Kevin, I was wondering how tthe PF would work under the covers to
provide the results Glen was describing.  Do the player graphics work in
the same way?  It looks like HBLANK time is gold since it's the only time
some of these registers can be updated safely.

You refer to a schematic of the PF, where can I find this?  Does it include
other parts of the TIA?

I have the complete TIA schematic. It's on 5 sheets of paper so it's not conducive to posting yet. Was thinking about scanning it or taking pictures with my digicam. I've been adding "notes" to the schematic as I go, commenting what certain parts do.

As for the player graphics, they are done differently. They have a 3 bit binary counter (haven't checked to see if it's an up or down counter) and use the Q outputs (non-inverted) and /Q (inverted) outputs and run 'em thru a selector. The reflect player bit controls if the Q or /Q outputs get passed. The Q outputs scan from 0 thru 7 while the /Q outputs scan from 7 to 0. This inverting trick to count backwards/forwards with 1 counter is useful elsewhere. I've used it a number of times to make "oscillating" counters that count up from 0 to 127, then 127 back to 0, then 0 to 127, etc. See end for this.

These then run through an AND matrix which decodes the 8 states to select a single bit from the player registers.

Here is where it gets really tricky! This is possibly new information on how the players work. Each player register is really a *two byte* register, instead of a single byte register.

The two bytes are termed "new" graphics data and "old" graphics data. The bytes are "stacked" side by side, and act as a 2 byte deep buffer. When the graphics data is written to, it goes into the "new" register. When you write to the OTHER player graphics data register, it latches the contents of the "new" graphics register into the "old" graphics register.

i.e. If I write to P0GF, the contents go into the "new" register of P0GF. Whatever happened to be on P1GF's "new" register will be latched in P1GF's "old" register.

It works both ways. Likewise, if I write a byte into P1GF, it will go into P1GF's "new" register (P1GF's "old" register is not modified at all during this). Then P0GF's "new" register contents get latched into P0GF's "old" register. That is what makes the 6 sprite per line trick possible.

The player vertical delay selects the "new" or "old" graphics register for display.

The rest of the position circuitry is quite complex, and at the heart is an LFSR with some decoded states. Remember that LFSR's were used in place of normal binary counters, since they required less logic/transistors, and they were truly syncronous (i.e. all outputs changed at the exact same time; no "ripple delay" from chained binary counters (called a "ripple counter" due to the "rippling" effect of the carry propagation down the entire chain).).


Oscillating counter code example:

do_osc: inc osc_ctr   ;inc our counter
        lda osc_ctr   ;get oscillator counter
        bpl no_inv    ;if 0-127, don't invert
        eor #0ffh     ;invert every bit including D7 :-)
no_inv: rts   ;end of code

"osc_ctr" counts from 0-255d. However, the accumulator will only return 0-127d. It does so in an "oscillating" fashion, however. 0-127, 127-0, 0-127, etc. The XOR 0ffh inverts all the bits. D7 is always set when this inversion occurs, so making sure our XOR has its 7th bit set will ensure D7 remains clear after the XOR (1 XOR 1 = 0). The remaining 7 bits then get flipped around and returned.

- Archives (includes files) at Unsub & more at

Current Thread