Jump to content
IGNORED

A working horizontal positioning routine!


CPC464Kid

Recommended Posts

For my first post I'd like to share my version of the horizontal positioning routine, as other variations I've seen always seem to be out by a few pixels which is frustrating ?  I started with the SetHorizPos subroutine from Steven Hugg's book, but this is out by 3 pixels for players.  He's fixed that in other projects at the 8-bit workshop, but even this is still out by one pixel for balls and missiles.  Michael Martin has a clever and well reasoned alternative that corrects the ball, but it uses more cycles and so often goes over a scanline.  Anyway, here is my updated variation of it:

; SetHorizPos routine
; A = X coordinate
; RESP0+X = reset register to strobe
SetHorizPos SUBROUTINE
            cpx #2 ; carry flag will be set for balls and missiles
            adc #0 ; (adding 1 to account for different timings)
            sec
            sta WSYNC
.loop       sbc #15
            bcs .loop
            eor #7
            asl
            asl
            asl
            asl
            sta.a HMP0,X  ; force absolute addressing for timing!
            sta RESP0,X
            rts

The first two lines adjust for balls and missiles - credit to Michael for this, see his excellent blog post for an explanation:

 

https://bumbershootsoft.wordpress.com/2018/08/30/an-arbitrary-sprite-positioning-routine-for-the-atari-2600/

 

The rest is from Steven, but I've forced absolute addressing (using sta.a to consume an extra cycle!) and moved a few things around to fix the timing without having to sleep.  For comparison here this what the fixed version in the 8-bit workshop does:

SetHorizPos
	sta WSYNC	; start a new line
    bit 0		; waste 3 cycles
	sec		; set carry flag
DivideLoop
	sbc #15		; subtract 15
	bcs DivideLoop	; branch until negative
	eor #7		; calculate fine offset
	asl
	asl
	asl
	asl
	sta RESP0,x	; fix coarse position
	sta HMP0,x	; set fine offset
	rts		; return to caller

Notice that in my version there is no need to waste 3 cycles and that I'm now also setting HMP0 before strobing RESP0.  This has the advantage of allowing the routine to return as soon as RESP0 has been strobed which works better for co-ordinates near the end of a scanline.  The sta.a instruction adds 5 cycles before the strobe, but moving the SEC to before WSYNC saves 2 which results in the same 3 cycle delay!

 

Hopefully this will be useful to other people scratching their heads over this.  I've certainly found it the most difficult concept to grasp so far!

  • Like 2
Link to comment
Share on other sites

Hi there,

 

If I'm understanding correctly, your new routine hits the player reset at a minimum of cycle 22. This would give a player horizontal resolution of 60 - 224 without spilling over to the next scan line. The importance of hitting the player reset at a minimum of cycle 23 gives a player horizontal resolution of 63 - 227 before spilling over to the next scan line.

  • Like 1
Link to comment
Share on other sites

4 hours ago, DEBRO said:

Hi there,

 

If I'm understanding correctly, your new routine hits the player reset at a minimum of cycle 22. This would give a player horizontal resolution of 60 - 224 without spilling over to the next scan line. The importance of hitting the player reset at a minimum of cycle 23 gives a player horizontal resolution of 63 - 227 before spilling over to the next scan line.

Hi Dennis, I've just checked with Stella and when A = 0 the scan cycle (CPU cycle) is 23 when the PC is pointing at the RTS instruction.  The position is set to 6 and HM is also 6 (meaning we do eventually end up with position 0 as requested).  The timing of the reset is exactly the same, but the subroutine can return right after because HMP0 has already been set.  An adjustment is also made for balls and missiles.

 

At the other side of the screen when I set A to 149 it just returns in time.  At 150 it starts to spill over onto an extra scan line.  Not too bad I think ?

Link to comment
Share on other sites

Hi there,

2 hours ago, CPC464Kid said:

Hi Dennis, I've just checked with Stella and when A = 0 the scan cycle (CPU cycle) is 23 when the PC is pointing at the RTS instruction.  The position is set to 6 and HM is also 6 (meaning we do eventually end up with position 0 as requested).  The timing of the reset is exactly the same, but the subroutine can return right after because HMP0 has already been set.

Sorry...my cycle counts were off. Yes, you are strobbing RESP0 at the desired cycle 23.

 

2 hours ago, CPC464Kid said:

