Jump to content
IGNORED

fine positioning not working


Recommended Posts

I have 4 sets of creatures. Only the top row is currently set to move. However the movement is jerky. It only moves in 8 pixel jumps. I am trying to to use, what I think is called battlezone positioning? I was hoping someone could point out what I'm doing wrong here.

 

Here is the code to draw the first row, full source is attached.

ScanLoop ;start of kernal +++++++++++++++++++++++ for player 0


;skipDraw
; draw player sprite 0:
lda     #C_P0_HEIGHT-1     ; 2
dcp     P0_Y            ; 5 (DEC and CMP)
bcs     .doDraw0        ; 2/3 ; should be bcs
lda     #0              ; 2
.byte   $2c             ;-1 (BIT ABS to skip next 2 bytes)(kinda like a jump)
.doDraw0:
lda     (P0_Ptr),y      ; 5
sta     GRP0            ; 3 = 18 cycles (constant, if drawing or not!)

lda P0_XPos
;lda #30
ldx #0

       sec	     ; 2 set carry
.Div15   
sbc #15      ; 2         
bcs .Div15   ; 3(2)

tax
lda fineAdjustTable,x       ; 13 -> Consume 5 cycles by guaranteeing we cross a page boundary
sta HMP0 ;,x

sta RESP0 ;,x	;the x must be a 0 for player 0  or 1 player 1



       DEY             ;count down number of scan lines          2 cycles = 
       STA WSYNC                                                ;3 cycles =
CPY #150
       BCS ScanLoop                                             ;2 cycles =
EndScanLoop ;end of kernal +++++++++++++++++ for player 0

face15.txt

Link to comment
Share on other sites

You need an STA HMOVE to actually do the fine positioning - it should be done directly after the STA WSYNC.

 

Chris

 

EDIT: Looking at the code again it appears that you are moving the sprites on every scanline? You only need to move the sprite once per frame. If you are trying to reuse the same sprite multiple times, then you only need to move it after the sprite has been drawn completely. In either case, you need to HMOVE every time you reposition.

Link to comment
Share on other sites

I didn't think about that it would only need to be positioned one per frame, I'll make those changes later. I am reusing the player0 for 4 different sprites. The modified code with sta hmove added is shown below. The first copy works properly, why do the 2 clones not use the fine positioning? They act the same as before.

 

ScanLoop ;start of kernal +++++++++++++++++++++++ for player 0


;skipDraw
; draw player sprite 0:
lda     #C_P0_HEIGHT-1     ; 2
dcp     P0_Y            ; 5 (DEC and CMP)
bcs     .doDraw0        ; 2/3 ; should be bcs
lda     #0              ; 2
.byte   $2c             ;-1 (BIT ABS to skip next 2 bytes)(kinda like a jump)
.doDraw0:
lda     (P0_Ptr),y      ; 5
sta     GRP0            ; 3 = 18 cycles (constant, if drawing or not!)

lda P0_XPos
;lda #30
ldx #0

       sec	     ; 2 set carry
.Div15   
sbc #15      ; 2         
bcs .Div15   ; 3(2)

tax
lda fineAdjustTable,x       ; 13 -> Consume 5 cycles by guaranteeing we cross a page boundary
sta HMP0 ;,x

sta RESP0 ;,x	;the x must be a 0 for player 0  or 1 player 1



       DEY             ;count down number of scan lines          2 cycles = 
       STA WSYNC                                                ;3 cycles =
STA HMOVE
CPY #150
       BCS ScanLoop                                             ;2 cycles =
EndScanLoop ;end of kernal +++++++++++++++++ for player 0

Edited by yllawwally
Link to comment
Share on other sites

I haven't studied your code carefully, but I have a few comments that might help. By the way, I just copied and pasted the updated code into the original program, because I'm assuming that was the only part that changed.

 

The first row of sprites shows the black HMOVE lines for the entire height of the sprites, which means you're calling HMOVE on every line. When you want to move the sprites for a given row, all you need to do is call (strobe) HMOVE once, at the top of the row, and the rest of the lines for that row will use the new positions. As I said, I haven't studied your code yet, but I wonder if you're also trying to reposition the sprites on every line? You only need to do it once, at the top of the row.

 

When you reposition a sprite on a given line, the main copy usually won't be visible on that line, but the extra copies will be. The main copy will show up on the next line.

 

