# Dustlayer

Retro means old but cool.

I grew up with the Commodore C64 but was never able to master the machine. I was young, I wanted to play the latest games and let other people do the pioneer work on exploring this incredible hardware. Today I have better skills to catch up on what it takes to code the C64. I will share what I learn along the way. Enjoy the trip to the past!

### Filtering by Author: actraiser

# VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

April 29, 2013 / actraiser

- VIC-II for Beginners Part 1 - When Visibility Matters
- VIC-II for Beginners Part 2 - To have or not to have Character
- VIC-II for Beginners Part 3 - Beyond the Screen, Rasters and Cycles
- VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen
- VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

### ​Games Unlimited

Hardware Sprites in addition to the Soft Scrolling capabilities were major reasons why the Commodore C64 was the ultimate gaming machine in the 80s. Sprite-based games were very popular in Arcades and converting them to the C64 often turned out not to be too difficult plus the outcome was sometimes exceptional good by home computer standards. It's no big secret that the availability of masses of games whether they were exclusive or conversions won the market for the C64 against the competition by Sinclair, Amstrad and Atari.

Thanks to the VIC-II we can officially move 8 sprites independently over the screen at any time - but when combined with Interrupt programming and a method called Sprite-Multiplexing much more action is actually possible. Many commercial games in fact move 16 or 24 sprites over the screen. Armalyte even manages to display up to 30 Sprites at the same time which is an exceptional achievement within a game. But how do Sprites work, how are they stored and retrieved in memory, how can we flip through frames to animate our Sprites?​

That will be the topics for the final part of the VIC-II beginners series. Note that I enumerate the eight Sprites from from 0 to 7 which corresponds nicely with a few sprite-mapped registers where a Bit#0 corresponds to some configuration for Sprite#0, Bit#1 for Sprite#1 etc - so when I refer for example to the 5th Sprite we talk about Sprite#4.

### ​Standard and Multicolor Sprites

A Standard Sprite on the C64 is a 2D-Object drawn in a 24x21 grid. 24x21 results in 504 Bits or 63 Bytes of required memory per Sprite Shape. The term Sprite Shape refers in a strict sense to one single frame of a Sprite which can consists of many individual frames to generate the illusion of motion when played back consecutively. In a day by day discussion the expressions Sprite and Sprite Shape are usually exchangeable though. Finally, for calculations convenience a trailing Zero Byte is added to each Sprite Shape which increases the total required memory to 64 Bytes per shape.

Our C64 Sprites come in two flavors - Standard and Multicolor. The Standard Sprite has a 24x21 matrix with one shared Background Color and one individual Foreground Color.​ Then there is once again a Multicolor option for Sprites which follows by the same principles as seen in Multicolor Character Mode or Multicolor Bitmap Mode. The horizontal resolution is cut in half which brings us down to a 12x21 grid. As with the other Multicolor Modes two Bits are paired at a time to enable us encoding up to four different values into each of the wide pixels. Those values again represent different sources of color information. Let's enlarge upon those two modes.

In Standard Mode you can choose one Foreground Color per Sprite and a shared Background Color applicable to all Sprites. The individual Foreground Color per Sprite is stored in the 8 Registers located from $D027 to $D02E - as expected by storing a value from $00 to $0F. ​The Background Color corresponds to the content of $D021. For all Registers which store color information apply that only Bit#0 to Bit#3 are taken into account while the higher Bits are ignored. For example, setting $D021 to $01 (%00000001) or to $81 (%10000001) will both set Background Color to White as $01 is the color code for White and the upper four Bits are ignored.

Multicolor Mode can be turned on for each of the 8 Sprites individually so mixing Standard and Multicolor Sprites is possible. When Multicolor is activated the Sprite has as expected four different color sources encoded in the paired horizontal pixels, namely the individual color per Sprite retrieved from $D027 to $D02E, the shared Background Color from $D021 and two additional shared colors stored in $D025 and $D026. ​Each Bit in the sprite-mapped register $D01C determine wether a Sprite is configured for Standard or Multicolor Mode.

Below we have a Standard Color Sprite in the 24*21 grid from the game Thrust and next to it a Multicolor Sprite with the 12*21 Matrix from the game Wizball. When you look at the Game Screenshots you will hardly notice that one of the Sprites has only half of the horizontal resolution. It's not only that actual dimensions in Wizball have not really changed as pixels were just paired but also the additional colors make the Multicolor Sprite just look much better. That's why Multicolor Sprites are usually what you will encounter in the C64 world of games - unless it's a 1:1 conversion of a Sinclair Spectrum game.​​

Simple 24x21 sprite in Standard Mode ripped from the game Thrust.

​The popular Sprite Mode is the Multicolor Sprite - here used in the game Wizball.

​Thrust sprite in the game

​The animated sprite in the game

### Collision Detection on the Interrupt

Of course Sprites would be kinda pointless if you can not make them hit anything in a game. Whether it is a collision with background graphics, say a tree or with other sprites in a game - you want to be able to get notified by the C64 when something happens. In Episode 2-3 of our introduction into C64 intros I explained how we ask the VIC-II to let us know when something of interest occurs, e.g. when the Raster Beam reached a certain line on the screen. Other topics of interest the VIC-II can be asked for are in fact collisions between two Sprites or the collision between a Sprite and Background graphics.

Interrupts revisited

There are two important registers, $D019 and $D01A. The Interrupt Request Register $D019 monitors four different sources of interrupts all the time. Bit#0 - Bit#3 do nothing but set Bits low and high depending on whats is going on in the system in respect to any of four different interrupt sources. The sources for Interrupts by the VIC-II are either Raster Interrupts, Lightpen interation and two Interrupts for Sprite Collisions. So two of those Bits monitor the Sprite activities, namely Bit#2 for Sprite-Sprite-Collisions and Bit#1 for Sprite-Background Collisions.​ Whenever Bit#2 is set high then two Sprites must have collided and if Bit#1 is set high a Sprite has collided with Background - and by Background the VIC-II is referring to anything non-Sprite on the screen plus which is not using the color stored in $D021.

Now that we know there is a place where activities are monitored all the time we want to ask the VIC-II to let us know when a certain event has happened. For this we need D01A, the Interrupt Mask Register, sometimes also called Interrupt Enable Register. Bit#0 - Bit#3 are identical to the corresponding Bits in $D019 but their job in $D01A is to command $D019 to let us know when any of the events we set to high in the Mask Register occur. Using $D01A we basically ask $D019 to trigger an interruption so we can react to the event.

