Jump to content
IGNORED

Session 22: Sprites, Horizontal Positioning Part 1


Recommended Posts

Finally got around to catching up on these.

 

Anyway here is some code that demos horizontal movement

 

Hi, I know this post is old, but I was searching for information on calculating missile coordinates and horizontal positioning. I looked at the horizontal-22.asm and I saw something that confused me.

 

201:		ldx #2 

;code snipped

235:		sta HMP0-1,x

 

On line 235, does that actually write to HMP1? And is this style of notation used to waste cycles? Or is there some other purpose to this?

 

Thanks,

 

Jim

Link to comment
Share on other sites

Hey xucaen,

 

It looks like that is used in a loop so it moves both player0 and player1 sprites:

 

ldx #2

PosSP

 

lda PlayerX-1,x

sta RESP0-1,x

sta HMP0-1,x

...and so on...

 

dex

bne PosSP

 

In the debugger it does update HMP0 and HMP1... I don't know why he didn't use ldx#1 and drop the -1s though?

Link to comment
Share on other sites

  • 11 years later...

It seems the part 2 is missing. Was it deleted/lost? I couldn't find it here or in Random Terrain's site.

 

And since Andrew said:

 


Next session we're going to dig into much more robust horizontal positioning code, and learn how the TIA provides us that fine control we need to get the horizontal positioning code precise enough to allow TIA-pixel-precise positioning. Once we've achieved that, we can pretty much forget about how this works forever more, and use the horizontal positioning code as a black box. Or perhaps a woodgrain box might be more appropriate icon_smile.gif

See you next time!

 

I just wonder what's this magical woodgrain box like!

Link to comment
Share on other sites

  • 5 months later...

It seems the part 2 is missing. Was it deleted/lost? I couldn't find it here or in Random Terrain's site.

 

I just wonder what's this magical woodgrain box like!

 

Yeah, I came here looking for Part 2 as well. I think we're the only two people to notice we got left hanging here. Digging through the forum reveals that Andrew took a long break between sessions 22 and 23. I can only suppose that it was forgotten during that time. That, or newbies just aren't meant to the know the secrets of the "HMPx fine-tune horizontal position register" as Andrew called it in Session 24 when listing things covered in Session 22 (but which, well, not all were).

Link to comment
Share on other sites

The tutorials were just a stream of consciousness typed out on the fly. I made some errors and missed a lot of stuff. And my knowledge of the machine isn't perfect especially as this stuff was over 10 years ago. Just consider the missing stuff "an exercise for the reader"

  • Like 1
Link to comment
Share on other sites

The tutorials were just a stream of consciousness typed out on the fly. I made some errors and missed a lot of stuff. And my knowledge of the machine isn't perfect especially as this stuff was over 10 years ago. Just consider the missing stuff "an exercise for the reader"

No worries, I'm thankful for all you wrote. And, yeah, there are plenty of other resources -- although, not written as accessible as yours are. It was just one of those "awe man" moments because I read only sessions 21 and 22 because all I was interested in really was just the horizontal positioning (trying to understand the odd horizontal positioning in the River Raid code). Then, at then the end of 22 you reach the teaser text (quoted by LidLikesIntellivision above) and it's a total cliffhanger that you spend another hour looking to find the other half of and can't. Regardless, though, learning did happen because of you and I thank you for that.

  • Like 1
Link to comment
Share on other sites

  • 10 months later...
  • 11 months later...

Yeah, it's me, so it's got to be necro time, right?

 

I'm up to this part and having trouble understanding what .POS means -- my mind parses anything to the far left of the editor as a label.  Is .POS acting as some kind of "local variable"?  I know it's shorthand for position, but I'm not sure how it's used since I see it both in the "label" column and being used as part of an instruction further to the right.

Divide15

.POS	SET 0

REPEAT 160

.byte .POS / 15

.POS	SET .POS + 1

REPEND

 

Edited by SavedByZero
Link to comment
Share on other sites

3 hours ago, SavedByZero said:

Yeah, it's me, so it's got to be necro time, right?

 