When repositioning a sprite several times on a screen to get multiple rows of sprites that can move independently, you should reposition the sprites on the line just above where they actually start, so all the lines of the main copy will be visible. The code should look something like this:

 

For the first row:

 

line 1 - set sprite graphics to 000000 - strobe RESP0 (or RESP1) and set HMP0 (or HMP1)

 

line 2 - strobe HMOVE after WSYNC - then load the player graphics for that line

 

lines 3 to whatever - load the player graphics for the remaining lines, but without doing any more repositioning or HMOVEs

 

For the second row:

 

line 21 (or whatever) - set sprite graphics to 000000 again - strobe RESP0 and set HMP0

 

line 22 - strobe HMOVE after WSYNC - then load the player graphics for that line

 

lines 23 to whatever - load the player graphics for the remaining lines, but without any more repositioning or HMOVEs

 

For the third row:

 

same as before

 

etc.

 

Frogger is a good game to look at to see this in action. On the screenshot below, notice that the black HMOVE lines appear only once per row, and the sprites are blank on the line where the HMOVE line is visible-- the sprite graphics don't actually start until the line after the HMOVE line, and you don't see any more HMOVE lines until further down, just before the next row of sprites.

 

Michael

post-7456-127293061015_thumb.png

Link to comment
Share on other sites

The first row of sprites shows the black HMOVE lines for the entire height of the sprites, which means you're calling HMOVE on every line. When you want to move the sprites for a given row, all you need to do is call (strobe) HMOVE once, at the top of the row, and the rest of the lines for that row will use the new positions.

 

The HMOVE circuitry on the 2600 is rather bizarre. It can do some very nice things, but to use it "normally" you should execute an HMOVE immediately after a WSYNC, and make sure that positioning registers are not written in the first 23 cycles or so following an HMOVE. Executing an HMOVE at any other time, or writing to position registers too soon after an HMOVE, will cause weird things to happen. The only unusual trick which is generally useful is hitting HMOVE just before the end of a scan line. If done properly, all sprites will end up 8 pixels to the left of where they normally would, but there won't be an "HMOVE bar" at the edge of the screen. I wonder if anyone discovered that trick back in the day?

Link to comment
Share on other sites

The HMOVE circuitry on the 2600 is rather bizarre. It can do some very nice things, but to use it "normally" you should execute an HMOVE immediately after a WSYNC, and make sure that positioning registers are not written in the first 23 cycles or so following an HMOVE. Executing an HMOVE at any other time, or writing to position registers too soon after an HMOVE, will cause weird things to happen. The only unusual trick which is generally useful is hitting HMOVE just before the end of a scan line. If done properly, all sprites will end up 8 pixels to the left of where they normally would, but there won't be an "HMOVE bar" at the edge of the screen. I wonder if anyone discovered that trick back in the day?

Is it used in any of the games from back then? I seem to remember reading in a post a while back that someone claimed to have discovered the trick while working on a game, but I can't remember who it was or what the game was.

 

I've been wondering if the repositioning routine can be rewritten to use a cycle 73 or 74 HMOVE. After you do the coarse positioning with RESxx, you'd have to move the sprite 0 to 15 clocks left, so positioning a sprite near the right edge of the screen would require setting its coarse position near the left and then moving it left past the edge of the screen, wrapping around to the right. And you'd have to set the movements of everything else to $80 to keep them in place. I don't know if all that would be worth the trouble, but it would be neat to have a generic repositioining routine that doesn't leave HMOVE lines. :)

 

Michael

Link to comment
Share on other sites

why does an hmove cause a black bar?

An HMOVE doesn't necessarily cause a black bar, but a "normal" HMOVE-- where you strobe HMOVE right after strobing WSYNC-- always produces a black bar.

 

It's a bit technical (to say the least!), and I only partially understand it myself. Probably the best source of information about this is the document called "Atari 2600 TIA Hardware Notes," by Andrew Towers, which you can find at http://www.atarihq.com/danb/files/TIA_HW_Notes.txt. I'll try to give a brief explanation as I understand it, but I can't promise that it will be accurate. :) And I'm going to leave out some things, or breeze through (by?) them very quickly.

 

Once you set a graphics register in the TIA, it will stay set until you change it. This goes for the graphics data itself (i.e., the pixel patterns for the playfield registers and sprites), as well as their positions. So if you set a player's position, it will stay at that position until you move it.

 