After we have configured our desired event in $D01A we just need to wait until $D019 sets Bit#7 high. This is the signal that an event we asked an interruption for has indeed happened.​ If we configured more than one event in $D01A we additionally need to check which of the four Bits #0-#3 have been set high in addition to Bit#7 in $D019.

Finally once an Interrupt Bit has been set, it's "latched" in and must be cleared by writing a 1 to that Bit in the Interrupt Request Register as soon as you're ready to handle it. This allows selective interrupt handling.

Let's apply that knowhow by looking at two use cases.

1. Sprite-Sprite Collision

In the Interrupt Mask Register $D01A we need to set Bit#2 high to ask for an interruption in case of a Sprite-Sprite collision. As soon as two Sprites collide $D019 will set Bit#2 and Bit#7 high accordingly. Bit#2 will be set high because it does this anyways - remember $D019 monitors activities all the time and Bit#7 is set because we specifically asked to be notified about that event. But there is more information we get. Once the interruption happens the Register $D01E will come into play. Each Bit in $D01E corresponds to one of the 8 Sprites. The Sprite Bits which are involved in the collision will be set high. We now have everything in place to react to a collision: a notification when collision happens and the actual Sprites involved. Now when the interrupt is triggered we could for example playback an explosion animation for the involved Sprites or increase the Scores or what not. By the way after the interrupt was triggered. we need to clear Bit#7 again in $D019 by acknowledging the IRQ. It will not reset automatically.​

​Registers affected when two Sprites collided and notification by Interrupt was requested.

2. Sprite-Background Collision

When you have understood Sprite-Sprite-Collision, then this is now very easy. Again we ask for interruption for the event of collision, this time by setting Bit#1 in $D01A as we now are interested in Sprite-Background Collisions. Again $D019 will set Bit#7 high in addition to Bit#1 when the collision takes place after we registered it in $D01A. This time $D01F which is again mapping each of its Bits to one of our 8 Sprites will let us know which Sprite was involved in the collision.

​Registers affected when a Sprite collides with the Background and notification by Interrupt was requested.

There is one more thing​...

To keep you interested I waited to share the following. The above way of detecting collisions is not really a viable one but just coding "by the book". Some games rather implement their own detection routines for sprite/sprite collisions using bounding boxes which is much more efficient and above all make more sense for most use cases. Also Sprite/Background collisions are rather detected by scanning the character memory map and keep track how they relate to a Sprite position.

Anyways, it does not hurt when you understand how things work the official way!

### Output to the Screen - Ready, Set, Go!

To display a Sprite on the Screen we need to enable it. The 8 Bits in $D015 turn on and off any of our Sprites. To move Sprites we need to position them and for this we have 17 Registers from $D000 to $D010. 8 Registers store the X-Coordinate (horizontal position) of any of the 8 Sprites, another 8 Registers the Y-Coordinates (vertical position) accordingly. Since we know already that our Canvas for Sprites is 512x256 large it is clear why we additional need a 9th Bit for each of the 8 X-Coordinates. The Register $D010 is that sprite-mapped Register helping out with that extra Bits we need to pass Position 255 horizontally. Don't confuse this Register with $D011 by the way - its Bit#0 functions as 9th Bit as well but for the Raster Line counter in collaboration with $D012.

Sprites can be moved freely across the screen. In fact they are not limited to the 320x200 area but their scope of activity spreads over a 512x256 grid - this makes it possible to position Sprites off-screen and let them move into the visible area. ​This really needs to be remembered as it is crucial when working with the the positioning - Sprites can cover a much larger canvas than the visible area of the C64 screen.​

​The area where Sprites can be positioned is actually much larger as the visible screen area

When positioning a Sprite using X/Y coordinates, the upper left Pixel of the Shape is the determining one. In other words, when you put a Sprite at Position X=50 and Y=50 then this is exactly the position on screen where the first Bit of your Sprite is drawn to followed by the other 503 Bits across the 24x21 grid to complete the Shape. ​

Last but not least there are two additional interesting registers $D01D and $D017. Both stretch the Sprite either horizontally or vertically by the factor 2. The number of used pixels respectively the 24x21 Matrix are not affected by this stretch and also the collision zone of the sprite is not changing from a visual standpoint. Everything not Background color will still be recognized as collision zone including all stretches.

​Four Sprites Shapes: normal, horizontally stretched, vertically stretched and both sides stretched

### Storing and Fetching Sprites from Memory

We already learned a lot about Sprites, that is the different Sprite Modes, how to get notified on the interrupt on collisions and how to activate and position a Sprite on screen. Now the final piece is to actually understand where those Sprite Shapes are located in memory and how we store and retrieve the sprite data.

At the very end of the Screen RAM block the last 8 Bytes are used as so-called Sprite Pointers. In standard configuration that would be the memory locations $07F8 to $07FF or Decimal 2040 to 2047. Since it's 8 Bytes and we have 8 Sprites we are obviously supposed to be able to locate a Sprite Shape by a number from 0 to 255 - anywhere in memory! You should be curious now how this is possible because we are working with 16Bit-addresses in the C64 after all, 8 Bits are not enough to address all 64Kbyte of RAM.

Remember that the VIC-II can only look at 16Kbyte of RAM at a time. A Sprite Shape has a fixed size of 64 Bytes and since a shape must be located somewhere the VIC-II can see it there are indeed only 256 different locations available per bank as 16384 (bytes size of 16Kb bank) divided by 64 (bytes size of a shape) equals 256 possibilities.

That is exactly why a single byte is all we need to point to a Sprite Shape. ll we need to take care of is that our Sprite Shape is located in a memory address in the currently active 16Kb bank that is divisable by 64. Let's assume we are in the default VIC-II Bank 3 that ranges from $0000 to $3FFF and we want to store a Sprite Shape for Sprite#0 starting at memory location $1600. For that location the Sprite Pointer in $07F8 would be $58. Too fast? Here comes the math!

$1600 or Decimal 5632 divided by 64 equals Decimal 88 or Hexadecimal $58. When we put $58 in location $07F8 the VIC-II will read 64 Bytes starting at $1600 to fetch our Sprite Shape. The content of the 64th Byte has no influence on the shape as it is defined by its 504 pixels (=63 Bytes).

