| |
Mantiz Account closed
Registered: Apr 2006 Posts: 36 |
Timing with y-moving sprites
Hi,
I have been trying to figure this thing out for a while.
What I have now is a stable raster routine so opening the sideborders is for example no problems at all.
What I wonder is how you can have sprites moving in the Y direction over these lines and still keep the timing, because it will change depending on which and how many sprites there are crossing it. No need to take care of badlines is required in this example.
Do I need to use NMI interrupts or is there another way, for example do a calculation somewhere else on the screen how many sprites there will be on a certain line and from that set up an appropriate delay for every line in the sideborder/raster/whatever routine you need to be stable with a changing amount of sprites over it? I've been trying the later approach but my code gets rather messy.
|
|
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
one common approach is to simply NOT move them at all (not in Y atleast). stretch them down using $d017 and switch sprite pointers and/or mod the way they are stretched to simulate Y movement. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
If I were you I'd use the timing technique used by ninja in real. This is applicable here since the $d016 write is to be done BEFORE the sprite fetches. I.e. for each line you stabalize between the sprite fetches of the current line and the next and POKE the $d016. But that's just me since I'm familiar with that technique.
Basically you have a timer, in your case ticking down from 62 to 0 all the time. So:
$dd06: #<$62
$dd07: #>$62
But then you place a JMP and a 0 on $dd04/dd05:
$dd04: jmp-opcode (#$4c)
$dd05: #$00
$dd06: #<$62 (lobyte of timer)
$dd07: #>$62 (hibyte of timer)
The point the NMI vector to $dd04
$fffa: #<$dd04
$fffb: #>$dd04
What happens is that when the timer reaches 0 the NMI will trigger and jump to $dd04 (with the normal unstabile jitter).
Since the NMI-setup takes some cycles and the jitter takes some cycles the lobyte of the timer will be in the $e0-$f0 range approximatly then the JMP on $dd04 executes.
So, depending on timer jitter you will jump to $xx00 where (xx is typically $e0-$f0 approx.).
So, basically depending on jitter, independant of sprites and all other delays you will jump to a specific routine. And for example in the $e000 routine (where the delay is most sever) you need to store the $d016 immediatly, but in the $f000 routine you havn't delayed that much so add the correct amount of NOPs to hit the correct $d016 cycles.
Also, you must be sure that the NMI triggers after the sprite fetches and not inside them otherwise you'll get fooled when you NOP.
I.e. start the timer initially using a standard stable raster and make sure it reaches 0 just after the sprite fetches, or preferably as tight as possible to the $d016 write.
Well... have fun! :D
But yes... groepaz suggestion is perhaps the most common and most wize method to use. ;) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
jackasser: is that actually quick enough to work on every line and with a few sprites enabled? |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
@groepaz: Hmm yes, assuming no bad lines ofcourse. NMI setup 7 cycles, jitter max 8 cycles. Sprites 17 cycles. inc dec $d016 12 cycles. Acknowledge NMI 3 cycles (when done with a jmp to a RTI opcode before the ack io) + the rti 6. so 7+8+17+12+3+6 = 53 cycles. Then you have the JMP itself also so, an additional 3 cycles. Total 56 cycles worst case jitter.
Question is, why waste 56 cycles with this method, when there are more elegand ways to do it when it comes to this particular problem? :D |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
because i havent used this method before :=) |
| |
enthusi
Registered: May 2004 Posts: 677 |
I like timers but jumping into them is new to me. Evil. Beautyful. |
| |
Jetboy
Registered: Jul 2006 Posts: 337 |
Once you learn ninja method you wont want to use anything else. It's beautifull, its elegant, and its fast! Especialy if you are doing every second line fli, or every 4th line fli (more elaborate duiscusion on this topic in some other thread). Surely - there is quite some memory usage with that method, so its not so good as everday solution, but it serves it;s purpose. |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
ninja for president \:D/ |
| |
Mantiz Account closed
Registered: Apr 2006 Posts: 36 |
Thanks a lot Groepaz and JackAsser for the ideas and explanation! I'm trying to learn as much as I can so I will try both approaches and see which one works best. The idea with using timers is nice and very clever.
Regards,
Mantiz |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
"The idea with using timers is nice and very clever"
Yes, but not so appropriate to use if you for instance only have a simple 8x3 sprite big logo swinger or something (with open borders). Then you simply only have two timing cases, either 0 sprites or all sprites and you know exactly when the sprites start and end so all you have to do is to do some simple self modifying timing code.
The timer method is nice if you have either 1) rather complex timing due to dynamic motion of sprites, 2) if you need to do something VIC-trickery every now and then really quick (stable that is), for instance setting up a 4x4 mode.
Using the timer approach alone for setting up a 4x4 mode won't help you add dynamic sprites on top since the $d011 write to do the FLI must take place almost directly after sprite fetch #8, but your timers must stabalize BEFORE the sprite fetches. So, unless you have equall amount of sprites all lines you have a problem, since you don't know the exact amount to NOP (due to different sprite timing), to find the correct $d011 place.
What I want to say really is that, however beautiful timers are etc. etc. they don't solve all timing problems. Sometimes you still have to manually adjust the code or use some tables due to complex y-motions of sprites.
|
| |
HCL
Registered: Feb 2003 Posts: 728 |
Appearantly the question was not about 2x2- or 4x4-timing, but having random y-position of sprites with open sideborder. Ninja's 2x2-timing is beautiful, but totally useless here.
Ok, the only thing you really need to do is setting up a timer counting down from 62 to 0 infinitely. Then you simply stabelize your timing every line by the vaue of the counter. One example:
ldx #no_of_lines
loop:
sec
lda #62
sbc $dd04
sta *+4
bpl *
lda #$a9
lda #$a9
lda #$a9
lda #$a9
lda #$a9
lda #$a9
lda #$a9
lda #$a9
lda $ea
dec $d016
inc $d016
!?!?nops??
dex
bpl loop
|
| |
Style
Registered: Jun 2004 Posts: 498 |
I used this technique for a simple DYSP I recently wrote.
Every line I would jmp indirect to the timer which would compensate for lost cycles due to sprite data reads.
Works well. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
@HCL: I'd say your method with reading the timer and compensate, and using "indirect" jump in the timer-io with NMI-triggering is quite the same.
Your method doesn't need NMI-setup time and interrupt ackknowledge, but on the other hand it has to read the timer value explicitly and apply logic, where as the interrupt approach don't.
Dunno which is most efficient, but the underlying principle is exactly the same. |
| |
HCL
Registered: Feb 2003 Posts: 728 |
@JA: You're just confusing by introducing interrupts, and starting to talk about FLI-timing. It's a rather complicated way of solving this, that's my whole point. This guy started to learn this February, common <:).
..and yes, you can of course replace the whole branch-thingie with..
dec $d016
inc $d016
jmp ($dc03)
..if you can affort a code-explode :). |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
@HCL: My bad... :) However a jmp ($dc03) was TEH NICE. ;) |
| |
Oswald
Registered: Apr 2002 Posts: 5094 |
lol, funny to see that every coding question results in a who can do it better debate :) |
| |
Graham Account closed
Registered: Dec 2002 Posts: 990 |
The whole demo scene is a "who is better" debate. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
other than the cracking scene ofcourse :=D |
| |
Jetboy
Registered: Jul 2006 Posts: 337 |
Quote: other than the cracking scene ofcourse :=D
O'realy? :D |
| |
Mantiz Account closed
Registered: Apr 2006 Posts: 36 |
Thank you people, I am using the routine HCL described and it works good. I had a little bit of trouble finding out how to get the cia to start counting on the beginning of the rasterline but I added some nops etc to time it to start at the right place.
I have a question regarding the method that Groepaz suggested, I know that you can stretch/unstretch sprites on every rasterline, but how can you stretch them even more than that? Is the idea that you place a transparent sprite on the uppermost line where your sprites will be located, stretch it down until you reach the place where your real sprite should be displayed, unstretch, change the sprite pointer, display it and change sprite pointer again to the transparent and stretch it for the remaining number of lines? Isn't it going to need an awful long stretch if you are going to have an effect on a very large portion of the screen.
And while talking about stretching, if using the timing method that I currently have, there isn't enough time to change the sprite y-stretch on the lines where the borders are open, because the lda $a9 needs to be there. Is this correct? I guess it could be done if the loop is unrolled, because you can get rid of the dex and bpl. Gotta try that out :)
Cheers! |
| |
Stryyker
Registered: Dec 2001 Posts: 468 |
What you can do is have the first sprite line empty. You can stretch one sprite the whole screen if you wish. You stretch the blank line until you come to where you want it then turn off the stretch. It means eachraster line needs code to keep stretching (or not stretch).
With changing sprite pointers you can make 1 sprite have the detail equivalent of multiple sprites layered in the Y direction.
Say you stretch each sprite line 5 times you get 165 lines for the same sprite, like:
sprite line: sprite pointer:
0:0
0:1
0:2
0:3
0:4
1:0
1:1
1:2
1:3
1:4
2:0
etc.
So line 0 is shown for 5 consecative lines but by changing the sprite pointer it shows new data. Also on the last line of the 5 line stretch you don't stretch so the VIC internal counters move on to the next line. By being creative with how it is used you can do many things once impossible. |
| |
Jetboy
Registered: Jul 2006 Posts: 337 |
You may wonder how can you change multiple sprites this way - each would need pointer change each line - so it seems too cycle consuming. There is solution to that - switch character screen memory location each line - this way sprite pointes change for all 8 sprites at once (they are stored at the end of character screen memory - so they change with character memory change). This however is only good if you dont require asynchronic streches, coz this method fails there.
And i guess unroling the loop is the way to go. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
when i coded this (looooong ago .=D) i just used d017, with a blank first line in each sprite like jackasser explained. then you just need one register write each line which modifies all 8 sprites. (and the table for d017 changes can be calculated outside the rastercode). |
| |
Quetzal
Registered: Jul 2002 Posts: 71 |
I've done DYSP at various times using either NMI interrupts or a method very similiar to the one HCL outlined above. The NMI versions were more trouble than it was worth and had some rather bizarre bugs, but that's more than likely due to my inability to code in a way that any normal person would do it than anything else.
Incidently, I once made an intro for PARALAX that had DYSP capability built in for the sideborder scroller, but never got around to actually installing the extra code to move each sprite individually in Y. So the scroll just bounced up and down as a whole. Talk about a wasted effort!
|
| |
Mantiz Account closed
Registered: Apr 2006 Posts: 36 |
I simply can't thank you guys enough for your help on this subject. Finally understanding how things are done means a lot for me. I guess for seasoned coders, this stuff is child's play. It'll be a while before I know enough to be able to contribute with my knowledge, but I hope you like beer in case our roads will cross someday... :-)
|