Jump to content
RSA

Efficient soft sprite library for mode E?

Recommended Posts

There are probably many titles using soft sprites in mode E when P/Ms are not enough. Considering that we have multiple pixels per byte, though, the logic needs shifting and masking, so it's not as simple to draw in assembly in this mode as with more modern hardware with 1+ bytes per pixel.

 

One example is Prince of Persia 8-bit which combines both for wide animations (especially when jumping) with nice coloring. But I can't find any library available for doing this or even plotting dots or lines efficiently. I would rather avoid slow OS routines. Do folks really re-invent the wheel for their own games? Or are implementations too optimized for specific needs to be reusable? At least in PoP it seems that it's a classic case of animating images on top of the background with transparency. I don't see special cases at play there, so that code would be usable in general.

 

I wanted to check before I go ahead and write my own 6502 implementation or try to reverse engineer the code from PoP 8-bit (or maybe from the Apple II code if it's close enough).

Edited by RSA
  • Like 1

Share this post


Link to post
Share on other sites
19 minutes ago, RSA said:

Do folks really re-invent the wheel for their own games? Or are implementations too optimized for specific needs to be reusable?

In short, "yes" and "yes".  

 

The Atari's graphics capabilities are impressive, but employing them to their greatest effect is at odds with using a general-purpose library. 

  • Like 4

Share this post


Link to post
Share on other sites

There are a few sprite routines in MADS examples that you can look at, but you will find out that they are for specific use-cases. Each game type using soft-sprites requires different approach depending on number of expected sprites, their size, their moving capabilities (horizontally, vertically, free), their velocity, if sprites need different animation frames, background they move on, display list organization, screen size and other factors. 

  • Like 1

Share this post


Link to post
Share on other sites
8 hours ago, RSA said:

Do folks really re-invent the wheel for their own games? Or are implementations too optimized for specific needs to be reusable?

As @mellis said - yes :)

 

Problem is that there's no such thing as simple gfx mode on a8 imho ;)

 

Mode E is perfect example. Two bits per pixel, 4 pixels per byte, scanline is from 32-48 bytes wide, put as many scanlines as you like in display list and you're done, right ?

 

Wrong ;)

If you want static screen it's simple, you need at least one more LMS in middle of screen because Antic can only address 4kb or memory.

You're clever so you put middle between two 4k banks in middle of screen. So your bitmap memory starts from  something like "middle address - 3840 bytes" and goes up to "middle address + 3839" for 160x192 screen.

You need some multiply with 40 table or similar but form this point on soft sprites are simple. Load frame byte from memory, shift, ora, sta.
Or you need masking ? Lda mask, shift, and screen, sta temp, lda sprite byte, ora temp sta screen...

You need speed ? ldx sprite, lda left_part_shifted_by_offset,x, ora screen, sta.

Speed and masking ? You know what goes next ;)

 

And this is with simplest possible mem layout. Need scrolling ? You're clever and going to move entire screen by just changing LMS addresses. One on top of screen, one on "middle", then one more when that 4Kb segment gets moved up... calculate those offsets, figure out where your sprite should go, make special case for when sprite goes over segments where one part of sprite goes in upper part of mem, other part of sprite into lower part of mem... It's a nightmare.

 

One more :) You need +1 color or scrolling is too hard? You use character mode. Reserve charsets to have enough different shapes. You need to change chbase from time to time, draw char matrix where sprite will be, find out which chars were under it before, draw in vblank so it doesn't flicker.. etc etc etc...

 

But hey, it's more than doable imho :)

 

Funny that you mention PoP, I've just found one of my oldes examples of soft sprite, and it's in mode E with PoP sprite :)

This one is xor sprite (drawn with eor, erased with eor).

 

Rest of videos bellow are with charmode sprites.

 

 

 

 

 

 

Gtia mode (much much much simpler but blocky):

 

 

  • Like 10

Share this post


Link to post
Share on other sites

Yes, reinventing the wheel and loving it!

 

The sprite code in PoP could be considered usable for a generic modeE sprite routine but it takes several KBs so it may not be the best for your use case. The performance is about as good as it gets in theory but it's still really slow.

 

