Sprite driver rewritten
Sprite driver has been rewritten to work with the new kernel. I've also written an alternative sprite driver that improves performance, though there is a minor tradeoff when using it. I've set OPTIMIZE to 1, which causes some extra 6507 code to come into play. The extra code extends the display by 10 scanlines in order to show what's left in TIM64T for Vertical Blank and Over Scan. Some TVs may not like displaying a 272 scanline image, so it's possible this ROM will roll for some people. My C=1084S displays it just fine, so I find it useful for testing how code changes will affect performance.
- RESET = "start game"
- SELECT = return to "menu" (rainbow screen)
- Left Joystick - move humanoid. Move to edge of room to trigger room change.
- Right Joystick - move Otto
- TV Type Color = Original sprite flicker routines, B&W = alternative sprite flicker routines
- Left Difficulty B = Berzerk Rooms, A = Frenzy Rooms
- Right Difficulty B = Normal robot distribution, A = Maximum robot distribution, lined up horizontally for extra flicker.
The VB/OS display will change color based on which sprite routine is active. Green = original flicker logic, Grey = alternative flicker logic.
Left & Right difficulty setting changes only take affect when changing rooms.
Example of original flicker routine with C (12) time remaining in Vertical Blank.
Alternative flicker routine with D (13) time remaining in Vertical Blank
Flicker Logic
Ideally when playing the game we'd like to see non-flickering sprites such this:
However, the Atari 2600 only has 2 players to draw the sprites with. In order to show more than 2 objects in a row we could use NUSIZx to set duplicate and triplicate mode (like in Space Invaders), but doing so will cause the missiles to also show up in duplicate or triplicate mode. Space Invaders doesn't use the missiles (all shots are drawn with the ball object), but we don't have that option so we'll use flicker. Traditional Atari games will dedicate one of the players to the human player and flicker the other player for all other in game objects. This is due to limited processing time on the 2600. With the extra horsepower of the ARM chip, the routines in Frantic will use both players to flicker all objects. Surprisingly, Amidar uses this method as well.
Psuedocode for the flicker logic is this (if you'd like to see the actual code, look for function DisplaySprites in file main.c which is located in the directory custom😞
loop AGE from OLDEST_AGE to 0, decrement AGE by 1 each time thru loop loop i from 0 to MAX_SPRITES if sprite[i] is active and spriteage[i] = AGE can we draw with player0? yes - draw it, set spriteage[i] to 0 no - can we draw with player 1? yes - draw it, set spriteage[i] to 0 no - increase spriteage[i] endif endloop i endloop AGE
Spriteage is how many frames since that specific sprite was last displayed. If there's no flicker then spriteage will always be 0.
Basically the above logic tries to draw sprites with the oldest age first. It then tries to draw the next oldest sprites, and so on until we've processed all ages.
Frantic supports 24 sprites, numbered 0-23. Due to the way the original routine was written, the lower numbered sprites ended up with a higher priority in the flicker logic. Because of this priority, I used sprite 0 for the humanoid and sprite 1 for Evil Otto. Flicker of the humanoid and 2 robots using the original routines would look like this:
The problem with the above flicker logic is the more flicker there is, the longer it takes to process because of the outer loop. If the max age is 0 we only go thru the loop one time, but if the max age is 3 then we go thru the loop 4 times. The idea I had Thursday night was to eliminate the AGE loop by changing the code to work like this (actual code in function DisplaySpritesNew in main.c😞
loop i from 0 to MAX_SPRITES set SHOWN to FALSE x = FLICKERLIST[i] if sprite[x] is active can we draw with player0? yes - draw it, set SHOWN to TRUE no - can we draw with player 1? yes - draw it, set SHOWN to TRUE endif if SHOWN add X to end of FLICKERLIST2 else add X to start of FLICKERLIST2 endloop i copy FLICKERLIST2 to FLICKERLIST
Now, instead of keeping track of ages, we keep a list that's in the order to attempt to draw the sprites. If a sprite isn't drawn it goes to the front of the list, if it is drawn it goes to the end. Example list of 5 sprites:
0, 1, 2, 3, 4
We attempt to draw 0, since we can it goes to the end of the new list:
_, _, _, _, 0
We next attempt to draw 1, since we can it goes to the end of the new list:
_, _, _, 1, 0
We attempt to draw 2, we can't so it goes to the front of the new list:
2, _, _, 1, 0
We can draw 3 and 4, so the new list (for next screen update) is this:
2, 4, 3, 1, 0
The new routine works well and is more efficient than the old routine as seen in the photos at the top of this blog entry. However, the priority for lower numbered sprites no longer works so instead of the above example where the humanoid didn't flicker, I'd see this:
or this:
What I ended up doing was instead of adding sprites to the start or end of a single list, I added sprites to the start of two lists that were then merged together. The psuecode for the lists is:
loop i from 0 to MAX_SPRITES ... if SHOWN add X to SHOWN_LIST else add X to NOT_SHOWN_LIST endloop i FLICKERLIST = NOT_SHOWN_LIST + SHOWN_LIST
Example list of 5 sprites:
0, 1, 2, 3, 4
We attempt to draw 0, since we can it goes in the shown list:
SL = 0, _, _, _, _ NSL = _, _, _, _, _
We next attempt to draw 1, since we can it gets added to the shown list:
SL = 0, 1, _, _, _ NSL = _, _, _, _, _
We attempt to draw 2, we can't so it goes in the not-shown-list:
SL = 0, 1, _, _, _ NSL = 2, _, _, _, _
We can draw 3 and 4, resulting in:
SL = 0, 1, 3, 4, _ NSL = 2, _, _, _, _
The merge step combines those lists into the new flicker list:
FL = 2, 0, 1, 3, 4
With this revision, the flicker ends up like this:
It's not as good (for the humanoid) as the original routine, but much better than the initial rewrite and we still have the better performance. For ease of comparision, here's all the flicker examples lined up:
ROM
Source
10 Comments
Recommended Comments