The TIA uses polynomial counters to tell itself when it's time to do something, and it has different counters (or "linear feedback shift registers") for different things. One of them is used for the horizontal timing that controls the HSYNC, HBLANK, and drawing the screen from the playfield registers. Another one is used for the timimg that controls drawing from the player0 register, including drawing the extra copies of player0 if they're enabled. We won't worry about the other sprites, but they have their own counters.

 

The horizontal counter has to run constantly, even during the HBLANK period, since it's used to control when the TIA turns HBLANK on and off. But the player0 counter runs only during the active portion of the scan line, or while HBLANK is turned off. This is why you can position a sprite near the right edge of the screen and its graphics will wrap around and continue on the left side of the screen. For example, if you've set player0's position 4 color clocks before the right edge of the screen, the left half of player0 will be drawn at the right side of the screen, then the TIA will stop drawing player0 as soon as the HBLANK period starts. Once HBLANK is turned off again at the left edge of the screen, the TIA will finish drawing player0, so the right half of player0 will appear at the left side of the screen.

 

This doesn't allow for any horizontal movement of player0, since the TIA will always start drawing player0 at the same place-- except, of course, when you use RESP0 to set player0 to a new position. The problem is, you can strobe RESP0 only on every third color clock, since each machine cycle lasts for 3 color clocks. And if you use some kind of loop to count down to when you want to strobe RESP0, you end up being able to position player0 at intervals of 3 times the length of the loop. For example, the subtraction loop in the repositioning routine lasts for 5 machine cycles, so each time you go through the loop the scanning beams move 15 color clocks (3 clocks per cycle, times 5 cycles, equal 15 clocks). So HMOVE was designed to let you move player0 (and the other sprites) in increments of 1 color clock.

 

I don't pretend to know the original thinking behind the design, but HMOVE works by feeding extra pulses to the position counter for player0 (and the other sprites). Normally, the counter runs only during the active portion of the line, as I already said. When the counter reaches a certain value or state, the TIA knows it's time to start drawing player0. So if you feed extra pulses to the counter, it reaches that value a little bit sooner than it would have otherwise, causing the TIA to start drawing player0 a little earlier on the scan line, thereby moving player0 to the left. The way it's designed, the TIA can feed extra pulses to the position counter, but it can't subtract pulses, which means player0 can be moved left, but it can't be moved right. Up to 15 extra pulses can be fed to the counter, so player0 can be moved up to 15 color clocks to the left.

 

To get around this "left only" movement restriction, the TIA adds 8 extra clocks of HBLANK, or delays turning off HBLANK by 8 clocks, which is what causes the "HMOVE bars"-- they are the visible result of waiting for 8 more color clocks than usual before turning off HBLANK. And since the position counter only runs while HBLANK is turned off, this has the effect of making player0 move 8 color clocks to the right. So without the extra HBLANK clocks, you can move player0 from 0 to 15 clocks to the left, by feeding 0 to 15 extra pulses to the counter. But when you add the 8 extra clocks of HBLANK, you end up being able to move player0 anywhere between 8 clocks to the right and 7 clocks to the left:

 

15 left to 0 left (without the extra HBLANK clocks)
+8 right  +8 right (due to the extra HBLANK clocks)
=7 left to 8 right

If you strobe HMOVE at different times on the scan line, you get different results, because the HMOVE processing begins after you strobe the HMOVE register, but it can feed extra pulses to the position counters only during HBLANK. If you strobe HMOVE in the middle of the scan line, player0 won't move, because all of the HMOVE processing will take place while HBLANK is turned off, so the TIA won't be able to feed any extra pulses to the counter. If you strobe HMOVE near the end of the scan line, or else a little after WSYNC, then some of the extra pulse will be able to be fed to the counter, but there won't be time to feed all of them to the counter.

 

Brad Mott did some experimenting on a 2600 to see the results of strobing HMOVE at different times, and he made a table that's been added to the "Atari 2600 Advanced Programming Guide," which can be found at http://www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt. I rearranged his table to produce the one shown below:

 

   75 04 05 07 08 09 11 12 13 15 16 17 19 20 21 55 56 57 59 60 61 63 64 65 67 68 69 71 72 73
