Programming tutorial: Part 7–The basics of sprites

Atari Lynx programming tutorial series:

About sprites

The sprites in the Lynx are drawn by Suzy, the chip that has dedicated hardware for the manipulation and bit-blitting of the pixels in the sprite structure. Suzy runs at 16 MHz unlike Mikey (the enhanced 65SC02 processor) that executes at “only” 4 MHz. This results in a very fast and highly capable graphics engine that can pull off stuff like none of the other game consoles of the time. Personally, I consider Suzy a hardware accelerated graphics adapter amongst other things.

The sprites of the Lynx are data structures that describe how the sprite should be drawn. Actually, there are always two data structures involved: the Sprite Control Block (SCB, pronounced scub) and the sprite pixel data. The pixel data is what you expect it to be: a compact format that holds the various pixels in the sprite, including their colors indexes, and some line and quadrant information, but nothing else. The SCB holds the position, size, and manipulation data as well as the palette for the colors that correspond to the indexes in the sprite data, plus a pointer to the memory of the sprite pixel data.

This separation of the two sets of data is pretty smart. This way you can reuse the sprite pixel data in more than one SCB, allowing you to draw different sprites with the same pixel data  (e.g. an enemy robot) at various locations and with different colors, sizes and so on. Also, the SCB can contain a pointer to the next SCB structure, which enables the chaining of sprites, so you can draw multiple sprites by starting the drawing of just one sprite.

Drawing sprites

To create a sprite we will need a couple of things:

  1. Sprite pixel data
  2. A SCB structure with the details of the sprite
  3. Some code to initiate the drawing of the sprite.

Let’s get started to do each of these.

Creating sprite pixel data

The easiest way to create the sprite pixel data is to use the sprpck.exe tool. This tool can create the sprite data structure for you. The most important input for the tool is the bitmap image from which the data will be calculated. Sprpck knows about the packing formats that the Lynx uses. It works exclusively for the Lynx sprite data. Your project will likely contain several .bmp files that get turned into binary object files (.o files) with some other things.

We will use the bitmap image of a robot as the working example.

image

Since bitmap files are sources for pixel data files, it is best to include the bitmap in your project. This way they are kept with the code and can be used to regenerate pixel data if the need occurs.

The lynxcc65.mak make file contains a rule to build a .o file out of a .bmp file using sprpck.

# Rule for making a *.o file out of a *.bmp file
.bmp.o:
	$(SPRPCK) -t6 -p2 $<
	$(ECHO) .global _$(*B) > $*.s
	$(ECHO) .segment "$(RODATA_SEGMENT)" >> $*.s
	$(ECHO) _$(*B): .incbin "$*.spr" >> $*.s
	$(AS) -t lynx -o $@ $(AFLAGS) $*.s
	$(RM) $*.s
	$(RM) $*.pal
	$(RM) $*.spr

You will need to add robot.o to the targets of your make file. The make tool will find and use the rule above for the robot.bmp file and produce a couple of output files:

  • robot.spr (contains the sprite pixel data)
  • robot.pal (contains color palette information)
  • robot.s (assembler code)
  • robot.o (the final object file with the compiled pixel data and reference variables)

Here you can see how the SPRPCK macro (referring to sprpck.exe) is used on the bitmap file ($< is the .bmp source file, in this case robot.bmp). It has two parameters -t6 and -p2 that indicate a Windows Bitmap file and a LYXASS output file. The latter is for the assembler/compiler from Bastian Schick, but can be used in CC65 as well.

The output of the sprpck tool is a robot.spr file with the following contents:

image

We will take a detailed look at the meaning of the contents of the .spr files some other time. For now it is enough to trust that the correct pixel data will be there.

The next ECHO macros will create a text file with the name of the input bitmap but with the .s extension resulting in robot.s . It will create a snippet that looks like this for the robot.bmp file:

.global _robot  
.segment "RODATA"
_robot: 
.incbin "robot.spr"

This fragment is enough to link to the pixel data for the sprite that is contained in the robot.spr file. Notice how the code fragment contains a global _robot variable in assembler that links to the binary contents of the .spr file. It will be included in the RODATA segment for read-only data in the memory of the Lynx and allows us to use the address of the pixel data later on.

At the fourth line from the end the CA65 assembler is called to compile the robot.s file into an object file robot.o. All files except robot.o will be deleted at the end by the $(RM) statements. If you want to look at these intermediate files, prefix the lines with a # (hash) sign.

