VIC-II for Beginners Part 1 - When Visibility Matters
- 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.
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...
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.
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.
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!
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.
|$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#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#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#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#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#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|