### Sprite Priorities - who covers what

The last thing you need to consider when working with Sprites are priorities which determine which Sprite overlays another Sprite or which Sprite overlays Background Graphics and vice-versa.

Register $D01B is again a sprite-mapped register. If a Bit is set high then the Background overlays the corresponding Sprite, if set low, the Sprite overlays the Background.

mong Sprites themselves the order of priority is already set by default and can not be changed. The higher the Sprite number, the lower is its priority. Sprite#0 has the highest priority and will overlay any other of the 7 Sprites. Sprite #3 will overlay Sprite #4 - Sprite#7 but can be covered by Sprite#0 - Sprite#2.

The C64 already defines priorities between sprites. Sprite#0 has the highest priority, Sprite#7 the lowest.

### Painting and Animating Sprites

​To animate Sprites you draw a handful of coherent Sprite Shapes for a flip-book style animation and store all of them next to each other in memory. It actually does not matter where you put them as long as you know which Shape you want to display next in an animation. Let's assume the first frame for Sprite#0 is at $1600, next one at $1640 and a third frame at $1680. To give the illusion of animation all you need to do is to iterate the Sprite Pointer in $07F8 over the locations of the frames - in our example you would iterate over the pointer values $58, $59 and $5A. If you do this iteration once per screen refresh you achieve a slick 60 frames per second animation. How many Frames you use per Sprite Animation is only limited by memory.

How to create Sprites Shapes and Animations

You want to paint and animate Sprites, of course - this is a very simple process: just buy a block with quad-ruled paper, draw a 24x21 grid and then for each set Bit just fill the appropriate cell on the paper. Then calculate the decimal value of each row, convert to Hexadecimal equivalent and write it into C64 memory. Repeat for all frames of your animated Sprite and all other Sprites you want to use in the game. For Multicolor based Sprites you of course also want to consider putting down the Bitmask information for each pair of Bits and have some colored pencils at hand to test different color combinations.

If this does not sound like exciting work for a few weeks I don't know what does!

​The old-school requirements to draw Sprites is using a piece of paper, a pen and a calculator.

​Wait! ​I was just kidding of course - it's not 1983 anymore! There are a few Sprite Editors out there which work perfectly well with your mouse skills and offer hot-switching between Multicolor and Standard mode as well as animated timelines to see your Sprite in action. I will introduce SpritePad in one of the tutorials including how to run this Windows-Program on Mac OSX.​

​SpritePad is an excellent Win32-based editor to draw and animate Sprites for the C64.

This closes the introduction into the VIC-II - it should have helped you to get some fundamental knowhow how graphics work in the Commodore C64. We only scratched the surface though - there is much more to learn about the VIC-II. Stay tuned. ​

-act

7 Likes

# VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen

April 26, 2013 / actraiser

- VIC-II for Beginners Part 1 - When Visibility Matters
- VIC-II for Beginners Part 2 - To have or not to have Character
- VIC-II for Beginners Part 3 - Beyond the Screen, Rasters and Cycles
- VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen
- VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

### Screen Modes - Spoilt for Choice

Thanks to the very versatile VIC-II the Commodore C64 has various possibilities to work with the screen. We distinguish between modes which are either Character-based or those which are Bitmap-based. Character-based means that you work in a 8x8 Bit matrix while in a Bitmap-based mode you have control over each individual Pixel on the screen.

Both of the those main modes can be used with a default Standard Color or an alternative Multicolor option. There is only one special Character-based mode called Extended Background Color Mode which can not be used with the Multicolor option. I will go through all combinations and discuss implications in this article. It should be a good reference for later use.

I will explain the official modes in this beginners series - there are plenty of other modes created by developers who exploited VIC-II bugs and came up with some interesting possibilities. Any of the official modes will come with a table which summarizes where the individual data or color for the final output originates from.

The metrics and adresses used in this article assume that we work with the VIC-II in the standard memory configuration with VIC-II looking at Bank 3 ($0000 - $3FFF) and nothing was moved by the user, e.g. the Screen RAM location. If you switch to another bank or move anything within the 16Kb area the VIC-II is looking at you must of course consider this below.

Last but not least all modes can be mixed on the screen using interrupt programming, that is you could have a character-based mode in the lower third of the screen and Bitmap graphics in the upper part - think Graphic Text Adventures. How to do this will be subject to a tutorial.

Let's start!

### Character-Based Modes

Character-Based modes work in the 40x25 screen grid with the standard 8x8 Bit Character Sets unless you modify them of course. When writing to the screen you allocate a character to one of the 1000 cells starting at $0400. Color RAM which starts at $D800 maps the 1000 cells of Screen RAM positions 1:1 so $D800 holds Color Information for $0400, $D801 for $0401 etc.

Don't reduce Character-Based Modes to just writing some text to the screen. In fact it is a common pattern to change the original character set of the Commodore C64 for building tiled background graphics for games and demos because it is much more efficient to for example to soft-scroll a background built from characters then doing the same with Bitmap-based graphics.

1. Standard Character Mode

When you turn on your C64 you actually see the first Character-Based Screen Mode - it's the Standard Character Mode. ​The characters are retrieved from the Character Generator ROM. They share one common Background Color as defined in $D021 and an individual color stored in the lower 4 Bits of Color RAM at the position mapping the corresponding cell in Screen RAM.

Characteristics:

- 8x8 Bit Matrix for each cell - supposed to be used with the Standard Character Sets from the Character Generator ROM.
- A single shared background color
- One individual foreground color

​Sources of Data in Standard Character Mode

​Source for data in Standard Character Mode.

Source of Data Information in Standard Character Mode

Writing to the Screen in Standard Character Mode

2. Multicolor Character Mode

When we enable the multicolor option for that Character Mode by setting Bit#4 in Register $D016 high something very interesting happens. Instead of an 8x8 Matrix we have now a 4x8 Matrix where every two Bits are paired, so instead of a single pixel each "dot" has the length of two pixels next to each other. ​What this at first strange pattern overcomes is the limitation of only using a single foreground color! Using the 2-Bit wide Pixels we can now encode more information what color to use per each pair.

