Jump to content
IGNORED

Can somebody explain the basics of the reset strobes and horizontal motion?


Recommended Posts

Hey,

 

I'm finally sitting down to figure out the intricacies of horizontal motion in VCS kernels.

 

I could use a little chalk talk on the intricacies of the various reset strobes, and their interaction with HMOVE. I am currently studying the Combat and Video Olympics source to figure out where things meet, and I see the stack smashing trick, very clever way to check and set values in a constant # of cycles....

 

I know my way around 6502 assembly, but the whole line oriented approach is tripping my brain up. :)

 

-Thom

Link to comment
Share on other sites

MiniDig - best of stella has lots of useful info collected from the old email discussion group. While it's defunct, you can access everything from the archive.

 

Also check my tutorial Collect, which covers the development of a 2K game from scratch. I put way more comments in the source than I normally would.

  • Like 1
Link to comment
Share on other sites

Thanks, Darrell. I'll take a look at that.

 

I've been using the disassemblies at the dig for a long time, to understand implementation of a lot of different kernel concepts...

 

I'm actually somewhat confused through, RESP0 and RESP1 in combat are only being set in one place, how is it delaying to the appropriate time to strobe the reset?

 

-Thom

Link to comment
Share on other sites

I'm actually somewhat confused through, RESP0 and RESP1 in combat are only being set in one place, how is it delaying to the appropriate time to strobe the reset?

I took a quick look and it appears that they're using RESP0 and RESP1 to set the players to a known position (the left and right sides of the screen) at the start of a game. After that, they only use HMP0 and HMP1 to move the players over successive frames during the game. Doing it that way means they're not keeping track of the X location of the players - smart really, you don't actually need to know it because you only care about collisions and TIA handles that for you. Since they don't know the X location, they just use the RESMP0 and RESMP1 to set the X position the missiles to the middle of the corresponding player. Not keeping track of the X locations means 4 bytes of RAM are "freed up" (2 for the players and 2 for the missiles).

 

 

From Collect, this is the subroutine (and extensive comments) used to position any object. It strobes the appropriate RESxx register and stores the proper value in the HMxx register.

;===============================================================================
; PosObject
;----------
; subroutine for setting the X position of any TIA object
; when called, set the following registers:
;   A - holds the X position of the object
;   X - holds which object to position
;       0 = player0
;       1 = player1
;       2 = missile0
;       3 = missile1
;       4 = ball
; the routine will set the coarse X position of the object, as well as the
; fine-tune register that will be used when HMOVE is used.
;
; Note: The X position differs based on the object, for player0 and player1
;       0 is the leftmost pixel while for missile0, missile1 and ball 1 is
;       the leftmost pixel:
;           players     - X range is 0-159
;           missiles    - X range is 1-160
;           ball        - X range is 1-160
; Note: Setting players to double or quad size will affect the position of
;       the players.
;===============================================================================
PosObject:
        sec
        sta WSYNC
DivideLoop
        sbc #15        ; 2  2 - each time thru this loop takes 5 cycles, which is 
        bcs DivideLoop ; 2  4 - the same amount of time it takes to draw 15 pixels
        eor #7         ; 2  6 - The EOR & ASL statements convert the remainder
        asl            ; 2  8 - of position/15 to the value needed to fine tune
        asl            ; 2 10 - the X position
        asl            ; 2 12
        asl            ; 2 14
        sta.wx HMP0,X  ; 5 19 - store fine tuning of X
        sta RESP0,X    ; 4 23 - set coarse X position of object
        rts            ; 6 29

This is the routine that calls the above for all 5 objects. It then strobes HMOVE so all the HMxx values are used to set the final positions.

;===============================================================================
; PositionObjects
; --------------
; Updates TIA for X position of all objects
; Updates Kernel variables for Y position of all objects
;===============================================================================
PositionObjects:
        ldx #4              ; position all objects
POloop        
        lda ObjectX,x       ; get the object's X position
        jsr PosObject       ; set coarse X position and fine-tune amount 
        dex                 ; DEcrement X
        bpl POloop          ; Branch PLus so we position all objects
        sta WSYNC           ; wait for end of scanline
        sta HMOVE           ; use fine-tune values to set final X positions

Is there enough time in a classic 2LK, to add both a vertically delayed ball, vertically delayed players, and two missiles?

Yep, Collect uses a 2LK and draws all 5 objects. It uses vertical delay on the players and ball when appropriate, as well as updates the playfield. Sadly there isn't a vertical delay feature for the missiles.

Link to comment
Share on other sites

oh yes, sorry, that's right. no delay on the missiles. derp.

 

This is basically teaching me that I didn't read the hardware manual close enough...

 

I thought the horizontal motion registers were only a single shot move +7 -7 from the initial reset point, after which, you needed to change where you reset the player. (Sort of similar to the 400/800 architecture, where the fine scroll registers only shifted things by 7 pixels in each direction, after which, you needed to either physically move graphic data, or change LMS pointers....anyway.)

 

Thanks a bunch! I continue onward.

 

(I am working on a game of Dodgeball for the VCS, which would have looked right at home with the original VCS launch titles, with two player shot balls, and one relentless bouncing ball which can be caught and reshot by the player, for a total of three potential balls that you must dodge, or lose points...catching a ball increases points...game is over when a player reaches 0)

 

-Thom

Link to comment
Share on other sites