The rule from .o to .bmp is useful for creating a single pixel data file from the entire bitmap. It is also possible to create multiple sprites from a sprite-sheet, i.e. a bitmap that contains multiple equally sized shapes for sprites. That is covered in the next tutorial.

Sprite Control Block data structures

The next step is to create a data structure that will contain the necessary bytes and words for the correct display of the sprite. We will start with the minimum required amount of data in the SCB. A C struct defined in _suzy.h is going to help to easily work with such a SCB instance:

typedef struct SCB_RENONE {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos;
signed int vpos; } SCB_RENONE;

You can see that there are three bytes named sprctl0, sprctl1 and sprcoll that are the sprite control bytes. These define what the behavior of the sprite is. Some examples are whether the sprite is flipped horizontally or vertically, whether the sprite will stretch or tilt, and what the format of pixel data is.

The void pointer next is an address to the next SCB. This is often going to be 0x0000 indicating there is no next sprite.  Only when chaining SCBs will it have a non-zero value. After that there is a void* data variable with the address of the sprite pixel data. Finally, the hpos and vpos integer values (16-bit) are the x and y position of the sprite.

A typical SCB instance will look like this:

extern unsigned char robot[];
SCB_RENONE robotsprite =  { BPP_4 | TYPE_NORMAL,
REUSEPAL, 0x01, 0x0000, &robot, 20, 50 };

This will draw the robot as a normal sprite with 4 bits per pixel color depth (the maximum as defined in the constant BPP_4) without specifying a palette (using the last defined one) and has a collision depository of 0x01 (ignore this thing for now). It will not chain to a new sprite and uses the address of &robot to get to the pixel data. The sprite will draw at the (x,y) coordinates of (20,50).

Notice also how the char array called robot imports the _robot variable that was created earlier in the robot.s assembly file. This is a common pattern where the assembler variable name is prefixed with an underscore, and the C variable has the name without the underscore. It is imported using the extern keyword in C.

Drawing the sprite

With all the preparations done, we can now draw the sprite with a few simple instructions.

tgi_clear();
tgi_setcolor(COLOR_WHITE);
tgi_outtextxy(0, 0, "Sprite basics");
tgi_sprite(&robotsprite);
tgi_updatedisplay();
while (tgi_busy());

The code fragment shows how the screen is cleared and gets the text “Sprite basics” in the top left corner. The meat of the drawing is the tgi_sprite call, that will accept a pointer address of a SCB (robotsprite in our case) to start. tgi_sprite will then put Suzy to work and the drawing starts into the hidden screen buffer, assuming you have double buffering enabled as it is by default. When the call to tgi_updatedisplay is made it will wait for a VBL (vertical blank) and swap out the buffer showing the new screen with our freshly drawn sprite.

image

Okay, when you run this, it will probably not be as pretty as you might expect from the picture above. I’ve been lying to you about these simple sprites. The sprite engine needs to have its color palette set at least once. We have not done so, and as a result the sprite will never show properly. The theory is true, though. Hold on till we get to color palettes and pen indexes. Trust me when I say it is better for didactic purposes.

We need to repeat drawing the sprites for every screen, as the current logic is to clear first. Some more advanced scenarios are possible should you be able to do partial updates of the screen.

Next time

The sprite engine deserves a lot more attention, so the next couple of episodes will focus on more capabilities of sprites and how to program them. Stay tuned as always.

This entry was posted in Tutorial. Bookmark the permalink.