$70 <7 <6 <5 <4 <3 <2 <1  0 >1 >2 >3 >4 >5 >6  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A <B <C <D <E <F
$60 <6 <6 <5 <4 <3 <2 <1  0 >1 >2 >3 >4 >5 >6  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A <B <C <D <E
$50 <5 <5 <5 <4 <3 <2 <1  0 >1 >2 >3 >4 >5 >6  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A <B <C <D
$40 <4 <4 <4 <4 <3 <2 <1  0 >1 >2 >3 >4 >5 >6  0  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A <B <C
$30 <3 <3 <3 <3 <3 <2 <1  0 >1 >2 >3 >4 >5 >6  0  0  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A <B
$20 <2 <2 <2 <2 <2 <2 <1  0 >1 >2 >3 >4 >5 >6  0  0  0  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9 <A
$10 <1 <1 <1 <1 <1 <1 <1  0 >1 >2 >3 >4 >5 >6  0  0  0  0  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8 <9
$00  0  0  0  0  0  0  0  0 >1 >2 >3 >4 >5 >6  0  0  0  0  0  0  0  0 <1 <2 <3 <4 <5 <6 <7 <8
$F0 >1 >1 >1 >1 >1 >1 >1 >1 >1 >2 >3 >4 >5 >6  0  0  0  0  0  0  0  0  0 <1 <2 <3 <4 <5 <6 <7
$E0 >2 >2 >2 >2 >2 >2 >2 >2 >2 >2 >3 >4 >5 >6  0  0  0  0  0  0  0  0  0  0 <1 <2 <3 <4 <5 <6
$D0 >3 >3 >3 >3 >3 >3 >3 >3 >3 >3 >3 >4 >5 >6  0  0  0  0  0  0  0  0  0  0  0 <1 <2 <3 <4 <5
$C0 >4 >4 >4 >4 >4 >4 >4 >4 >4 >4 >4 >4 >5 >6  0  0  0  0  0  0  0  0  0  0  0  0 <1 <2 <3 <4
$B0 >5 >5 >5 >5 >5 >5 >5 >5 >5 >5 >5 >5 >5 >6  0  0  0  0  0  0  0  0  0  0  0  0  0 <1 <2 <3
$A0 >6 >6 >6 >6 >6 >6 >6 >6 >6 >6 >6 >6 >6 >6  0  0  0  0  0  0  0  0  0  0  0  0  0  0 <1 <2
$90 >7 >7 >7 >7 >7 >7 >7 >7 >7 >7 >7 >7 >7 >7  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 <1
$80 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8 >8  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

The numbers on the top row (i.e., the column headings) represent a particular machine cycle when you finish strobing the HMOVE register. The numbers on the left (i.e., the row headings) represent the value that you store in the HMP0 register. The rest of the numbers represent how far left (<) or right (>) player0 will move.

 

For example, if you set HMP0 to $70, then do a STA HMOVE that ends at cycle 75, player0 will move 7 color clocks to the left (<7). If you set HMP0 to $60, then do a STA HMOVE that ends at cycle 73, player0 will move 14 color clocks to the left (<E, where E is the hexadecimal digit for 14).

 

You'll notice that some machine cycles are missing from the top row. That's because I left out the cycles if they produce the same results as the one before. In other words, if you do a cycle 75 HMOVE (or one where STA HMOVE ends at cycle 75), you get the results shown in that column. But you get the same results if you do a cycle 0 (or cycle 76) HMOVE, or a cycle 1 HMOVE, or a cycle 2 HMOVE, or a cycle 3 HMOVE. The results don't change until you do a cycle 4 HMOVE, so cycle 4 is the next column shown after cycle 75. After that, the results change every cycle, but they skip a cycle each 4 cycles-- i.e., 4, 5, skip 6 (because it gives the same results as 5), then 7, 8, 9, skip 10 (because it gives the same results as 10), then 11, 12, 13, skip 14, etc. I should stress that I got all of these values from Brad Mott's table-- all I did was arrange them differently-- and I haven't verified their accuracy. And I'm not certain why some cycles produce the same results as others. But I believe it's due to the fact that 1 tick of the TIA's polynomial counters lasts for 4 color clocks, whereas 1 machine cycle lasts for 3 color clocks, so this produces the pattern you see of "3 unique results for every 4 cycles."

 