I'm up to this part and having trouble understanding what .POS means -- my mind parses anything to the far left of the editor as a label.  Is .POS acting as some kind of "local variable"?  I know it's shorthand for position, but I'm not sure how it's used since I see it both in the "label" column and being used as part of an instruction further to the right.


Divide15
.POS   SET 0
    REPEAT 160
       .byte .POS / 15
.POS   SET .POS + 1
    REPEND

 

This is in effect a mini-programming language inside the assembler.

Unfortunately the required spaces have been removed from the code when the forum upgraded. I fixed that in the above quote.

.POS is a local label. When you use the SET directive, you are allowed to re-define the value later. So we're treating this local label as if it was a variable in a program. It is set to 0, and then the repeat loop runs 160 times, each time incrementing .POS by one.

So it will create a series of 160 bytes, each value being the byte # (held in .POS) divided by 15.

It's basically creating a lookup table ("Divide15") where you use an index register to lookup a value. The value you get is the index divided by 15.

 

 

 

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

For those looking for the missing part 2, here is my interpretation of what it was set to cover (more or less).

 

This is taken from the Stella Programmer's Guide by Steve Wright 12/03/79.

 

Session 22: Sprites, Horizontal Positioning (Part 2)

 

Fine Tuning Horizontal Position

 

Horizontal Motion Registers

 

20 HMP0 1 1 1 1 . . . . horizontal motion player 0
21 HMP1 1 1 1 1 . . . . horizontal motion player 1
22 HMM0 1 1 1 1 . . . . horizontal motion missile 0
23 HMM1 1 1 1 1 . . . . horizontal motion missile 1
24 HMBL 1 1 1 1 . . . . horizontal motion ball

 

8.0 Horizontal Motion

 