It is actually pretty clever because if you look at each double-wided pixel it becomes clear that any can hold any of four values, represented in Binary that is %00, %01, %10 and %11. With this we can now refer to one of four sources of color information as opposed to just have options between one foreground and one background color. Two of those sources are the already known Background Color Register $D021 as well as the Color RAM but additionally adds the lower four bits of the two registers $D022 and $D023. If we just switch to Multicolor right after turning the C64 on by setting Bit#4 of $D016 you will instantly notice the difference between the original 8x8 Matrix of Standard Character Mode and the 4x8 Matrix we transit to when adding in the Multicolor Option.

It looks awkward because the Standard Characters are not properly set up for Multicolor at this time and we also use the initial values of $D022 and $D023. By modifying the characters appropriately you could generate some exciting graphics but more importantly colorful tiles for the background of your game or demo.

There is one catch with setting Multicolor though. Turning it on using Bit#4 in $D016 will enable it globally for all Characters, however, you need to enable it on a character-by-character basis to have an effect. This is done by setting Bit#3 in the corresponding Color RAM location. The constraint is notable at once when you think about it. If you put a color code from 0-7 into the Color RAM location of a Character it will be displayed in Standard Mode. If you put in a value of 8-15 into that location - which simply means that Bit#3 is set high - you get a Multicolor-Character. This restricts you in the selection of colors you can actually use in a Multicolor Character.

​This poke will switch to Multicolor Character Mode

​Multicolor Character Mode activated

In Multicolor Mode every two bits are paired to encode one of four values pointing at the source of color information for this pair of pixels.​ The overlay is just illustrative to still recognize the letter A here.

Characteristics:

- Each character has the dimensions 4x8 Pixels where each of the horizontal Bits are paired to have double the width.
- We can now choose up to four colors per character by encoding the appropriate source of color information into each double-wide pixel.
- Three shared colors across all characters come from the registers $D021 - $D023
- One individual Color per Character can be chosen from Color RAM but only the values 8-15 are accepted to have the Character be displayed in Multicolor Mode since Bit#3 determines if the Character is Standard or Multicolor in the first place.

Source of Color Information in Multicolor Character Mode

Source of Data Information in Multicolor Character Mode

3. Extended Background Color Mode

The final one of our character based modes is actually not so often seen in the wild. I read that Activisions H.E.R.O uses it though. The Extended Background Color Mode let's you choose one in four background colors per 8x8 Bit Character as opposed to just a single background color in Standard Character Mode. The difference to Multicolor Character Mode is that you keep the 8x8 Matrix and have not to cut horizontal resolution by half.

Using this mode comes with a cost though. Like with the Multicolor option two bits are still needed to encode the information from where some color is retrieved from. In the Extended Background Color Mode those two bits are the two highest Bits of the Screen Code of any Character and they determine the Background Color used for this Character across the screen while the individual Foreground Color per cell is still retrieved from Color RAM.

This theft of two Bits to encode a color source comes with a problem of course. Since only 6 Bits per Character can be used now are limited to point to only 64 different characters as opposed to 256 Characters when all Bits being available. If you do a game and want to use some of the Characters for building background graphics plus need some standard letters for text, this will get complicated.

The selection of background colors are defined in the Registers $D021 - $D024 and then displayed accordingly to the Bitmask of the two highest Bit in the Screen Code.​ For example, the Bit Pattern of the Screen Code for the Letter A is %00000001 . If we want the character to be displayed with the color information from $D023, the Screen Code needs to be changed to %10000001 .​​

If you turn on the Extended Background Color Mode by setting Bit#6 in $D011​ only the blinking cursor will actually change color. The reason is that all Standard Characters have their two high bits not set and the blinking cursor is actually a reversed character and as we already learned this means that in this case the highest bit IS actually set, ultimately making the used Screen Code look like this %10xxxxxx. The reversed version of the cursor therefor uses the color of $D023.

Extended Background Color Mode can not be mixed with the Multicolor Option.​

Characteristics:​

- ​Characters are displayed in a 8x8 Bit Matrix like in Standard Character Mode.
- Bit#6 and Bit#7 of the Screen Code are used to determine the desired Color source ​
- Since we lose 2 Bits in the Screen Code, only 64 different Characters can be displayed on the screen.​
- Choice of one in four defined Background Colors per Character
- One Individual Foreground Color per Character

Source of Data Information in Extended Background Character Mode

This closes our three Character-Based modes, let's turn to Bitmap graphics.​

### Bitmap-based Modes

In Bitmap-Mode you control every and each pixel on the screen, that are all of the 64.000 in the 320x200 area - and beyond if you use some tricks to remove the borders. ​Our 1000 Byte large Screen RAM can not be used anymore for this large amount of data as 64.000 Bits correspondent to about 8KByte memory consumption. If you now recall that the VIC-II actually can only look at 16Kb at a time, losing 8Kb for some Bitmap Graphics sounds wasteful - and it is!

Of course there are ways to overcome limitations like by bank switching but let's for now look at standard way to work with Bitmap Graphics. There are only two Bitmap modes, the Standard Bitmap Mode with Multicolor option turned on, the Multicolor Bitmap Mode. When you understood the difference between Standard Character and Multicolor Character Mode, than you will be familiar with the concept already. Bitmap Mode is turned on by setting Bit#5 of Register $D011 high.

1. Standard Bitmap Mode

Technically there is not so much to talk about graphics in Standard Bitmap Mode. The C64 wants us to reserve a 8Kb large area in the bank the VIC-II is pointing to. This is required to overcome the limitations of the Screen RAM which simply is not big enough to hold a full bitmapped graphics. However, the Screen RAM has now another function - its new job is to be the source for colors for the Bitmap. The Background Pixel Color is defined by Bits#0 - Bit#3 of the corresponding Byte in Screen RAM. The Foreground Pixel Color is defined by Bits#4 - Bits#7 - again from the corresponding Byte in Screen RAM.

​Poke to set Bitmap Mode

​C64 in Bitmap Mode

So even in Bitmap mode, you define the color for a full 8x8 Matrix using a Byte in Screen RAM. You can not define a color per Pixel - which would be hard to store in the first place. Imagine that a color needs four Bits to cover all 16 combinations. If you multiply this by 64.000 individual Pixels, you would need 250Kb for color information in addition to the 8Kb for the actual Image if every pixel has it's own dedicated color code stored somewhere. Not happening!

Characteristics:

- ​Set or unset any of the 64.000 Pixels on the screen
- Change color for each 8x8 cella using information from Screen RAM
- 8Kb of RAM within the 16Kb area of the VIC-II needs to be reserved to store a full bitmap graphic ​​
- Initial reserved RAM for Bitmap in Bank 3 is $2000 - $3FFF. This can be changed but since 8Kb is required only two possible locations in each 16Kb bank are possible.