I thought the horizontal motion registers were only a single shot move +7 -7 from the initial reset point, after which, you needed to change where you reset the player. (Sort of similar to the 400/800 architecture, where the fine scroll registers only shifted things by 7 pixels in each direction, after which, you needed to either physically move graphic data, or change LMS pointers....anyway.)

I'm not familiar with 400/800 but the 2600 doesn't work that way. Every time you strobe HMOVE the values in the HMxx registers are used to change the objects' X positions.

 

So basically, if I'm just using the basic players, missiles and ball in a 2LK, I don't really need to keep track of the X? I can just adjust the horizontal movement registers, as needed?

Correct.

Link to comment
Share on other sites

FWIW this is what happens inside the chip:

 

Each sprite has its own position counter that gets incremented at every visible pixel and wraps around to 0 at position 159. When it happens, a signal is generated to start drawing the sprite. Other signals are generated at positions 16, 32 and 64 and fed into the logic that generates 2nd/3rd copies of the sprite.

 

Under normal circumstances there are 160 visible pixels and so the counter overflows and the sprite is drawn at the same position on every scanline.

 

RESxx strobe resets the counter to 0 immediately. As there is no wrap around from 159, the sprite is not generated at the new position, but its 2nd/3rd copy will be drawn. Since 1 cpu cycle = 3pixels, it only works with 3 pixel accuracy.

 

When HMOVE is issued, 2 things happen:

1. A flag is set, causing subsequent horizontal blanking to be extended by 8 pixels, reducing the number of visible pixels to 152.

2. A sequence of N pulses (1 pulse every 4 pixel clocks) is generated and injected into the counter clock line. N is the value 0..15 that was written to HMxx register.

 

Now the way these pulses and regular counter clock pulses are mixed together causes some interesting side effects:

* If a pulse occurs during blanking interval, the counter is incremented by 1

* If a pulse occurs during visible portion, the clock misses a beat and the counter is NOT incremented by 1

So when you issue HMOVE immediately after WSYNC, the counter is first incremented by N (0..15) during the blank and then not incremented during the first 8 pixels, resulting in -8/+7 motion range, everything works as advertised.

 

If HMOVE is issued at other times, the pulses that occur during hblank are added, and pulses that occur during visible portion are subtracted from position counter, causing all sorts of weird effects depending on the timing. Plus, extending hblank by 8 pixels or may may not happen, causing extra 8 pixel shift. Also the sprites themselves may be weirdly stretched (sprite logic is driven by the same clock as the position counter).

 

HTH,

dmk

  • Like 2
Link to comment
Share on other sites

Am I remembering correctly that HMOVE also results in short black lines on the left side of the screen? dmk's explanation of extending hor blanking 8 pixels made me think back. Never knew the why.

 

Yep, HMOVE causes the black line. If you hit HMOVE at cycle 73 or 74 the short line goes away, but the values for the HMxx registers that used to adjust the X position by -7 to 8 will now change them by -15 to 0.

 

For more info on that, check out the the MiniDig tricks page. You'll find an entry called HMOVE Timing with a couple of links:

  • hmove.txt shows the fine tuning values for every cycle you can hit HMOVE on
  • Brad Mott takes you to the archived email message from the old Stella mailing list

At the bottom of the archived email are links that let you view that particular thread, which started because Brad was trying to figure out why He-Man's titlescreen was messed up in Stella.

  • Like 1
Link to comment
Share on other sites

The TIA hardware notes provide a detailed description of the motion circuits.

 

As far as I understand this, by feeding additional pulses to the position counter, sprites can only move to the left. To be able to move left and right, the (re)start of the position counter is delayed by 8 pixels, which also covers up the resulting gap on the left border with the black bar. What I don't understand is why this is done at all as the playfield could still be displayed :?

Link to comment
Share on other sites

  • 6 months later...

Somewhat related question - why do some games have lots of HMOVEs/bars (sometimes every other line) when they are seemingly only repositioning a handful of things on any given frame? Spider-Man is a good example that comes to mind.

 

I'm only about 80% through commenting my first disassembly, perhaps the answer lies in the last 20%. Or maybe there is no general answer, and it's just game-specific. Hmm?

Link to comment
Share on other sites

Somewhat related question - why do some games have lots of HMOVEs/bars (sometimes every other line) when they are seemingly only repositioning a handful of things on any given frame? Spider-Man is a good example that comes to mind.

 

Timing is very tight in the kernel. It's much quicker to regularly strobe HMOVE than to have additional logic that decides if HMOVE needs to be hit or not. Spider-Man hits it every other line so it can draw the web diagonally using a missile - without using HMOVE the web could only go directly up or down. That's the same reason it's hit every-other-line in Missile Command.

Link to comment
Share on other sites

 

Timing is very tight in the kernel. It's much quicker to regularly strobe HMOVE than to have additional logic that decides if HMOVE needs to be hit. Spider-Man hits it every other line so it can draw the web diagonally using a missile - without using HMOVE the web could only go directly up or down. That's the same reason it's hit every-other-line in Missile Command.

Gotcha, I had a feeling the close repositions of the web had something to do with it there. Ms Pac Man was another example I had in mind - but from a quick peek in the Stella debugger, I'm guessing it's the same timing / code simplification you mention (RESP1 is strobed once per ghost, but the HMOVEs are just fired periodically down the frame).

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