At the other side of the screen when I set A to 149 it just returns in time.  At 150 it starts to spill over onto an extra scan line.  Not too bad I think ?

Really? My calculations show a value of 134 is the maximum before you spill to another scan line.

 

A value of 134 should strobe RESP0 at cycle 63 with a 8 pixel right shift. Adding 6 more cycles for the RTS you are now at cycle 69 on the return with enough time to do a WSYNC.

 

A value of 135 should strobe RESP0 at cycle 68 with a -6 pixel left shift. Adding 6 more cycles for the RTS you are now at cycle 74 on the return and no time to do a WSYNC without spilling to the next scan line.

  • Like 2
Link to comment
Share on other sites

7 hours ago, DEBRO said:

Really? My calculations show a value of 134 is the maximum before you spill to another scan line.

 

A value of 134 should strobe RESP0 at cycle 63 with a 8 pixel right shift. Adding 6 more cycles for the RTS you are now at cycle 69 on the return with enough time to do a WSYNC.

 

A value of 135 should strobe RESP0 at cycle 68 with a -6 pixel left shift. Adding 6 more cycles for the RTS you are now at cycle 74 on the return and no time to do a WSYNC without spilling to the next scan line.

Yes, your right with 149 I do find myself at cycle 74 on the return.  I was just checking that it returned on the same scan line, but of course that doesn't leave enough time for WSYNC ?  The calling code would have to take that into account and skip the WSYNC if the X position were greater than 134.  Another option would be to WSYNC and HMOVE within the subroutine before returning (if you don't need to move multiple things at once).  Or if using a timer during vertical sync it wouldn't matter of course.

 

Good discussion!

  • Like 1
Link to comment
Share on other sites

Hi there,

 

I'm sure something like this has been shared or posted before. Still, I thought I'd share a spreadsheet I created to help me with player color clock position values.

 

I used something similar when constructing Climber 5 and Pacman4K to help me with object positioning and playfield graphic positions.

 

Changing the "RESPx strobe ends at cycle" value will automatically compute the machine cycle and color clock position for me. Using this, I can then play with position values or RESPx strobe values to get the results I'm looking for.

 

Atari_VCS_Horizontal_Positioning_Chart.xls

  • Like 2
Link to comment
Share on other sites

  • 1 year later...

Necro bumping this thread, and I'm not even a real 2600 programmer.  But just playing around, I came up with this "optimization":

   ORG $F0DD

SetXPos    CPX   #2
           STA   WSYNC
           ADC   #0
           SEC
.div15     SBC   #15
           BCS   .div15
           TAY
           LDA   $F000,Y
           STA   HMP0,X
           STA.W RESP0,X
           HEX 60 50 40 30 20 10 00 F0 E0 D0 C0 B0 A0 90 80

Must start at xxDD offset, uses the $f1-$ff remainder in Y to directly load the fine offsets, and uses the $60 offset as the RTS.   I don't even know if this adds any value to the routine except making it bigger, LOL.  Maybe using a table might be faster under some circumstance?  I don't know.

 

EDIT: Here's a "demo" using this routine and doing some weird stuff I've not seen done before...

psy.bin

Edited by glurk
Added 'Demo'
Link to comment
Share on other sites

15 hours ago, glurk said:

Necro bumping this thread, and I'm not even a real 2600 programmer.  But just playing around, I came up with this "optimization":


   ORG $F0DD

SetXPos    CPX   #2
           STA   WSYNC
           ADC   #0
           SEC
.div15     SBC   #15
           BCS   .div15
           TAY
           LDA   $F000,Y
           STA   HMP0,X
           STA.W RESP0,X
           HEX 60 50 40 30 20 10 00 F0 E0 D0 C0 B0 A0 90 80

Must start at xxDD offset, uses the $f1-$ff remainder in Y to directly load the fine offsets, and uses the $60 offset as the RTS.   I don't even know if this adds any value to the routine except making it bigger, LOL.  Maybe using a table might be faster under some circumstance?  I don't know.

 

EDIT: Here's a "demo" using this routine and doing some weird stuff I've not seen done before...

psy.bin 2 kB · 1 download

woah how did you clear the ram here using ldx? 

Link to comment
Share on other sites

4 hours ago, ultima said:

woah how did you clear the ram here using ldx? 

I assume you mean in the psy.bin demo I posted?  It's just the 'CLEAN_START' macro code, but with the SEI and TAY left out...  It's in macro.h

Edited by glurk
  • 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...