Source of Data and Color Information in Standard Bitmap Mode

2. Multicolor Bitmap Mode

The Multicolor Bitmap Mode follows the principle I explained with the Multicolor Character Mode. It pairs up every two pixel on the screen which ultimately brings us down to a resolution of 160x200 - but with wide Pixels that is. ​The benefit is that we can now appoint again four different sources of color information within every two-wide Pixel. This makes up for losing half of the horizontal resolution. Where the Bitmask is %00, the Background Color Register $D021 is used. For %01 and %10 the VIC-II retrieves the information from Screen RAM in a similar fashion as in Standard Bitmap Mode. Finally we now also have the 1000 Byte large Color RAM where the lower four Bits of each Byte supply us with the fourth color.

Characteristics

- ​Horizontal Resolution cut in half to 160x200 to pair up every two pixels and use them as container for color source information.
- Four colors can be appointed for any of the wide Pixels. Background Color is shared from Register $D021 but Color 1 to Color 3 can be freely chosen for each 2x1 Pixel in the Bitmap. ​

'Look Twice' by Pal / Offence

Source of Data and Color Information in Multicolor Bitmap Mode

These were all the Screen Modes the VIC-II can officially handle. There are many more often evolved from exploiting VIC-II bugs or the clever combination of the available modes. ​We will eventually talk about those advanced screen modes at another time.

-act

2 Likes

# VIC-II for Beginners Part 3 - Beyond the Screen: Rasters and Cycles

April 25, 2013 / actraiser

- VIC-II for Beginners Part 1 - When Visibility Matters
- VIC-II for Beginners Part 2 - To have or not to have Character
- VIC-II for Beginners Part 3 - Beyond the Screen: Rasters and Cycles
- VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen
- VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

### Screen Dimensions from a Raster Beam perspective

​When we talk about the screen we must first clarify what the screen actually is. When you turn on your C64 you have an (officially) accessible resolution of 320x200 surrounded by a light blue colored border. This is the same for PAL and NTSC machines. However, if we talk about the number of lines the raster beam needs to generate per each screen redraw it is not just 200 but in fact 262 lines for NTSC models and 312 lines on a PAL C64. ​ I will use metrics based on a PAL system for the following explanation.

With analog television you need to take the time the raster beam needs to go from the last line it drawed to the first line into account. This is called Vertical blank or VBLANK. The same applies horizontally, that is after finishing drawing the last pixel to the right, the raster beam needs to start over in the next line on the very left. This is called horizontal blank or HBLANK. ​Taking VBlank and HBlank into account the Raster beam has to travel 504 pixels horizontally and 312 lines vertically for one full screen refresh on a PAL C64. ​The actual travelling the raster beam does to get from the last line to the top or from the last pixel on the right back the left is called Screen Blanking. ​Since timing is very crucial when working on the Commodore C64, we need to know that our screen refresh includes more than just the visible area.

​Rasters & Cycles on the Screen - image taken from the C64 presentation by Michael Steil. Thanks!

So we care about how much time a raster beam needs to reach a specific position on the screen or how much time it takes to draw one byte of information. For this we need to learn some other common expressions.

There is Raster Time which is the duration it takes for the VIC-II to put one byte of graphic data on the screen. Raster Time is measured in CPU cycles. CPU cycles are basically the smallest unit of currency on the Commodore C64. At some point all you care about is to optimize your code to save CPU cycles per screen refresh. There is also the expression Raster Line ​which applies to a full line of the screen and not just a single byte.

The Raster Time to put one byte of data on the screen is exactly 1 Cycle. As far as the Raster Beam is concerned a full horizontal line has 504 pixels which we can divide by 8 to get the time required for a Raster Line - it is 63 Cycles. To complete the math, since we have 312 lines of distance from top to the bottom of the screen, the amount of Cycles needed to draw once over the full screen is 312 lines * 63 Cycles = 19656 Cycles. The 6510 CPU in a PAL C64 is clocked with 985248 Hz. If we divide this number by the CPU Cycles we calculated for one screen refresh we are at 50.125 which equals ~50HZ for the final refresh rate. That sounds just about right for the PAL standard. The calculation does not consider the time we need for the screen blanking though.

### The Black Sheep of Raster Lines - The Bad Line

Now with this information we would assume that we have 63 Cycles of computation time to do something cool on each line on the screen. However, there is one additional catch with timing and to understand we need to take a look at the close partnership between the CPU 6510 and the VIC-II chip.

The CPU and the VIC-II both share the same data bus which they access both once per cycle. Since they can not access the bus at the same moment they agree to take turns within a cycle. One cycle is therefor divided in two phases, a low and a high phase. The VIC-II accesses the data bus in low phase and the CPU in high phase. However, it turns out that the VIC-II sometimes needs a few more cycles for a raster line than it got reserved from its share in the low phase windows. This for example occurs when it needs to retrieve color information, or when it starts fetching characters. It also happens when the VIC-II has to deal with Sprites on the screen.

For example displaying a character takes an additional 40 cycles as the VIC-II needs that extra time to check the destination of a character pointer for retrieving the actual data. The additional cycles are usually needed every 8 lines because standard characters are 8 lines high and the VIC-II needs to fetch the location of the to be drawn character on every first line of the 8x8 character matrix.

So what the VIC-II actually does to get hold of those required additional cycles is to block the CPU for about 40 cycles. The raster line where this stunning of the CPU happens is called a Bad Line ​because we have now less cycles left for computation in our actual assembler code - the VIC-II just stole it from us! Instead of 63 cycles of CPU time we only have 20something cycles on every Bad Line. This will become important when we need exact timing in our game or demo. There are various ways to exactly sync the action on the screen with the refresh rate and will talk about building so-called Stable Raster Routines ​when an opportunity comes up.

This is a simplified explanation but we will come back to Bad Lines at a later point and for that you should have a basic understanding.

-act

​

​

​

​

5 Likes

# VIC-II for Beginners Part 2 - To have or not to have Character

April 23, 2013 / actraiser

- VIC-II for Beginners Part 1 - When Visibility Matters
- VIC-II for Beginners Part 2 - To have or not to have Character
- VIC-II for Beginners Part 3 - Beyond the Screen: Rasters and Cycles
- VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen
- VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

