Jump to content
IGNORED

understanding fine motion from the book


SavedByZero

Recommended Posts

Unfortunate that while those RESMPx registers are allowed to collect positions of the parent GRPx sprites, a programmer is not afforded the same benefit.

 

There was a message BITD that the value used for RESMPx must not use any other bit than 1 to avoid side-effects.  Is this a fact?  Anything exploitable?

Link to comment
Share on other sites

On 8/16/2019 at 11:19 PM, Nukey Shay said:

Unfortunate that while those RESMPx registers are allowed to collect positions of the parent GRPx sprites, a programmer is not afforded the same benefit.

 

[Edited a bit since I originally misread Nukey Shay's point...]

 

I initially figured there was something that kept track of the constantly-moving horizontal position, and that strobing RESPx caused that current position value to be stored somewhere. Subsequent scanlines would then just refer to the "frozen" stored position to know whether or not to start drawing. RESMPx would just read in the value from the player and copy it over to the missile.

 

But, as I'm learning, horizontal positions aren't actually kept track of anywhere in a traditional sense. Strobing the reset registers merely resets the counters that control when an object's display begins (not where it begins, technically... although "when" and "where" are basically the same thing from the TIA's perspective).

 

These object position counters just cycle through a period of time equal to 160 horizontal pixels, over and over again. Whenever the beginning of the cycle comes around again, it triggers the start of object display, similar to how a clock striking midnight triggers the chimes to go off. The counters are independent of each other, of course, so each can have their own local time and thus their own personal midnight. 

 

The strobing any of the object position reset registers (with RESPx, RESMx, or RESBL) doesn't change the cycle state at which the trigger happens. That's always "midnight" as far as the counter/clock is concerned. The strobe just adjusts the hands to make them immediately "point to midnight" right there and then. That's how I've been thinking about it, anyway.

 

Which makes sense, I guess. If you wanted a grandfather clock to remind you to take a roast out of the oven at 6:15, you wouldn't modify the internal workings to get the chimes to go off at 6:15. Much easier to just adjust the clock hands to a time that, while incorrect compared to what the actual time is, will nonetheless make it strike midnight when your roast is ready.

 

Anyway, as I understand it, RESMPx doesn't access the state of the player position counters or directly set the missile position counter. All it does it set some flag that effectively causes a RESMx to occur in the middle of the next player x drawing routine. So yeah, the RESMPx does sort of "collect" the position in a sense, but at no point does any of the logic genuinely "know" where any of the objects are, does it?

 

 

Edited by JeffJetton
Link to comment
Share on other sites