Very few games need large sprites with lots of animations (I can only think of fighting games and most ones on the A8 don't draw over a background) so there may be other options.

 

fcatrin started a PoP a long time ago and wrote his sprite routine, it's quite similar but his takes less memory while mine is a little faster and supports clipping. It's available here I believe:  https://github.com/fcatrin/a8pop

 

  • Like 4

Share this post


Link to post
Share on other sites
16 hours ago, ilmenit said:

There are a few sprite routines in MADS examples that you can look at, but you will find out that they are for specific use-cases. Each game type using soft-sprites requires different approach depending on number of expected sprites, their size, their moving capabilities (horizontally, vertically, free), their velocity, if sprites need different animation frames, background they move on, display list organization, screen size and other factors. 

 

I see, thanks. Many examples there. In https://github.com/tebe6502/Mad-Assembler/tree/master/examples/LIBRARIES/graphics/lib I see a procedure for plotting a single point and others to draw lines and circles using it. The plotter seems to use a precalculated table to access the y^th line in screen memory, which is what I had in mind to avoid multiplying y by 40 at runtime and jumping the 4K boundary. It seems a good starting point. To draw an image efficiently, of course, the logic would have to be smarter than calling plot for every pixel; it would have to draw each horizontal line inside it.

Share this post


Link to post
Share on other sites
12 hours ago, popmilo said:

Problem is that there's no such thing as simple gfx mode on a8 imho ;)

Ha, yes. Unless we draw on the TV screen with a sharpie :P :D 🤡!

 

12 hours ago, popmilo said:

Mode E is perfect example. Two bits per pixel, 4 pixels per byte, scanline is from 32-48 bytes wide, put as many scanlines as you like in display list and you're done, right ?

 

Wrong ;)

If you want static screen it's simple, you need at least one more LMS in middle of screen because Antic can only address 4kb or memory.

You're clever so you put middle between two 4k banks in middle of screen. So your bitmap memory starts from  something like "middle address - 3840 bytes" and goes up to "middle address + 3839" for 160x192 screen.

You need some multiply with 40 table or similar but form this point on soft sprites are simple. Load frame byte from memory, shift, ora, sta.
Or you need masking ? Lda mask, shift, and screen, sta temp, lda sprite byte, ora temp sta screen...

You need speed ? ldx sprite, lda left_part_shifted_by_offset,x, ora screen, sta.

Speed and masking ? You know what goes next ;)

 

And this is with simplest possible mem layout. Need scrolling ? You're clever and going to move entire screen by just changing LMS addresses. One on top of screen, one on "middle", then one more when that 4Kb segment gets moved up... calculate those offsets, figure out where your sprite should go, make special case for when sprite goes over segments where one part of sprite goes in upper part of mem, other part of sprite into lower part of mem... It's a nightmare.

 

One more :) You need +1 color or scrolling is too hard? You use character mode. Reserve charsets to have enough different shapes. You need to change chbase from time to time, draw char matrix where sprite will be, find out which chars were under it before, draw in vblank so it doesn't flicker.. etc etc etc...

 

But hey, it's more than doable imho :)

 

Yeah, apparently people have done it, and some of you have even survived it :D  Thanks @popmilo for those insights. I did notice that needing 40 bytes per line wastes 16 bytes at the end of the 4K block, but using an address table for line starts as you mention takes care of that and also of the y multiplication by 40 (simpler than doing ASL by 3 and 2 more to achieve the same result).

 

Drawing and animating on character sets is even more complicated. I did that to draw variable-width text on background tiles for the GameBoy 20+ years ago, but it wasn't animated. Your animations are impressive. Have you published the code for those examples?

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, rensoup said:

Yes, reinventing the wheel and loving it!

Ha, true. It's part of the fun, of course. But I'd still try to reuse existing code for speed of development, or at least look at examples to find out about any tricks or pitfalls I might not be aware of.

 

1 hour ago, rensoup said:

The sprite code in PoP could be considered usable for a generic modeE sprite routine but it takes several KBs so it may not be the best for your use case.

Are you using pre-shifted copies of each animation frame to speed up the drawing? Or is it simply due to the large number of frames?

 

1 hour ago, rensoup said:

Very few games need large sprites with lots of animations (I can only think of fighting games and most ones on the A8 don't draw over a background) so there may be other options.

For PoP or fighting games, sure, but other kinds of games can have a larger number of smaller animations, like platformers or shooters, and the technique needed is the same, right? Assuming the sprites move around freely, or else the code can be further optimized to restricted forms of animation.

 

1 hour ago, rensoup said:

fcatrin started a PoP a long time ago and wrote his sprite routine, it's quite similar but his takes less memory while mine is a little faster and supports clipping. It's available here I believe:  https://github.com/fcatrin/a8pop

Nice pointer, will look into it. Thanks @rensoup.

Share this post


Link to post
Share on other sites
33 minutes ago, RSA said:

Are you using pre-shifted copies of each animation frame to speed up the drawing? Or is it simply due to the large number of frames?

Nope it's just for the mask and shift tables. Oh yeah I forgot it does mirroring as well so there's a table for that too!

Preshifted copies would be impossible with 128KB of RAM (or without a cart).

I think there's about 3.5KB of tables + a few hundred bytes of unrolled code ?

 

35 minutes ago, RSA said:

For PoP or fighting games, sure, but other kinds of games can have a larger number of smaller animations, like platformers or shooters, and the technique needed is the same, right? Assuming the sprites move around freely, or else the code can be further optimized to restricted forms of animation.

Depends what your target frame rate is. A sprite as large as those in PoP easily takes 2/3 of a frame to plot, if not more !

I also take advantage of the fact that there are large empty areas in most sprite bitmaps: the sprites are split in up to 3 smaller slices with tighter bouding boxes.

 

Sure, smaller sprites with lots of animations which are displayed just once per frame could be reasonable candidates (the main character for a platformer for instance). 

  • Like 2

Share this post


Link to post
Share on other sites
On 12/3/2021 at 2:54 AM, RSA said:

Have you published the code for those examples?

Plan was to make these routines nicely commented and easy to use. Kind of library you mentioned. Include and just code higher level stuff...
As complexity grew, I was just happy that it worked at all ;)

 