### Hide and Seek with the Character Generator ROM

When you turn on your C64 and are welcomed with some information about the BASIC Version and the free BASIC Bytes printed on the screen you are actually looking at one of two distinct Character Sets. Check an original C64 keyboard and you will notice that there are lots of symbols and other special characters which can be used - and which by the way makes working with a standard PC keyboard in an C64 emulator so tedious.

​Up to four different characters can be accessed with various short cuts per key.

All this symbols and characters are unalterable stored in the Character Generator ROM, a 4Kb large area overlapping $D000-$DFFF. f you have followed the other articles you know that this area is crowded. We have RAM under ROM, we have I/O mapped registers and now there is also the Character Generator ROM! How do we access what we need at a particular moment? To understand this, we need to look at the CPU 6510 and the VIC-II separately.

From a CPU perspective $D000 to $DFFF is I/O-mapped memory including a 1Kb block of Color RAM. The Character Generator ROM is actually not in sight of the CPU at this point. It will let the VIC-II take care of working with it as required. Since we usually want to access the Character Generator ROM at least once for the purpose of copying it to some other location in RAM we first need to switch out that I/O mapped area by changing the memory location $01. It is used to configure various memory layouts in the C64. With this done the CPU can now see the Character Generator ROM and we are able to read and copy everything to RAM to be able to change characters to our liking. After copying don't forget to switch the I/O mapping in again!

From a VIC-II perspective he Character Generator ROM is only visible in Bank 0 and Bank 2. In either bank the VIC-II sees it at location $1000-$1FFF. Technically the Character Generator ROM is of course located at $5000 in Bank 2 but the VIC-II does not know at which bank it is looking so it always assumes it is watching a block of 16Kb of memory starting at $0000. In the other two banks 1 and 3 there is no Character Generator ROM shadow image available. That means if we want to point the VIC-II to Bank 1 or 3 but still need to use some of the original Commodore characters we have to to copy it beforehand. However even when working in Bank 0 and Bank 2 we usually still want to copy Characters from ROM to the RAM underneath so we can modify it.

Here is a routine to copy the complete Character Generator ROM in Bank 0 from it's ROM location at $D000 to the RAM underneath at the same address:

### Looking at the Character Sets

Each character in a set is stored in a 8x8 Bit matrix, in other words every character takes up 8 Bytes of space in memory. In our 4Kb ROM area we have therefor space for 512 Characters and this space is actually completely populated. The 512 characters are divided into two sets with 256 characters each, let's call them Set A and Set B. Each set is again split into standard and negative-imaged versions of each individual character.

You can only use one of the 2Kb large sets, that is 256 characters at a time. ​In BASIC interactive mode when you hit SHIFT+Commodore Key you can switch between Set A and Set B. Ultimately everything currently on the screen will change to correspond to the selected set. You can not write to the screen with say exclusive symbols from Set A1 and then continue writing with lower case characters from Set B1 without affecting the symbols entered prior. ​

That's why the possibility to copy character sets into RAM to change it to ones use-case is so important. Basically every game or demo uses a custom font often just loosely based on one of the original Character Sets.​

Four 128-Sets of Characters

### ​Microscoping into the Character Generator ROM

Let's examine how the characters are actually stored within the ROM. Every character is a consecutive series of eight Byte values to represent the 8x8 Bit matrix. When looking at the Binary representation we can see that a turned on Bit is considered to use the active foreground color and if a Bit is not set then the active background color is applied on the screen.

Lets confirm that the Byte values for the letter A are $18, $3C, $66, $7E, ​$66, $66, $66 and $00 as shown in the Binary representation below. We compare with a ROM dump of the Character Generator ROM.

​the 'A'-character and how it is stored in memory. Each of the 8 rows are a Byte value consecutively stored in the Character Generator ROM.

​Character Generator ROM Dump

### PETSCII - the Commodore way of not using ASCII

PETSCII stands for PET Standard Code of Information Interchange and is loosely based on the ASCII standard but offers lots of symbols not available in the original ASCII set. The reason for a custom standard were the limitations of older Commodore PET machines. Their Character Set was not changeable and there was no Bitmap mode for drawing graphics as opposed to the capabilities you have with the Commodore C64. So lots of symbols were added to make drawing to the screen somewhat flexible. There are also some fun facts like there are for example card suit symbols in the PETSCII set of characters. The idea was that Card Games should be easy to be programmed on Commodore systems. PETSCII is also often referred as CBM ASCII. Besides Letters and Symbols, PETSCII also includes cursor and screen control codes to clear the screen for example.

The following two screenshots show to the left Character Set A which includes only Upper Case letters but all available symbols while to the right there is Character Set B which includes both, Upper and Lower Case letters.. To compensate for the required space Character Set B has to go without some of the symbols. Character Set A is often referred as unshifted and Character Set B as shifted set. Note, that the screens don't show cursor or screen control codes.

​Unshifted Characters

​Shifted characters

### Screen Codes vs PETSCII

The final detail which often leads to confusion is the difference between Byte values we put into Screen RAM to display some text and the actual values PETSCII defines and which are used for example in BASIC. ​

When we write Bytes to Screen RAM we count up from $00 starting with the @​ sign having Screen Code 0, the letter A has Screen Code 1 etc. The reversed presentation of each character requires a simple addition of $80 or Decimal 128 since the second part of any of the two Character Sets is exactly 128 Bytes off. Since 128 is the Decimal value of the highest Bit in a Byte, the switching between standard and reversed characters is as simple as EOR'ing against %10000000 what I already explained in the Bit Manipulation article.

This should do it for a start regarding the Character Generator ROM. We will revisit characters when we want to built custom fonts or use modified character sets as background graphics.

-act

​

1 Likes

# VIC-II for Beginners Part 1 - When Visibility Matters

April 22, 2013 / actraiser

- VIC-II for Beginners Part 1 - When Visibility Matters
- VIC-II for Beginners Part 2 - To Have or not to Have Character
- VIC-II for Beginners Part 3 - Beyond the Screen: Rasters and Cycles
- VIC-II for Beginners Part 4 - Screen Modes Cheaper by the Dozen
- VIC-II for Beginners Part 5 - Bringing Sprites in good Shape

### VIC-II - the Power horse in your C64