So I've done a wee bit of research, including studying Andrew Towers' amazing TIA Hardware Notes, writing some experimentation programs, checking out the TIA schematics, poking around the Stella source code, and even simulating the TIA polynomial counters in Python to get a better understanding of how they work (I put it up on GitHub if anyone's interested).

 

What I found out is probably not news to most of you, but I figured I'd report back anyway for any future fellow newbies who stumble onto this thread.

 

Here's why my math was off in my earlier post:

 

As DEBRO pointed out, I had my fine adjustment backwards. $C0, which can be thought of as -4 in the high nibble, is a shift of 4 pixels to the right, not left. But as it turns out, HMOVE does not add another 8 pixels. More on that interesting fact later...

 

That gets us to position 35. How do we get the rest of the way over to 40?

 

Well, Karl G is indeed correct that you have to allow another 5 pixels for internal TIA timing reasons (only 4 pixels for missles/ball), which gets us to the desired 40-pixel position. Problem solved!

 

But... apparently those 5 pixels are not due to a delay in setting the position with RESPx, but rather a delay in displaying the player on each line. Per Andrew Towers: "Each START decode [of the position counter value] is delayed by 4 CLK in decoding." In other words, there's an inherent four-TIA-cycle delay between the horizontal movement counters hitting the "draw" trigger and that drawing actually taking place. Player objects (due to the added complexity of their bitmaps, I assume?) take another cycle on top of that, for a total delay of five cycles/pixels for them.

 

So the RESPx really does reset the position counter to zero at the exact spot on the scanline it finishes executing. There's no delay going on there. Left to its own devices, the counter will continue to hit zero at that same time (and thus same position) on every line.

 

This explains, by the way, why hitting RESPx during the horizontal blanking period--and not using HMOVE at all--still results in the player displaying five pixels from the left edge, even if you hit RESPx immediately after the WSYNC. (It's true! I've attached some source code... try it and see.)

 

If the delay was happening on the RESPx end, you should still have plenty of time for it to set the counter to zero. The position counter acts essentially like an unplugged clock during HBLANK, and you have 68 cycles to reset the hands of that clock back to midnight before it gets plugged back in for the visible scan line. So an early enough RESPx during HBLANK would give you a player smack-dab on the left-most pixel if display were immediate. The fact that that doesn't happen is a clue that the delay is in the display, not the reset itself.

 

 - Jeff
 


P.S. Oh, and I realized that, if there really was a inherent shift of 4/5 pixels going on in the TIA, I should be able to find evidence of it in the Stella source code. Sure enough, if you look in Player.cxx (lines 364-391) you can see that the functions that get and set player position take a "renderCounterOffset" into account. Its value is defined in the header file:

  private:
    enum Count: Int8 {
      renderCounterOffset = -5,
    };

Same thing in the Missle.cxx/hxx and Ball.cxx/hxx files, although the renderCounterOffset is defined there as -4.

 

BTW, these values wind up getting subtracted in the calculations. Since they're defined as negative, they effectively add to the horizontal position (i.e., move it to the right).

 

resptest.asm

Edited by JeffJetton
Link to comment
Share on other sites

I promised (threatened?) more about HMOVE. Here we go...

 

HMOVE really does delay the end of horizontal blanking by eight color clocks, which delays the point at which the clock driving the position counters is "plugged back in". So wouldn't that mean that all the objects would indeed be shifted to the right by eight pixels? Why can we get away with ignoring that when calculating the horizontal position?

 

As it turns out, before HMOVE delays the start of the clock, it goes through a process that essentially bumps the counters up by anywhere from zero to 15 cycles/pixels, depending on the values found in the horizontal motion registers (HMxx).

 

To continue the clock analogy, while HMOVE really does keep the clock unplugged another eight ticks, it also potentially "manually" moves the hands forward by some amount while they're still dormant. Those two things work together to determine the amount of time by which the clock winds up being "off" once it's plugged back in.

 

The position counters are never "subtracted from" by moving them backwards. In fact, due to the way the counters are designed, I don't think there even is an efficient way to reverse them. They're strictly forward-only circuits.

 

It's the combination of a variable forward shift (variable left movement between 0-15 clocks) with a non-variable start delay (fixed move right by 8 clocks) that results in a final move capability of up to 7 left or 8 right.

 

Which means that the fine-positioning chart in the Stella Programmer's Guide is a dirty lie! Or at least a bit misleading. ? Per Andrew Towers:

Quote

In an odd twist, this was done purely for the convenience of the programmer! The [leftmost bit of each HMxx register] is wired up in reverse, costing nothing in silicon and effectively inverting this bit so that the value can be treated as a simple 0-15 count for movement left.

 

So...

Stella Prog Guide Chart           As Actually Seen by the TIA
------------------------------    -------------------------------
D7  D6  D5  D4  Clocks            D7  D6  D5  D4  Clocks
0   1   1   1   +7  Move left     1   1   1   1   +15
0   1   1   0   +6                1   1   1   0   +14
0   1   0   1   +5                1   1   0   1   +13
0   1   0   0   +4                1   1   0   0   +12
0   0   1   1   +3                1   0   1   1   +11
0   0   1   0   +2                1   0   1   0   +10
0   0   0   1   +1                1   0   0   1    +9
0   0   0   0    0  No Motion     1   0   0   0    +8
1   1   1   1   -1  Move right    0   1   1   1    +7
1   1   1   0   -2                0   1   1   0    +6 
1   1   0   1   -3                0   1   0   1    +5 
1   1   0   0   -4                0   1   0   0    +4 
1   0   1   1   -5                0   0   1   1    +3 
1   0   1   0   -6                0   0   1   0    +2
1   0   0   1   -7                0   0   0   1    +1 
1   0   0   0   -8                0   0   0   0     0

Notice how the "real" value for "no motion" is +8. That cancels out exactly the 8 color clock delay, thus no motion!

 

 - Jeff

 

P.S. I've attached a portion of the TIA schematic that shows the HMxx circuit detail. I've annotated it and boxed in the area that is different between bits 4-6 and bit 7. Even if you're not a hardware engineer (I sure as heck ain't), it's pretty obvious that bit 7 is indeed wired "the other way around".

 


 