Here is zipped folder that can be compiled using Mads. It's example of soft sprites in char mode.

 

char_sprites.zip

  • Like 3
  • Thanks 2

Share this post


Link to post
Share on other sites

popmilo,

 

I not dealing with char masking but.  Screen byte bit masking.  I was able to get the masking overlay to work but the result makes all the colors that overlap a different color.   Is there a trick to make one of the two (S or D) bytes color a priority.  I'm Using Action! so my routine looks like below:  D dest byte, S source byte, N new merged byte.   There can be many combinations of overlap colors.

\ /  = X  (where the center point of the X is a different color) I would like to able to make S(0) color priority.   

M1 = D(0) ! 255   

M2 = S(0) ! 255

N = M1 ! M2

 

 

 

Share this post


Link to post
Share on other sites
On 12/21/2021 at 12:39 AM, rsh said:

popmilo,

 

I not dealing with char masking but.  Screen byte bit masking.  I was able to get the masking overlay to work but the result makes all the colors that overlap a different color.   Is there a trick to make one of the two (S or D) bytes color a priority.  I'm Using Action! so my routine looks like below:  D dest byte, S source byte, N new merged byte.   There can be many combinations of overlap colors.

\ /  = X  (where the center point of the X is a different color) I would like to able to make S(0) color priority.   

M1 = D(0) ! 255   

M2 = S(0) ! 255

N = M1 ! M2

Don't know why you're doing xor (!) with 255, that gets you inverse of original byte values ?


But if I understood you well, you wanna have something like this:
image.png.7e5e0bafe5346eadf74d24dfe72927a9.png

 

You have two options:

1 - just do "or" between S and D:   N = S % D

xx or 00 = xx
xx or 01 = x1
xx or 10 = 1x
xx or 11 = 11

 

So if 11 bitpair is yellow, you'll always get yellow on top. Where D is 00, you'll get value from S whatever that is.
This method is not universal because you get kind of mixing for other combinations.

To get full possible range you need mask bits. Best for something like this is to use "auto generated mask".
You make lookup table (256 bytes long array for example).

Let's say "i" is index into that array. Where  ever index has bitpair with "x1","1x" or "11" your mask value is "00", where ever you have "00" in index you have "11" in mask.

Something like:
index                mask
00000000 => 11111111
00000001 => 11111100
00000010 => 11111100
00000011 => 11111100
00000100 => 11110011
00000101 => 11110011
etc...

Then you do this:
N = (S & mask(D)) % D

First & will "and" mask with S, erase pixels where D pixels need to go.
Then you just "or" D with that and pixel values from D will be written in those clear spots.
Each pixel can be any of 4 values, done :)

ps. If you need that mask table, you could write generator in action basic, my guess something like this:

BYTE ARRAY M(256)

FOR I=0 TO 255
DO
 P3 = (I RSH 6)
 IF P3>0 THEN
  P3 = 0
 ELSE
  P3 = 128+64
 FI

 P2 = (I RSH 4) & 3
 IF P2>0 THEN
  P2 = 0
 ELSE
  P2 = 32+16
 FI

 P1 = (I RSH 2) & 3
 IF P1>0 THEN
  P1 = 0
 ELSE
  P1 = 8+4
 FI

 P0 = I & 3
 IF P0>0 THEN
  P0 = 0
 ELSE
  P0 = 2+1
 FI

 M(I) = P3+P2+P1+P0
OD

ps. Sorry for late answer, I've just seen your post now by accident :)

Happy coding !

 

Share this post


Link to post
Share on other sites
On 4/5/2022 at 1:15 PM, popmilo said:

ps. Sorry for late answer, I've just seen your post now by accident :)

Happy coding !

 

yes the  XOR 255 was not needed lol.. I figured that out later .. I got something to work that was satisfactory...  However your table approach has full control.  

I will see if I can add that in later.. it may solve my other issue I need to correct...  appreciate the great response! 

Share this post


Link to post
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...