This is a four-part series to introduce beginners to the VIC-II. The render capabilities of the C64 are in fact truly awesome for it's time. You have plenty of screen modes which can be mixed, there are 8 sprites available, a palette of 16 colors, hardware soft scrolling - and that's just the official stuff. An ironic fact is, that if you look in the official Commodores Programmers Reference Manual, there is near to no information how to utilize all this great feature set of the VIC-II. Consider this topic to be one which needs your full attention because it will be overwhelming - you probably need to read through all parts several times to "get it". You need also to combine knowledge from different other things to understand the VIC-II and it's features - it does not hurt to revisit articles on Memory Layout, Bit Manipulation and Interrupt Programming.

​The VIC-II 8566 was the last PAL version built for the Commodore C64

Luckily smart programmers started very early to explore the VIC-II capabilities and came up guidance on how to use the VIC-II and along the way explored some interesting new features by exploiting various bugs in the chip, for example...

- ​render more than 100 Sprites on the screen at the same time
- achieve interlace modes that can generate palettes with 128 colors
- open the top and side borders for extended visible screen area

Those examples are advanced topics but it should give you a great idea what this machine is capable to generate when you know what you are doing. For now we will stick to the official capabilities though. ​

​16 Colors + exploiting some VIC-II quirks and lots of talent can lead to wonderful graphics like this one.

### The Limited Power of Sight of the VIC-II

One limitation of the VIC-II is its only 14 Bits wide address bus which means it can address only 16384 memory locations or 16Kb at a time. Why did Commodore not use a 16-Bit bus in the first place? I don't know - but instead we are able to select which out of four 16kb large areas the VIC-II can work with. To select any of those so called banks we use two Bits of address $DD00 which is a data register belonging to the CIA-2 chip. The register is wired to the so-called PORT A of that chip.

In fact Bit#0 and Bit#1 of $DD00 could be interpreted as the lacking Bit#15 and Bit#16 of the VIC-II address bus, however, the VIC-II will not know at what bank it looks when we change configuration. All the VIC-II knows is that it is looking at 16Kb of memory somewhere in the C64 RAM. Finally, to make this awkward setup requirement even more confusing, those two Bits of PORT A are low-active, that means they are considered to be turned on when set to low and off when set to high. I don't know the reason behind this but all what needs to be remembered by the C64 coder is that the higher the value of the Bit pattern is, the lower is the selected bank in memory.

​Banks and their area in C64 Memory. Notice the "inverse" relationship between the Bit patterns and the VIC bank memory addresses.

### The trouble with the Character Generator ROM

The question will come up which of the four banks is the best to do game or demo coding. For this question you need to take one more Commodore design decision into consideration. In two of the 16Kb areas - namely Bank 1 and Bank 3 - the Character Generator ROM overlays the RAM. It actually only overlaps in Bank 3 at location $9000 but is additionally "shadowed" to $1000. The idea behind this design was to provide two banks were the programmer had directly access to the standard character sets. Otherwise he had to copy the Character Generator ROM content to some other memory location first. As a matter of fact you usually want to do exactly that - copy the Standard Characters somewhere into RAM and modify it to your liking - after all you want a kick-ass font or tiled background in your demo! So obviously Bank 1 or Bank 3 are no good options for us in the standard system configuration. We rather want to use Bank 0 or Bank 2, and before we switch to either bank, we copy just the portion of characters from the standard set into our banks memory where the VIC-II can see the Characters Set and then do the switch.

### ​Where is the Color RAM?

In the Memory Map article the Color RAM is somewhat stacked on top of ROM - in fact the 1Kb of Color RAM is I/O mapped starting at $D800, but the VIC-II can magically see the Color RAM in any of the banks - but how? It turns out that four of the VIC-II data pins are directly wired to the Color RAM which really is a dedicated chip on the C64 mainboard. The I/O mapped area of the 1Kb large Color RAM is always from $D800 - $DBFF, and again, no matter to which of the four banks your VIC-II is pointing to, it can always see Color RAM at $D800 - it's the law.

### Bringing everything together

This was already a good amount of initial information and I want to recap what is actually required when working with the VIC-II for our first intros.​

- ​We need to choose a bank the VIC-II will consider his work area. This bank will hold all our graphics, sprite definitions and character set information as well as our Screen RAM and Color RAM - it is a crowded area! That configuration is done via PORT A of the CIA-2 chip accessible at $DD00.
- We need to configure the memory start locations for Charsets and Screen RAM. In certain graphic screen modes, the Screen RAM takes over the function of Color RAM and in this case we need to let the VIC-II know where we reserved the ca. 8Kb of RAM for graphics of 320x200 Pixel or 64.000 Bits.​ That configurations are done in VIC-IIs own register $D018 and is also affected by the screen mode we choose in $D011 and $D016. There is also register $0288 to be taken into consideration as it is used to change the Screen RAM location. Sprite Pointers at the end of Screen RAM point to our Sprite Definitions.

Once you have set up everything - and I know it's a lot - actual work with graphics and sprites becomes relatively easy. Follow the other knowledge base entries and the tutorials to get more starting help on working with the VIC-II. For reference, I included the 47 registers of the VIC-II below. ​You will need this table ALL the time when working with anything VIC-II related - bookmark it!

### Registers

There are 47 I/O-mapped registers starting at $D000. ​When we break them down you will notice that there is actually a lot of repeating responsibilities like the 16 registers alone for the X/Y coordinate of each of the 8 hardware sprites as well as another 14 registers which set various color information. There are some control registers which are very important for interrupt programming - we used them in the Episode 2-3 tutorial before.

Since it is very crucial to understand the VIC-II I have included a table of all those registers below. The explanation in each row should be either self-explanatory or is described in another article. ​

By the way, note that the way the VIC-II interprets X and Y positions on the screen might be confusing if you come from a traditional Math coordinate system. X-coordinates on the screen refer to rows while Y-coordinates refer to columns.

