CPC464Kid Posted June 28, 2020 Share Posted June 28, 2020 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! 2 Quote Link to comment Share on other sites More sharing options...
DEBRO Posted June 29, 2020 Share Posted June 29, 2020 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. 1 Quote Link to comment Share on other sites More sharing options...
CPC464Kid Posted June 30, 2020 Author Share Posted June 30, 2020 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 ? Quote Link to comment Share on other sites More sharing options...
DEBRO Posted June 30, 2020 Share Posted June 30, 2020 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. 2 Quote Link to comment Share on other sites More sharing options...
CPC464Kid Posted June 30, 2020 Author Share Posted June 30, 2020 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! 1 Quote Link to comment Share on other sites More sharing options...
DEBRO Posted July 4, 2020 Share Posted July 4, 2020 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 2 Quote Link to comment Share on other sites More sharing options...
glurk Posted June 25, 2022 Share Posted June 25, 2022 (edited) 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 June 25, 2022 by glurk Added 'Demo' Quote Link to comment Share on other sites More sharing options...
ultima Posted June 25, 2022 Share Posted June 25, 2022 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? Quote Link to comment Share on other sites More sharing options...
glurk Posted June 25, 2022 Share Posted June 25, 2022 (edited) 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 June 25, 2022 by glurk 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.