Anyway, if you strobe HMOVE just before HBLANK starts (i.e., ending at cycle 75), or sometime after HBLANK has already started, you get the HMOVE bar (8 extra clocks of HBLANK), which allows you to move player0 to the right. If you strobe HMOVE just before HBLANK ends (i.e., ending at cycle 21), or sometime after HBLANK has already ended, it's too late for the TIA to feed any extra pulses to the counter, so you don't get any movement. And if you strobe HMOVE near the end of the line, the TIA can feed one or more extra pulses to the counter-- depending on how much of the HMOVE processing extends into the HBLANK period-- but you won't get an HMOVE bar, so you can move player0 left, but not right. That's because the latch that causes the HBLANK to be extended by 8 extra clocks gets flipped off as soon as HBLANK starts. It takes about a cycle after strobing HMOVE for the latch to get set, so a cycle 75 HMOVE will set the latch just after HBLANK starts, and you get the HMOVE bar. But if you do a cycle 74 HMOVE, the latch gets set before HBLANK starts, then it gets cleared right away when HBLANK starts, hence there's no HMOVE bar.

 

I don't think I explained it very well (a lot of pretty pictures would have helped!), and I'm fuzzy on some of the technical details myself, but fortunately you don't need to understand all of the technical details-- you can just use the HMOVE timing chart without needing to know the whys of it.

 

The gist of it is that, if you do a cycle 3 HMOVE (i.e., right after WSYNC), you can move player0 up to 7 color clocks to the left, or up to 8 color clocks to the right, depending on the value you wrote to HMP0, and you also get the HMOVE bar at the beginning of that line. If you do a cycle 73 or 74 HMOVE, you can move player0 up to 15 color clocks to the left, and you won't get the HMOVE bar on the next line, but that also means you won't be able to move player0 to the right. :)

 

Michael

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

Now I have the creatures moving properly. Thanks for all the help. There are four lines of sprites. Why do I need to position them at the same place, for them to appear. If I set each P?_YPosFromBot to 172, and place an offset for the skip draw code, they show up. If I don't make these modifications, they don't show up. When I move the hero up the shift as well, such that he loses two pixels on the 3rd row, then two more on the 2nd, and 4 on the 1st.

 

;Setting some variables...
LDA #172
STA P0_YPosFromBot
LDA #50
STA P0_XPos
LDA #122 ;was 172
STA P1_YPosFromBot
LDA #50
STA P1_XPos
LDA #72 ;was 172
STA P2_YPosFromBot
LDA #50
STA P2_XPos
LDA #22 ;was 172
STA P3_YPosFromBot
LDA #50
STA P3_XPos

 

;setup pic animations ----------------------------------------------

lda #<MainPlayerGraphics2 	;low byte of ptr is graphic
CLC	;clear carry
ADC PICS
sta P2_Ptr		;(high byte already set)

lda #>MainPlayerGraphics2 ;high byte of graphic location
sta P2_Ptr+1	;store in high byte of graphic pointer

lda #C_KERNAL_HEIGHT + #C_P0_HEIGHT - #1
sec
sbc P2_YPosFromBot ;subtract integer byte of distance from bottom
sta P2_Y


lda P2_Ptr
sec
sbc P2_YPosFromBot	;integer part of distance from bottom
clc
adc #C_P0_HEIGHT+#92
sta P2_Ptr	;2 byte

;setup pic animations ----------------------------------------------

;setup pic animations ----------------------------------------------

lda #<MainPlayerGraphics3 	;low byte of ptr is graphic
CLC	;clear carry
ADC PICS
sta P3_Ptr		;(high byte already set)

lda #>MainPlayerGraphics3 ;high byte of graphic location
sta P3_Ptr+1	;store in high byte of graphic pointer


lda #C_KERNAL_HEIGHT + #C_P0_HEIGHT - #1
sec
sbc P3_YPosFromBot ;subtract integer byte of distance from bottom
sta P3_Y


lda P3_Ptr
sec
sbc P3_YPosFromBot	;integer part of distance from bottom
clc
adc #C_P0_HEIGHT+#142
sta P3_Ptr	;2 byte

;setup pic animations ----------------------------------------------

face.bin

Link to comment
Share on other sites

  • 3 weeks later...

I figured why I needed to offset the graphics and start positions of the creatures. I wasn't decrementing their skipdraw counters on each scanline. So the first was off by 20 lines, the next one by the 20 lines plus the 50 the first was displayed within, and this continued with the next 2 as well. I had trouble finding time to decrement all 5 counters on each scanline. How are other people doing this? I'm going to change the value used for the top of the screen for each creature, so that position 172 is the same place for all the creatures, without the need for all the decrementing.

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