Horizontal motion allows the programmer to move any of the 5 graphics objects relative to their current horizontal position. Each object has a 4 bit horizontal motion register (HMP0, HMP1, HMM0, HMM1, HMBL) that can be loaded with a value in the range of +7 to -8 (negative values are expressed in two's complement from). This motion is not executed until the HMOVE register is written to, at which time all motion registers move their respective objects. Objects can be moved repeatedly by simply executing HMOVE. Any object that is not to move must have a 0 in its motion register. With the horizontal positioning command confined to positioning objects at 15 color clock intervals, the motion registers fills in the gaps by moving objects +7 to -8 color clocks. Objects can not be placed at any color clock position across the screen. All 5 motion registers can be set to zero simultaneously by writing to the horizontal motion clear register (HMCLR).

 

There are timing constraints for the HMOVE command. The HMOVE command must immediately follow a WSYNC (Wait for SYNC) to insure the HMOVE operation occurs during horizontal blanking. This is to allow sufficient time for the motion registers to do their thing before the electron beam starts drawing the next scan line. Also, for mysterious internal hardware considerations, the motion registers should not be modified for at least 24 machine cycles after an HMOVE command.

 

HMP0 (HMP1, HMM0, HMM1, HMBL)

These addresses write data (horizontal motion values) into the horizontal motion registers. These registers will cause horizontal motion only when commanded to do so by the horiz. move command HMOVE. The motion values are coded as shown below :
D7 D6 D5 D4 Clocks Effect
0 1 1 1 +7 Move left indicated number of clocks
0 1 1 0 +6
0 1 0 1 +5
0 1 0 0 +4
0 0 1 1 +3
0 0 1 0 +2
0 0 0 1 +1
0 0 0 0 0 No Motion
1 1 1 1 -1 Move right indicated number of clocks
1 1 1 0 -2
1 1 0 1 -3
1 1 0 0 -4
1 0 1 1 -5
1 0 1 0 -6
1 0 0 1 -7
1 0 0 0 -8
WARNING : These motion registers should not be modified during the 24 computer cycles immediately following an HMOVE command. Unpredictable motion values may result.

 

Undocumented Quirk

 

Not all objects position exactly the same.  Double- and quad-wide player sprites shift +1 color clock.  Missiles and the ball shift -1 color clock.  This is discussed here:

 

Example code from River Raid (comments from myself and JTZ)

 

; x-position missile:
       LDA    missileX          ; 3
       JSR    SetPosX           ; 6             position missile
       JSR    DoHMove           ; 6             do HMOVE

...

SetPosX SUBROUTINE
; calculates the values and positions objects:
       JSR    CalcPosX          ; 6 $FAEF
SetPosX2:
       STA    HMP0,X            ; 4             fine position player, missile, or ball, depending on value of X
       INY                      ; 2             get past the 68 pixels of horizontal blank
       INY                      ; 2
       INY                      ; 2
       STA    WSYNC             ; 3             wait for sync; wait for TIA to finish line
.waitPos:
       DEY                      ; 2
       BPL    .waitPos          ; 2/3           5-cycle loop = 15 TIA color clocks (pixels)
       STA    RESP0,X           ; 4
       RTS                      ; 6

...

CalcPosX SUBROUTINE
; calculates values for x-positioning:
; Basically, divides pos by 15 and stores the int result of that.
; Then, the mod of that is adjusted to equal 6 + HMP1.
;   HMPx bits 4..7: Offset value:
;     0000 ($00): No offset
;     0001 ($10): Left 1 clock
;     0010 ($20): Left 2 clock
;     0011 ($30): Left 3 clock
;     0100 ($40): Left 4 clock
;     0101 ($50): Left 5 clock
;     0110 ($60): Left 6 clock
;     0111 ($70): Left 7 clock
;     1000 ($80): Right 8 clock
;     1001 ($90): Right 7 clock
;     1010 ($A0): Right 6 clock
;     1011 ($B0): Right 5 clock
;     1100 ($C0): Right 4 clock
;     1101 ($D0): Right 3 clock
;     1110 ($E0): Right 2 clock
;     1111 ($F0): Right 1 clock
;
; INPUT:
;   A = x-position
; RETURN:
;   Y = coarse value for delay loop (shifted to lower 4 bits), 1 loop = 5 clocks = 15 pixels
;   A = fine value for HMPx, HMMx, or HMBL
       TAY                      ; 2 $FDD8       Y = A
       INY                      ; 2             Y++           (setup for div 15; causes 15 to overflow)
       TYA                      ; 2             A = Y
       AND    #$0F              ; 2             A chopped to lower 4 bits 00001111 (fine pos value + 1)
       STA    temp2             ; 3       $ED   temp2 = A  (fine pos value + 1)
       TYA                      ; 2             A = Y
       LSR                      ; 2             A chopped to upper 4 bits and shifted to lower 4 bits (A div 16)
       LSR                      ; 2
       LSR                      ; 2
       LSR                      ; 2
       TAY                      ; 2             Y = A, backup shifted bits to Y
       CLC                      ; 2             CF = 0
       ADC    temp2             ; 3             A = A + temp2  (shifted bits added to change this from div 16 to div 15)
       CMP    #15               ; 2             is A < 15?     (look for div 15 overflow)
       BCC    .skipIny          ; 2              yes, skip to EOR  (no div 15 overflow)
       SBC    #15               ; 2              no, A = A - 15  (CF = 1 but not needed here)
       INY                      ; 2               Y++              (yes on the div 15 overflow so one-up Y)
.skipIny:
       EOR    #$07              ; 2             A = A XOR 00000111,  7's complement, sets offset for 6 + HMPx
; CAUTION: Any JSR here will use 20 cycles instead of 8
; to save 1 byte (3 bytes instead of 4)!
Mult16:
       ASL                      ; 2             A's lower 4 bits shifted to upper 4
       ASL                      ; 2
       ASL                      ; 2
       ASL                      ; 2
Wait12:
       RTS                      ; 6

...

DoHMove:
       STA    WSYNC             ; 3             wait for sync; wait for TIA to finish line
       STA    HMOVE             ; 3             must follow WSYNC if horizontal motion desired
       RTS                      ; 6

 

Other Algorithms

 

Hopefully, after this, Session 24 will make more sense, which discusses other algorithms for horizontal positioning.

 

  • Like 1
Link to comment
Share on other sites

4 hours ago, juanitogan said:

For those looking for the missing part 2, here is my interpretation of what it was set to cover (more or less).

 

Thanks for sending me an e-mail. I'll start working on adapting this for my web site some time tomorrow if it's OK with Andrew Davie. Should it just be added to the Session 22 page or should it have its own Session 22 (part 2) page?

  • Like 1
Link to comment
Share on other sites

1 hour ago, Random Terrain said:

 

Thanks for sending me an e-mail. I'll start working on adapting this for my web site some time tomorrow if it's OK with Andrew Davie. Should it just be added to the Session 22 page or should it have its own Session 22 (part 2) page?

The more information the merrier. Happy for it to be included in 22 if you think that makes sense.

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, Random Terrain said:

 

Thanks for sending me an e-mail. ... Should it just be added to the Session 22 page or should it have its own Session 22 (part 2) page?

Happy to. ...  To me, at least, it looks like another page since I made no attempt to make it look like Davie's work.  It's just some intel I scraped together that I didn't want to lose for if and when I need it.

Link to comment
Share on other sites

1 hour ago, juanitogan said:

Happy to. ...  To me, at least, it looks like another page since I made no attempt to make it look like Davie's work.  It's just some intel I scraped together that I didn't want to lose for if and when I need it.

 

Thanks. Is juanitogan the name you want me to use? And should it say "By ______" or "Information compiled by _____"? (Insert name you want to use in the blank space.)

Link to comment
Share on other sites

12 hours ago, Random Terrain said:

 

Thanks. Is juanitogan the name you want me to use? And should it say "By ______" or "Information compiled by _____"? (Insert name you want to use in the blank space.)

Primary credit, of course, goes to Wright for the parts of his book I excerpted.  A thank-you line, however, would be cool (I take care to give out thank yous to contributors on my sites as well -- it's the right thing to do).  So, maybe something like "Thank you to Matt Jernigan for compiling and contributing this."  And, yes, that's my real name there.

 

I should also clarify, concerning the River Raid code, that JTZ is Thomas Jentzsch (he uses both his name and JTZ in the code).  I began with his commented code and then added a lot to it as I worked through my River Raid port (which I will publish on my River Raid Squadron page at some point).  Nearly all the comments in the example code here are mine, but I wouldn't have gotten here without him.  All of this intel, actually, was gathered during that port work.  It was running into that quirk that led to so many comments on the HMPx stuff before I finally figured out something else must be going on that affects fine positioning.  Thus, it was time to put it all together in one place and share.

  • Like 1
Link to comment
Share on other sites

13 hours ago, Random Terrain said:

Here's my first try:

Looks good to me.  One thing I would change is that the example code needs a header.  Otherwise, the code looks like it applies to the quirk when it really applies to the whole chapter.  Perhaps me starting it with the missile code was misleading (the missile was just the object that got to the point the quickest).

 

Thus, I guess I can help out the reader a bit more with a couple more changes.  I added a couple sentences to the quirk section:

 

Quote

Not all objects position exactly the same. Double-wide and quad-wide player sprites shift +1 color clock. Missiles and the ball shift -1 color clock.

 

For example, to compensate for this, the quad-wide bridge sprite in River Raid is curiously set to an x-position of 63 (instead of 64) before its fine-positioning subroutine is called. This quirk will shift the bridge +1 color clock and draw it at the intended x-position of 64.

 

This quirk is discussed in more detail here:

 

And, here's a better block of example code with some updated comments:

 

       INX                      ; 2             x = 0! (player jet)
...
       LDY    playerX           ; 3             y = player's x-pos
...
       TYA                      ; 2             a = y
       JSR    SetPosX           ; 6             x-position player jet
       INX                      ; 2             x = 1 (enemy object)
...
       INX                      ; 2             x = 2 (player missile)
       LDA    missileX          ; 3             a = missile's x-pos
       JSR    SetPosX           ; 6             x-position missile
       JSR    DoHMove           ; 6             do HMOVE

...

SetPosX SUBROUTINE
; calculates the values and positions objects:
; INPUT:
;   A = x-position
;   X = object to position (0=P0, 1=P1, 2=M0, 3=M1, 4=BL)
       JSR    CalcPosX          ; 6 $FAEF
SetPosX2:
       STA    HMP0,X            ; 4             fine position the object specified in X
       INY                      ; 2             get past the 68 pixels of horizontal blank
       INY                      ; 2
       INY                      ; 2
       STA    WSYNC             ; 3             wait for sync; wait for TIA to finish line
.waitPos:
       DEY                      ; 2
       BPL    .waitPos          ; 2/3           5-cycle loop = 15 TIA color clocks (pixels)
       STA    RESP0,X           ; 4
       RTS                      ; 6

...

CalcPosX SUBROUTINE
; calculates values for fine x-positioning:
;
; Basically, divides pos by 15 and stores the int result of that.
; Then, the mod of that is adjusted to equal 6 + HMP1.
;   HMPx bits 4..7: Offset value:
;     0000 ($00): No offset
;     0001 ($10): Left 1 clock
;     0010 ($20): Left 2 clocks
;     0011 ($30): Left 3 clocks
;     0100 ($40): Left 4 clocks
;     0101 ($50): Left 5 clocks
;     0110 ($60): Left 6 clocks
;     0111 ($70): Left 7 clocks
;     1000 ($80): Right 8 clocks
;     1001 ($90): Right 7 clocks
;     1010 ($A0): Right 6 clocks
;     1011 ($B0): Right 5 clocks
;     1100 ($C0): Right 4 clocks
;     1101 ($D0): Right 3 clocks
;     1110 ($E0): Right 2 clocks
;     1111 ($F0): Right 1 clock
;
; INPUT:
;   A = x-position
; RETURN:
;   Y = coarse value for delay loop (shifted to lower 4 bits), 1 loop = 5 clocks = 15 pixels
;   A = fine value for HMPx, HMMx, or HMBL
       TAY                      ; 2 $FDD8       Y = A
       INY                      ; 2             Y++           (setup for div 15; causes 15 to overflow)
       TYA                      ; 2             A = Y
       AND    #$0F              ; 2             A chopped to lower 4 bits 00001111 (fine pos value + 1)
       STA    temp2             ; 3       $ED   temp2 = A  (fine pos value + 1)
       TYA                      ; 2             A = Y
       LSR                      ; 2             A chopped to upper 4 bits and shifted to lower 4 bits (A div 16)
       LSR                      ; 2
       LSR                      ; 2
       LSR                      ; 2
       TAY                      ; 2             Y = A, backup shifted bits to Y
       CLC                      ; 2             CF = 0
       ADC    temp2             ; 3             A = A + temp2  (shifted bits added to change this from div 16 to div 15)
       CMP    #15               ; 2             is A < 15?     (look for div 15 overflow)
       BCC    .skipIny          ; 2              yes, skip to EOR  (no div 15 overflow)
       SBC    #15               ; 2              no, A = A - 15  (CF = 1 but not needed here)
       INY                      ; 2               Y++              (yes on the div 15 overflow so one-up Y)
.skipIny:
       EOR    #$07              ; 2             A = A XOR 00000111,  7's complement, sets offset for 6 + HMPx
; NOTE: Any JSR here will use 20 cycles instead of 8
; but saves 1 byte of code overall (3 bytes instead of 4).
Mult16:
       ASL                      ; 2             A's lower 4 bits shifted to upper 4
       ASL                      ; 2
       ASL                      ; 2
       ASL                      ; 2
Wait12:
       RTS                      ; 6

...

DoHMove:
       STA    WSYNC             ; 3             wait for sync; wait for TIA to finish line
       STA    HMOVE             ; 3             must follow WSYNC if horizontal motion desired
       RTS                      ; 6

 

  • Like 1
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...