Register | Description | Comment
--- | --- | ---
$D000 (53248) | X-Coordinate Sprite#0 | Sets vertical line position of Sprite#0 considering Bit#0 in $D010
$D001 (53249) | Y-Coordinate Sprite#0 | Sets horizontal position of Sprite#0
$D002 (53250) | X-Coordinate Sprite#1 | Sets vertical line position of Sprite#1 considering Bit#1 in $D010
$D003 (53251) | Y-Coordinate Sprite#1 | Sets horizontal position of Sprite#1
$D004 (53252) | X-Coordinate Sprite#2 | Sets vertical line position of Sprite#2 considering Bit#2 in $D010
$D005 (53253) | Y-Coordinate Sprite#2 | Sets horizontal position of Sprite#2
$D006 (53254) | X-Coordinate Sprite#3 | Sets vertical line position of Sprite#3 considering Bit#3 in $D010
$D007 (53255) | Y-Coordinate Sprite#3 | Sets horizontal position of Sprite#3
$D008 (53256) | X-Coordinate Sprite#4 | Sets vertical line position of Sprite#4 considering Bit#4 in $D010
$D009 (53257) | Y-Coordinate Sprite#4 | Sets horizontal position of Sprite#4
$D00A (5325a) | X-Coordinate Sprite#5 | Sets vertical line position of Sprite#5 considering Bit#5 in $D010
$D00B (53259) | Y-Coordinate Sprite#5 | Sets horizontal position of Sprite#5
$D00C (53260) | X-Coordinate Sprite#6 | Sets vertical line position of Sprite#6 considering Bit#6 in $D010
$D00D (53261) | Y-Coordinate Sprite#6 | Sets horizontal position of Sprite#6
$D00E (53262) | X-Coordinate Sprite#7 | Sets vertical line position of Sprite#7 considering Bit#7 in $D010
$D00F (53263) | Y-Coordinate Sprite#7 | Sets horizontal position of Sprite#7
$D010 (53264) | Bit#9 for X-Coordinates | As the C64 screen has more than 255 lines each Bit represents the required 9th Bit to get pass the number 255 for each Sprite X-Coordinate
$D011 (53265) | Control Register #1 | Initial Value: %10011011 Bit responsibilities: Bit#0-#2: Screen Soft Scroll Vertical Bit#3: Switch betweem 25 or 24 visible rows Bit#4: Switch VIC-II output on/off
Bit#5: Turn Bitmap Mode on/off
Bit#6: Turn Extended Color Mode on/off
Bit#7: 9th Bit for $D012 Rasterline counter
$D012 (53266) | Raster Counter | When Reading:Return current Rasterline When Writing:Define Rasterline for Interrupt triggering Bit#7 of $D011 is (to be) set if line number exceeds 255
$D013 (53267) | Light Pen X-Coordinate | Light Pen X-Coordinate
$D014 (53268) | Light Pen Y-Coordinate | Light Pen Y-Coordinate
$D015 (53269) | Sprite Enable Register | Each Bit corresponds to a Sprite. If set high the corresponding Sprite is enabled on Screen
$D016 (53270) | Control Register 2 | Initial Value: %00001000 Bit responsibilities: Bit#0-#2: Screen Soft Scroll Horizontal Bit#3: Switch betweem 40 or 38 visible columns Bit#4: Turn Multicolor Mode on/off
Bit#5-#7: not used
$D017 (53271) | Sprite Y Expansion | Every Bit corresponds to one Sprite. If set high, the Sprite will be stretched vertically x2
$D018 (53272) | VIC-II base addresses | Initial Value: %00010100 Bit responsibilities: Bit#0: not used Bit#1-#3: Address Bits 11-13 of the Character Set (*2048) Bit#4-#7: Address Bits 10-13 of the Screen RAM (*1024)
$D019 (53273) | Interrupt Request Register | Initial Value: %00001111 Bit responsibilities: Bit#0: Interrupt by Rasterline triggered when high Bit#1: Interrupt by Spite-Background collision triggered when high Bit#2: Interrupt by Sprite-Sprite collision triggered when high
Bit#3: Interrupt by Lightpen impulse triggered when high
Bit#4-#6: not used
Bit#7: If set high at least one of the Interrupts above were triggered
$D01A (53274) | Interrupt Mask Register | Initial Value: %00000000 Bit responsibilities: Bit#0: Request Interrupt by Rasterline by setting high Bit#1: Request Interrupt by Spite-Background collision by setting high Bit#2: Request Interrupt by Sprite-Sprite collision by setting high
Bit#3: Request Interrupt by Lightpen impulse by setting high
Bit#4-#7: not used
$D01B (53275) | Sprite Collision Priority | Each Bit corresponds to a Sprite. If set high, the Background overlays the Sprite, if set low, the Sprite overlays Background.
$D01C (53276) | Sprite Multicolor | Each Bit correspondents to a Sprite. If set high, the Sprite is considered to be a Multicolor-Sprite
$D01D (53277) | Sprite X Expansion | Each Bit corresponds to a Sprite. If set high, the Sprite will be stretched horzontally x2
$D01E (53278) | Sprite-Sprite Collision | Each Bit corresponds to a Sprite. If two sprites collide, then corresponding Bits involved in the collision are set to high. This event will also set Bit#2 of the Interrupt Request Register high.
$D01F (53279) | Sprite-Background Collision | Each Bit corresponds to a Sprite. If a sprite collides with the backgroud, then its Bit is set to high. This event will also set Bit#1 of the Interrupt Request Register high.
$D020 (53280) | Border color | Set Border Color to one of the 16 Colors ($00-$0F)
$D021 (53281) | Background Color 0 | Set Background Color 0 to one of the 16 Colors ($00-$0F)
$D022 (53282) | Background Color 1 | Set Background Color 1 to one of the 16 Colors ($00-$0F)
$D023 (53283) | Background Color 2 | Set Background Color 2 to one of the 16 Colors ($00-$0F)
$D024 (53284) | Background Color 3 | Set Background Color 3 to one of the 16 Colors ($00-$0F)
$D025 (53285) | Sprite Multicolor 0 | Set Color 1 shared by Multicolor Sprites
$D026 (53286) | Sprite Multicolor 1 | Set Color 2 shared by Multiclor Sprites
$D027 (53287) | Color Sprite#0 | Set individual color for Sprite#0
$D028 (53288) | Color Sprite#1 | Set individual color for Sprite#1
$D029 (53289) | Color Sprite#2 | Set individual color for Sprite#2
$D02A (53290) | Color Sprite#3 | Set individual color for Sprite#3
$D02B (53291) | Color Sprite#4 | Set individual color for Sprite#4
$D02C (53292) | Color Sprite#5 | Set individual color for Sprite#5
$D02D (53293) | Color Sprite#6 | Set individual color for Sprite#6
$D02E (53294) | Color Sprite#7 | Set individual color for Sprite#7

-act

7 Likes