tia_schematic_cropped.png

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

On 8/7/2019 at 10:23 AM, SpiceWare said:

 

In my Collect tutorial I recommend checking out the Easy 6502 tutorial for those who aren't familiar with 6502 Assembly. 

 

I need to update my tutorial, all the code snippets ended up on 1 line after the forum upgrade. Should still be able to download the source to follow along.

Nice, though the zero page addressing doesn't work on my end.  I would expect this to color the screen:

  LDA #$01
  LDX #$01
  STA $02,X;

however, unless I use direct addressing (STA $0201), I don't see the color appear. 

Link to comment
Share on other sites

41 minutes ago, SavedByZero said:

Nice, though the zero page addressing doesn't work on my end.  I would expect this to color the screen:


  LDA #$01
  LDX #$01
  STA $02,X;

however, unless I use direct addressing (STA $0201), I don't see the color appear. 

 

If I understand your question correctly...

 

When the tutorial says that "a zero page address is given, and then the value of the X register is added", it doesn't mean added as in "tacked on to the end". It means added mathematically. The address value is summed with the contents of X.

 

So STA $02, X would be equivalent to STA ($02 + $01) or STA $03.

 

Which, in turn, is equivalent to STA $0003.

 

 

 

 

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

1 hour ago, JeffJetton said:

 

If I understand your question correctly...

 

When the tutorial says that "a zero page address is given, and then the value of the X register is added", it doesn't mean added as in "tacked on to the end". It means added mathematically. The address value is summed with the contents of X.

 

So STA $02, X would be equivalent to STA ($02 + $01) or STA $03.

 

Which, in turn, is equivalent to STA $0003.

 

 

 

 

Oh I see.  I think it was this sequence of explanaitons that confused me:

 

Absolute: $c000

With absolute addressing, the full memory location is used as the argument to the instruction. For example:

STA $c000 ;Store the value in the accumulator at memory location $c000

Zero page: $c0

 

Made me think that $c0 in zero page would be equivalent to $c000 in absolute.  Sorry,

Link to comment
Share on other sites

Okay, DigitalAnthony pointed out that, while using RESP0 alone won't get you right on the left edge, the difference isn't the 5 pixels as I described above, but rather only 3.

 

Well heck, he's right. At least in Stella and Javatari.

 

I've attached a slightly-improved version of my test program. I'm putting in an HMOVE, just to be more complete, although it's set up for "zero" and shouldn't change the coarse positioning. I also changed the way the player is drawn, to make it easier to see where it sits.

 

Anyway, here are my recent findings:

 

Sleep Cycles   After RESP0   Color Clocks   Trigger Pos Est.   Draw Pos   Emu Actual
           0             3              9                  0          5            3
           5             8             24                  0          5            3
          10            13             39                  0          5            3
          15            18             54                  0          5            3
          17            20             60                  0          5            3
          18            21             63                  0          5            3
          19            22             66                  0          5            3
          20            23             69                  1          6            6
          21            24             72                  4          9            9
          22            25             75                  7         12           12

 

So if you do 20 cycles of SLEEP or more, the player lands just where the math says it would. For example, at 20 cycles, you're at cycle 23 after the RESP0. That's 69 color clocks, or at pixel 1 (the 2nd pixel from the left). Take the 5-pixel draw delay into account, and you should be at x-position 6 (7th pixel over), which is what actually happens.

 

(Yeah, the HMOVE bumps it to the right by 8 pixels, but then the clock stuffing of 8 clocks cancels it out exactly, so you're right back where you started.)

 

But a SLEEP of 19, which I'm thinking should put you at position 5 really puts you at 3. And every value lower than that also puts you at 3. So why is there a delay of only 3 color clocks when RESP0 is hit during HBLANK, but a delay of 5 color clocks when it's not?

 

Is this the way the real hardware would work? Or is this a case of the emulator not getting it quite right (and no one really caring because, really, what game would actually rely only on coarse positioning to put an object on the far left anyway? It's an admittedly weird example.)

resptest2.asm

Link to comment
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.
Note: Your post will require moderator approval before it will be visible.

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