21 Responses to Programming tutorial: Part 7–The basics of sprites

  1. Pingback: Programming tutorial: Part 8–Changing appearances | Diary of an Atari Lynx developer

  2. Pingback: Programming tutorial: Part 9–Advanced sprites | Diary of an Atari Lynx developer

  3. Gadget says:

    These tutorials are excellent, thanks for taking the time to publish them! I am having trouble trying to work out where the constants SPRCTL0_4_bits_per_pixel etc come from, what do I need to include?

    • alexthissen says:

      Thanks for the compliments.
      SPRCLT0_4_bits_per_pixel come from lynx.h. So you will need to include this line:

      #include <lynx.h>

      It is usually included already, but it might be that you’re including some other file first that does not include it itself.

      #include “custom.h” // Custom.h uses constants from lynx.h
      #include <lynx.h>

      This means you need to swap the declarations around or include lynx.h from your custom.h file.
      Hope this helps. Let me know if not.

  4. Gadget says:

    Hmm, they aren’t in my lynx.h? I’ve got passed this by using the contants from Karri’s pong tutorial:-
    sprite_simple robotsprite = {
    BPP_4 | TYPE_NORMAL, LITERAL, 0x01,
    0x0000,
    &robot,
    20, 50
    };

    Which is fine, but now I dont seem to have sprpck – any idea where to get that exe?

    • Gadget says:

      Got the exe but it must be an older version or something as I am now getting an another error:-

      E:\CC65\sprpck\sprpck -t6 -p2 robot.bmp
      1>NMAKE : fatal error U1077: ‘E:\CC65\sprpck\sprpck.EXE’ : return code ‘0xc0000135’
      1>Stop.
      1>Project : error PRJ0019: A tool returned an error code from “Performing Makefile project actions”

      • alexthissen says:

        I’m sure you got this figured out, but just in case: if the Error Window of Visual Studio reports an error, it is best to check the Output Window for detailed information. Usually it is easier to figure out what is going on/wrong than from the short message in the listitem of the Error Window.

  5. Gadget says:

    Sorted…

    cygwin.dll was required by the exe. For anyone else reading this, sprpck is contained within the BLL dev kit – I just couldnt see it. And the DLL is there as well.

  6. Gadget says:

    Using 1.97 atm… Having a lot of trouble with pixel format of images now =/ I had it working on some sprites – 4bit .bmp, then after tinkering with Fireworks and PSP 9.0 my images are screwed, despite being 4bit still and showing the correct palette.

  7. Pingback: Programming tutorial: Part 10–Collisions | Diary of an Atari Lynx developer

  8. Pingback: Programming tutorial: Part 11–Pens and more collisions | Diary of an Atari Lynx developer

  9. I don’t know if this page is still alive but I’m having problems with SPRCTL0_4_bits_per_pixel as well. The solution I used is putting this code in sprites.h:
    #define SPRCTL0_1_bit_per_pixel 0
    #define SPRCTL0_2_bits_per_pixel 0x40
    #define SPRCTL0_3_bits_per_pixel 0x80
    #define SPRCTL0_4_bits_per_pixel 0xc0
    #define SPRCTL0_h_flip 0x20
    #define SPRCTL0_v_flip 0x10
    #define SPRCTL0_shadow 7
    #define SPRCTL0_xor_shadow 6
    #define SPRCTL0_non_collide 5
    #define SPRCTL0_normal 4
    #define SPRCTL0_boundary 3
    #define SPRCTL0_boundary_shadow 2
    #define SPRCTL0_background_no_coll 1
    #define SPRCTL0_background_shadow 0

    #define SPRCTL1_literal 0x80
    #define SPRCTL1_hsize_vsize_strech_tilt 0x30
    #define SPRCTL1_hsize_vsize_strech 0x20
    #define SPRCTL1_hsize_vsize 0x10
    #define SPRCTL1_no_palette 0x08
    #define SPRCTL1_skip_sprite 0x04
    #define SPRCTL1_start_drawing_up 0x02
    #define SPRCTL1_start_drawing_left 0x01

    #define SPRCOLL_dont_collide 0x20
    #define SPRCOLL_number_0_3 0x00

    Off course ‘include’ sprites.h in my program.
    Then it worked. 🙂

    Hope this helps others.

    • alexthissen says:

      Still alive. 😉
      You are right. It does not work (anymore) like shown in the code fragments above. The CC65 compiler suite has since moved on and the latest include files use different values for sprite constants. I will update this part of the tutorial tonight and look through the others as well.
      Thanks for the pointer.
      Are you based in The Netherlands, BTW?

      • Ah..good it’s alive cause I’m struggling like hell with the Lynx 😉
        And no, not NL but very close..Belgium..I’m Dutch though..live in Belgium for 10 years now…and you?
        Grts
        Tony

      • alexthissen says:

        Hi Tony,
        I live in The Netherlands, Hedel to be exact. Pretty close to Belgium.
        Can you tell me what you are struggling with? Maybe I can help. Feel free to contact me at my email address (first and last name concatenated at hot mail).
        Alex

      • Oh yeah..forgot..thanx for the GREAT tutorials, they’re really helping me 🙂

      • alexthissen says:

        Thanks very much. I really appreciate this thankyou. I am glad that you are using them and find them useful. Priority requests for a new tutorial part are always welcome.
        I’ll make sure the source code is also rereleased, as I did a lot of updates.
        Alex

  10. hikachii says:

    Thanks for this amazing tutorial. Makes me want to try write some Lynx games. Unfortunately i got stuck on easy part – sprpck.exe thing, where should i put that file. For example i try to compile Collisions (from http://atariage.com/forums/topic/206456-atari-lynx-programming-tutorial/ tutorial zip)

Leave a comment