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!

VIC-II for Beginners Part 1 - When Visibility Matters

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. 

    RegisterDescriptionComment
    $D000 (53248)X-Coordinate Sprite#0Sets vertical line position of Sprite#0 considering Bit#0 in $D010
    $D001 (53249)Y-Coordinate Sprite#0Sets horizontal position of Sprite#0
    $D002 (53250)X-Coordinate Sprite#1Sets vertical line position of Sprite#1 considering Bit#1 in $D010
    $D003 (53251)Y-Coordinate Sprite#1Sets horizontal position of Sprite#1
    $D004 (53252)X-Coordinate Sprite#2Sets vertical line position of Sprite#2 considering Bit#2 in $D010
    $D005 (53253)Y-Coordinate Sprite#2Sets horizontal position of Sprite#2
    $D006 (53254)X-Coordinate Sprite#3Sets vertical line position of Sprite#3 considering Bit#3 in $D010
    $D007 (53255)Y-Coordinate Sprite#3Sets horizontal position of Sprite#3
    $D008 (53256)X-Coordinate Sprite#4Sets vertical line position of Sprite#4 considering Bit#4 in $D010
    $D009 (53257)Y-Coordinate Sprite#4Sets horizontal position of Sprite#4
    $D00A (5325a)X-Coordinate Sprite#5Sets vertical line position of Sprite#5 considering Bit#5 in $D010
    $D00B (53259)Y-Coordinate Sprite#5Sets horizontal position of Sprite#5
    $D00C (53260)X-Coordinate Sprite#6Sets vertical line position of Sprite#6 considering Bit#6 in $D010
    $D00D (53261)Y-Coordinate Sprite#6Sets horizontal position of Sprite#6
    $D00E (53262)X-Coordinate Sprite#7Sets vertical line position of Sprite#7 considering Bit#7 in $D010
    $D00F (53263)Y-Coordinate Sprite#7Sets horizontal position of Sprite#7
    $D010 (53264)Bit#9 for X-CoordinatesAs 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-CoordinateLight Pen X-Coordinate
    $D014 (53268)Light Pen Y-CoordinateLight Pen Y-Coordinate
    $D015 (53269)Sprite Enable RegisterEach 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 ExpansionEvery 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 PriorityEach Bit corresponds to a Sprite. If set high, the Background overlays the Sprite, if set low, the Sprite overlays Background.
    $D01C (53276)Sprite MulticolorEach Bit correspondents to a Sprite. If set high, the Sprite is considered to be a Multicolor-Sprite
    $D01D (53277)Sprite X ExpansionEach Bit corresponds to a Sprite. If set high, the Sprite will be stretched horzontally x2
    $D01E (53278)Sprite-Sprite CollisionEach 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 CollisionEach 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 colorSet Border Color to one of the 16 Colors ($00-$0F)
    $D021 (53281)Background Color 0Set Background Color 0 to one of the 16 Colors ($00-$0F)
    $D022 (53282)Background Color 1Set Background Color 1 to one of the 16 Colors ($00-$0F)
    $D023 (53283)Background Color 2Set Background Color 2 to one of the 16 Colors ($00-$0F)
    $D024 (53284)Background Color 3Set Background Color 3 to one of the 16 Colors ($00-$0F)
    $D025 (53285)Sprite Multicolor 0Set Color 1 shared by Multicolor Sprites
    $D026 (53286)Sprite Multicolor 1Set Color 2 shared by Multiclor Sprites
    $D027 (53287)Color Sprite#0Set individual color for Sprite#0
    $D028 (53288)Color Sprite#1Set individual color for Sprite#1
    $D029 (53289)Color Sprite#2Set individual color for Sprite#2
    $D02A (53290)Color Sprite#3Set individual color for Sprite#3
    $D02B (53291)Color Sprite#4Set individual color for Sprite#4
    $D02C (53292)Color Sprite#5Set individual color for Sprite#5
    $D02D (53293)Color Sprite#6Set individual color for Sprite#6
    $D02E (53294)Color Sprite#7Set individual color for Sprite#7

    -act