The MOS 6567/6569 video controller (VIC-II) and its application in the Commodore 64 by Christian Bauer 2024-09-29 Contents -------- 1. Introduction 2. The architecture of the Commodore 64 2.1. Overview 2.2. 6510 processor 2.3. 6567/6569 graphics chip 2.4. Memory 2.4.1. Memory map as seen by the 6510 2.4.2. Memory map as seen by the VIC 2.4.3. Memory access of 6510 and VIC 3. Description of the VIC 3.1. Block diagram 3.2. Registers 3.3. Color palette 3.4. Display generation and display window dimensions 3.5. Bad Lines 3.6. Memory access 3.6.1. The X coordinates 3.6.2. Access types 3.6.3. Timing of a raster line 3.7. Text/bitmap display 3.7.1. Idle state / display state 3.7.2. VC and RC 3.7.3. Graphics modes 3.7.3.1. Standard text mode (ECM/BMM/MCM=0/0/0) 3.7.3.2. Multicolor text mode (ECM/BMM/MCM=0/0/1) 3.7.3.3. Standard bitmap mode (ECM/BMM/MCM=0/1/0) 3.7.3.4. Multicolor bitmap mode (ECM/BMM/MCM=0/1/1) 3.7.3.5. ECM text mode (ECM/BMM/MCM=1/0/0) 3.7.3.6. Invalid text mode (ECM/BMM/MCM=1/0/1) 3.7.3.7. Invalid bitmap mode 1 (ECM/BMM/MCM=1/1/0) 3.7.3.8. Invalid bitmap mode 2 (ECM/BMM/MCM=1/1/1) 3.7.3.9. Idle state 3.8. Sprites 3.8.1. Memory access and display 3.8.2. Priority and collision detection 3.9. The border units 3.10. Display Enable 3.11. Lightpen 3.12. VIC interrupts 3.13. DRAM refresh 3.14. Effects and applications 3.14.1. Hyperscreen 3.14.2. FLD 3.14.3. FLI 3.14.4. Linecrunch 3.14.5. Doubled text lines 3.14.6. DMA delay / VSP 3.14.7. Sprite stretching / Sprite crunch 4. The addresses 0 and 1 and the $de00 area 5. Revision history Appendix A: Bibliography Appendix B: Acknowledgments 1. Introduction --------------- This paper is an attempt to summarize the results of various people's examinations of the graphics chip "6567/6569 Video Interface Controller (VIC-II)" (simply called "VIC" in the following) which is used in the legendary Commodore 64, and to provide a complete reference to its specified and unspecified properties. It is primarily intended for C64 programmers and authors of C64 emulators, but should also be interesting to "outsiders" interested in hardware design and programming, and utilizing a computer up to its last bits. For this purpose, some general information (e.g. the C64 memory map) already known to experienced C64 programmers has been included as well. The description of the unspecified properties is based on tests done by Marko Mäkelä, Andreas Boose, Pasi Ojala, Wolfgang Lorenz and myself (not to mention numerous others) during the 1990s. It also covers internal registers and workings of the VIC. As no schematics of the VIC are available it can of course only be speculative, but in all cases a descriptive model has been chosen that explains the observed phenomena with the minimally required circuitry. E.g. for the video matrix counter (VC), a model with two simple counters was given preference to a more elaborate one with a +40 adder. Although some measurements were done with an oscilloscope directly on the chip, most insights have been gathered via test programs on the C64, and by comparing them with the implementation in single cycle emulations like "Frodo". 2. The architecture of the Commodore 64 --------------------------------------- This chapter gives an overview of the basic hardware architecture of the C64 and the integration of the VIC into the system. 2.1. Overview ------------- The C64 basically consists of the following units: · 6510 8-bit microprocessor · 6567/6569 VIC-II graphics chip · 6581 SID sound chip · Two 6526 CIA I/O chips · 64KB DRAM (64K*8 bit) main memory · 0.5KB SRAM (1K*4 bit) Color RAM · 16KB ROM (16K*8 bit) for operating system and BASIC interpreter · 4KB ROM (4K*8 bit) character generator Most chips are manufactured in NMOS technology. 2.2. 6510 processor ------------------- The 6510 microprocessor [1] has an 8-bit data bus and a 16-bit address bus, and is object-code compatible with the famous 6502. It has two external interrupt inputs, one maskable (IRQ) and one non-maskable (NMI), and as a special feature a 6-bit wide bidirectional I/O port. It is clocked at approximately 1 MHz in the C64. Important signals: ϕ2 Processor clock output This clock signal is the reference for the entire bus timing. Its frequency is 1022.7 kHz (NTSC models) or 985.248 kHz (PAL models). One period of this signal corresponds to one clock cycle consisting of two phases: ϕ2 is low in the first phase and high in the second phase (hence the name 'ϕ2' for "phase 2"). The 6510 only accesses the bus in the second clock phase, the VIC normally only in the first phase. ϕ0 Processor clock input This signal, with the same frequency as ϕ2, is generated by the VIC (see section 2.3.) and used to derive the ϕ2 clock. R/W This signal indicates a read (R/W high) or write (R/W low) bus access. IRQ If this input is held low an interrupt sequence is triggered, unless interrupts are masked with the interrupt mask bit in the processor status register. The interrupt sequence begins at least two clock cycles later at the start of the next instruction. With this pin, the VIC can trigger an interrupt in the processor. Interrupts are only recognized if the RDY line is high. RDY If this line is low during a read access, the processor stops with the address lines reflecting the current address being fetched. It is ignored during write accesses. In the C64, RDY is used to stop the processor if the VIC needs additional bus cycles for character pointer and sprite data accesses. It is connected to the BA signal on the VIC. AEC This pin tri-states the address lines. This is used for making the processor address bus inactive during VIC accesses. The signal is connected to the AEC output on the VIC. P0-P5 This is the built-in 6-bit I/O port. Each line can be individually programmed as input or output. A data direction register and a data register are internally mapped to addresses 0 and 1, respectively. You might therefore expect that the processor cannot access the RAM addresses 0 and 1, as they are overlaid by the I/O port, but more on this in chapter 4... 2.3. 6567/6569 graphics chip ---------------------------- The 656* series graphics chips by MOS Technologies were originally designed to be used in video games and graphics terminals. But as the sales in these markets had been rather poor, Commodore decided to use the chips when they were planning to make their own home computers. In the C64, the "Video Interface Controller II (VIC-II)" [2] is used, featuring 3 text (or tile) based video modes (40x25 characters with 8x8 pixels each) and 2 bitmap based video modes (320x200 pixels), 8 hardware sprites and a fixed palette of 16 colors. It can manage up to 16KB of dynamic RAM, including the generation of RAS and CAS and handling the RAM refresh, and also has a light pen input and interrupt capabilities. Two VIC types appear in the C64: The 6567 in NTSC machines and the 6569 in PAL machines. There are several mask steppings of both types, but the differences are mostly negligible with the exception of the 6567R56A. Newer C64 versions are employing the functionally equivalent chips 8562 (NTSC) and 8565 (PAL). In the following, only 6567/6569 will be mentioned, but all information is also applicable to the 856* chips. There is also a 6566 designed to be connected to static RAM but this one was never used in C64s (it was used, however, in the somewhat similar Commodore MAX computer). Important signals: A0-A13 The 14-bit video address bus used by the VIC to address 16KB of memory. The address bits A0-A5 and A8-A13 are multiplexed in pairs (i.e. A0/A8, A1/A9 etc.) on one pin each. The bits A6-A11 are (additionally) available on separate lines. D0-D11 A 12-bit wide data bus over which the VIC accesses the memory. The lower 8 bits are connected to the main memory and the processor data bus, the upper 4 bits are connected to a special 4 bit wide static memory (1024 addresses, A0-A9) used for storing color information, the Color RAM. IRQ This output is wired to the IRQ input on the processor and makes it possible for the VIC to trigger interrupts. The VIC has four interrupt sources: On reaching a certain raster line (raster interrupt), on the collision of two or more sprites, on the collision of sprites with graphics data and on a negative edge on the light pen input. BA With this signal, the VIC indicates that the bus is available to the processor during the second clock phase (ϕ2 high). BA is normally high, as the VIC usually accesses the bus during the first phase only. But for the character pointer and sprite data accesses, the VIC also needs the bus during the second phase. In this case, BA goes low three cycles before the VIC access. After that, AEC remains low during the second phase and the VIC performs its accesses. Why three cycles? BA is connected to the RDY line of the processor as mentioned, but this line is ignored on write accesses (the CPU can only be interrupted on reads), and the 6510 never does more than three writes in sequence (see [5]). AEC This pin is wired to the processor signal with the same name (see above). It reflects the state of the data and address line drivers of the VIC. If AEC is high, they are in tri-state. AEC is normally low during the first clock phase (ϕ2 low) and high during the second phase so the VIC can access the bus during the first phase and the 6510 during the second phase. If the VIC also needs the bus in the second phase, AEC remains low. LP This input is intended for connecting a light pen. On a negative edge, the current position of the raster beam is latched to the registers LPX and LPY. As this pin shares a line with the keyboard matrix, it can also be accessed by software. ϕIN This is the feed for the pixel clock of 8.18 MHz (NTSC) or 7.88 MHz (PAL) that is generated from the crystal frequency. Eight pixels are displayed per bus clock cycle (ϕ2). ϕ0 From the pixel clock on ϕIN, the VIC generates the system clock of 1.023 MHz (NTSC) or 0.985 MHz (PAL) by dividing ϕIN by eight. It is available on this pin and fed into the processor which in turn generates the signal ϕ2 from it. ϕCOLOR This input receives a 14.31818 MHz (NTSC) or 17.734475 MHz (PAL) signal from which the VIC derives its color output signal. Its frequency corresponds to four times the NTSC or PAL color burst signals of 3.579545 MHz and 4.43361875 MHz, respectively. S/LUM This pin outputs the horizontal and vertical sync signals and the luminosity information for the image produced by the VIC. The VIC does not support interlace and always outputs a progressive image. COLOR This pin outputs the composite color information for the image, including the color burst signal. 2.4. Memory ----------- Three memory areas in the C64 are involved with the graphics: · The 64KB main memory · The 1K*4 bit Color RAM · The 4KB character generator ROM (Char ROM) In the following two sections we explain how these memory areas share the address space as seen by the CPU and the VIC. After that, we will cover the basics of memory access and DRAM handling. 2.4.1 Memory map as seen by the 6510 ------------------------------------ The 6510 can address 64KB linearly with its 16 address lines. With the aid of a special PAL chip in the C64, many different memory configurations can be used, controlled via the 6510 I/O port lines and additional signals on the expansion port (see [3]). Only the standard configuration will be discussed here as the other configurations don't change the position of the different areas. They only map in additional areas of the main memory. This is the memory map as seen by the 6510: The area at $d000-$dfff with CHAREN=1 CHAREN=0 $ffff +--------------+ /$e000 +----------+ +----------+ | Kernal ROM | / | I/O 2 | | | $e000 +--------------+/ $df00 +----------+ | | |I/O, Char ROM | | I/O 1 | | | $d000 +--------------+\ $de00 +----------+ | | | RAM | \ | CIA 2 | | | $c000 +--------------+ \$dd00 +----------+ | | | Basic ROM | | CIA 1 | | | $a000 +--------------+ $dc00 +----------+ | Char ROM | | | |Color RAM | | | . RAM . | | | | . . $d800 +----------+ | | | | | SID | | | $0002 +--------------+ |registers | | | | I/O port DR | $d400 +----------+ | | $0001 +--------------+ | VIC | | | | I/O port DDR | |registers | | | $0000 +--------------+ $d000 +----------+ +----------+ Basically, the 64KB main memory can be accessed in a linear fashion, but they are overlaid by ROM and register areas at several positions. A write access to a ROM area will change the byte in the RAM lying "under" the ROM. The 6510 I/O port is mapped to addresses $0000 (for the data direction register) and $0001 (for the data register). In the area at $d000-$dfff you can switch between the I/O chip registers and the Color RAM, or the character generator ROM, with the signal CHAREN which is bit 2 of the 6510 I/O port. The Color RAM is mapped at $d800-$dbff and connected to the lower 4 data bits. The upper 4 bits are open and produce "random" values on reading. The two areas named "I/O 1" and "I/O 2" are reserved for expansion cards and also open under normal circumstances. Hence, a read access will fetch "random" values here too. As we will explain in chapter 4 these values are not fully random. Reading from open addresses fetches the last byte read by the VIC on many C64s. The 47 registers of the VIC are mapped in at $d000. Due to the incomplete address decoding, they are mirrored every 64 bytes in the area $d000-$d3ff. 2.4.2 Memory map as seen by the VIC ----------------------------------- The VIC has only 14 address lines, so it can only address 16KB of memory. It can nevertheless access the entire 64KB of main memory because the two missing address bits are provided by one of the CIA I/O chips (they are the inverted bits 0 and 1 of port A of CIA 2). With these you can select one of four 16KB banks for the VIC at a time. The (extended) memory map as seen by the VIC looks like this: $ffff +----------+ -- | | | | | | | RAM | Bank 3 | | | | | | $c000 +----------+ -- | | | RAM | | | $a000 +----------+ Bank 2 | Char ROM | $9000 +----------+ | RAM | $8000 +----------+ -- | | | | | | | RAM | Bank 1 | | | | | | $4000 +----------+ -- | | | RAM | | | $2000 +----------+ Bank 0 | Char ROM | $1000 +----------+ | RAM | $0000 +----------+ -- The Char ROM is mapped in at the VIC addresses $1000-$1fff in banks 0 and 2 (it appears at $9000 in the above diagram, but remember that the VIC doesn't know about the two address bits generated by the CIA. From the VIC's point of view, the Char ROM is at $1000-$1fff also in bank 2). The attentive reader will already have noticed that the Color RAM doesn't seem to appear anywhere. But as explained earlier, the VIC has a 12-bit data bus of which the upper 4 bits are connected to the Color RAM. Generally speaking, the sole purpose of the upper 4 bits of the VIC data bus is to read from the Color RAM. The Color RAM is addressed by the lower 10 bits of the VIC address bus and is therefore available in all banks at all addresses. 2.4.3 Memory access of the 6510 and VIC --------------------------------------- The VIC only performs read accesses to memory, while the 6510 performs both reads and writes. 6510 and VIC are both based on relatively simple hard-wired designs, and both chips do a memory access in EVERY clock cycle, even if unnecessary. For example, if the processor is busy executing an internal operation such as calculating an indexed address, that doesn't actually require an access to memory, it nevertheless performs a read access and then discards the read byte. There are no wait states, no internal caches and no sophisticated access protocols for the bus as seen in more modern computer designs. Every access is done in a single cycle. The VIC generates the clock frequencies for the system bus and the RAS and CAS signals for accessing the dynamic RAM, for both the processor and the VIC itself. It has primary control over the bus and may "stun" the processor at times when it needs additional cycles for memory accesses. Additionally, the VIC takes care of the DRAM refresh by reading from 5 refresh addresses in each raster line. The division of accesses between 6510 and VIC is basically static: Each clock cycle (one period of the ϕ2 signal) consists of two phases. The VIC accesses in the first phase (ϕ2 low), the processor in the second phase (ϕ2 high). The AEC signal closely follows ϕ2. That way the 6510 and VIC can usually alternate their memory use without disturbing each other. However, the VIC sometimes needs more cycles than it has available via this scheme. This is the case when the VIC accesses the character pointers and the sprite data. In the first case it needs 40 additional cycles, in the second case it needs 2 cycles per sprite. BA will then go low 3 cycles before the VIC takes over the bus completely (3 cycles is the maximum number of successive write accesses of the 6510). After 3 cycles, AEC stays low during the second clock phase so that the VIC can output its addresses. The following diagram illustrates the process of the take-over: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ϕ2 _ _ _ _ _ _ _ _ _ _ _ _ _ ..._ _ _ _ _ _ _ _ _ _ _ _ _ ______________ __________________ BA ____________...________ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ AEC _ _ _ _ _ _ _ _ _ _ ______..._________ _ _ _ _ _ _ _ _ Chip VPVPVPVPVPVPVPVpVpVpVVVVVV...VVVVVVVVVPVPVPVPVPVPVPVP 1 | 2 | 3 | 4 Normal |Take-| VIC has taken | VIC releases bus activity |over | over the bus | the bus The line "Chip" designates which chip is just accessing the bus (as said before, there is an access in every cycle). "V" stands for the VIC, "P" for the 6510. The cycles designated with "p" are accesses of the 6510 that are only performed if they are write accesses, and the first "p" read access stops the 6510. On a "p" read access the processor addresses are still output on the bus because AEC is still high. The diagram describes the normal process of a bus take-over. By modifying VIC register $d011 in certain ways it is possible to force a bus take-over at unusual times. This is explained in chapter 3 together with the complete bus timing of a VIC raster line. 3. Description of the VIC ------------------------- This chapter explains the individual function units in the VIC, their way of working and their unspecified behavior, and the insights into the internal functions of the VIC that can be gained by that. 3.1. Block diagram ------------------ The following block diagram gives an overview over the internal structure of the VIC and the independently working function units: IRQ <---------------------------------+ | +---------------+ +-----------------+ |Refresh counter| | Interrupt logic |<----------------------+ +---------------+ +-----------------+ | +-+ | ^ | A |M| v | | d |e| +-+ +--------------+ +-------+ | d |m| |A| |Raster counter|->| VC/RC | | r |o| |d| +->| X/Y | +-------+ | . <==>|r| |d| | +--------------+ | | + |y| |r| | | | | | | d | | |.|<--------+----------------+ +------------------------+ | a |i| |g|===========================>|40×12 bit video matrix-/| | t |n|<=>|e| | | | | color line | | a |t| |n| | | | +------------------------+ | |e| |e| | | | || | |r| |r| | | | +----------------+ || | BA <--|f| |a|============>|8×24 bit sprite | || | |a| |t|<----+ | | | data buffers | || | AEC <--|c| |o| | | v | +----------------+ || | |e| |r| | +-----+ | || || | +-+ +-+ | |MC0-7| | \/ \/ | | +-----+ | +--------------+ +--------------+ | | | | Sprite data | |Graphics data | | +---------------+ | | sequencer | | sequencer | | RAS <--| | | +--------------+ +--------------+ | CAS <--|Clock generator| | | | | ϕ0 <--| | | v v | +---------------+ | +-----------------------+ | ^ | | MUX | | | | | Sprite priorities and |-----------+ ϕIN -----------+ | | collision detection | | +-----------------------+ VC: Video Matrix Counter | | | v RC: Row Counter | +-------------+ +----------->| Border unit | MC: MOB Data Counter | +-------------+ | | v v +----------------+ +----------------+ |Sync generation | |Color generation|<-------- ϕCOLOR +----------------+ +----------------+ | | v v Video output (S/LUM and COLOR) The lightpen unit is not shown. As you can see, the "Raster counter X/Y" plays a central role. This is no surprise as the complete screen display and all bus accesses are synchronized to it. It is important to note that the units for display and for memory access are separate from each other for the sprites as well as for the graphics. There is a data buffer between the two units that holds the read graphics data and buffers it for the display circuits. In the normal operation of the VIC, the functions of the two units are so closely tied to each other that they appear like a single function block. By appropriate programming, however, you can decouple the circuits and, for example, display graphics without previously having read data (in this case, the data which is still in the buffer is displayed). 3.2. Registers -------------- The VIC has 47 read/write registers for the processor to control its functions: #| Adr. |Bit7|Bit6|Bit5|Bit4|Bit3|Bit2|Bit1|Bit0| Function --+-------+----+----+----+----+----+----+----+----+------------------------ 0| $d000 | M0X | X coordinate sprite 0 --+-------+---------------------------------------+------------------------ 1| $d001 | M0Y | Y coordinate sprite 0 --+-------+---------------------------------------+------------------------ 2| $d002 | M1X | X coordinate sprite 1 --+-------+---------------------------------------+------------------------ 3| $d003 | M1Y | Y coordinate sprite 1 --+-------+---------------------------------------+------------------------ 4| $d004 | M2X | X coordinate sprite 2 --+-------+---------------------------------------+------------------------ 5| $d005 | M2Y | Y coordinate sprite 2 --+-------+---------------------------------------+------------------------ 6| $d006 | M3X | X coordinate sprite 3 --+-------+---------------------------------------+------------------------ 7| $d007 | M3Y | Y coordinate sprite 3 --+-------+---------------------------------------+------------------------ 8| $d008 | M4X | X coordinate sprite 4 --+-------+---------------------------------------+------------------------ 9| $d009 | M4Y | Y coordinate sprite 4 --+-------+---------------------------------------+------------------------ 10| $d00a | M5X | X coordinate sprite 5 --+-------+---------------------------------------+------------------------ 11| $d00b | M5Y | Y coordinate sprite 5 --+-------+---------------------------------------+------------------------ 12| $d00c | M6X | X coordinate sprite 6 --+-------+---------------------------------------+------------------------ 13| $d00d | M6Y | Y coordinate sprite 6 --+-------+---------------------------------------+------------------------ 14| $d00e | M7X | X coordinate sprite 7 --+-------+---------------------------------------+------------------------ 15| $d00f | M7Y | Y coordinate sprite 7 --+-------+----+----+----+----+----+----+----+----+------------------------ 16| $d010 |M7X8|M6X8|M5X8|M4X8|M3X8|M2X8|M1X8|M0X8| MSBs of X coordinates --+-------+----+----+----+----+----+----+----+----+------------------------ 17| $d011 |RST8| ECM| BMM| DEN|RSEL| YSCROLL | Control register 1 --+-------+----+----+----+----+----+--------------+------------------------ 18| $d012 | RASTER | Raster counter --+-------+---------------------------------------+------------------------ 19| $d013 | LPX | Light pen X --+-------+---------------------------------------+------------------------ 20| $d014 | LPY | Light pen Y --+-------+----+----+----+----+----+----+----+----+------------------------ 21| $d015 | M7E| M6E| M5E| M4E| M3E| M2E| M1E| M0E| Sprite enabled --+-------+----+----+----+----+----+----+----+----+------------------------ 22| $d016 | - | - | RES| MCM|CSEL| XSCROLL | Control register 2 --+-------+----+----+----+----+----+----+----+----+------------------------ 23| $d017 |M7YE|M6YE|M5YE|M4YE|M3YE|M2YE|M1YE|M0YE| Sprite Y expansion --+-------+----+----+----+----+----+----+----+----+------------------------ 24| $d018 |VM13|VM12|VM11|VM10|CB13|CB12|CB11| - | Memory pointers --+-------+----+----+----+----+----+----+----+----+------------------------ 25| $d019 | IRQ| - | - | - | ILP|IMMC|IMBC|IRST| Interrupt register --+-------+----+----+----+----+----+----+----+----+------------------------ 26| $d01a | - | - | - | - | ELP|EMMC|EMBC|ERST| Interrupt enabled --+-------+----+----+----+----+----+----+----+----+------------------------ 27| $d01b |M7DP|M6DP|M5DP|M4DP|M3DP|M2DP|M1DP|M0DP| Sprite data priority --+-------+----+----+----+----+----+----+----+----+------------------------ 28| $d01c |M7MC|M6MC|M5MC|M4MC|M3MC|M2MC|M1MC|M0MC| Sprite multicolor --+-------+----+----+----+----+----+----+----+----+------------------------ 29| $d01d |M7XE|M6XE|M5XE|M4XE|M3XE|M2XE|M1XE|M0XE| Sprite X expansion --+-------+----+----+----+----+----+----+----+----+------------------------ 30| $d01e | M7M| M6M| M5M| M4M| M3M| M2M| M1M| M0M| Sprite-sprite collision --+-------+----+----+----+----+----+----+----+----+------------------------ 31| $d01f | M7D| M6D| M5D| M4D| M3D| M2D| M1D| M0D| Sprite-data collision --+-------+----+----+----+----+----+----+----+----+------------------------ 32| $d020 | - | - | - | - | EC | Border color --+-------+----+----+----+----+-------------------+------------------------ 33| $d021 | - | - | - | - | B0C | Background color 0 --+-------+----+----+----+----+-------------------+------------------------ 34| $d022 | - | - | - | - | B1C | Background color 1 --+-------+----+----+----+----+-------------------+------------------------ 35| $d023 | - | - | - | - | B2C | Background color 2 --+-------+----+----+----+----+-------------------+------------------------ 36| $d024 | - | - | - | - | B3C | Background color 3 --+-------+----+----+----+----+-------------------+------------------------ 37| $d025 | - | - | - | - | MM0 | Sprite multicolor 0 --+-------+----+----+----+----+-------------------+------------------------ 38| $d026 | - | - | - | - | MM1 | Sprite multicolor 1 --+-------+----+----+----+----+-------------------+------------------------ 39| $d027 | - | - | - | - | M0C | Color sprite 0 --+-------+----+----+----+----+-------------------+------------------------ 40| $d028 | - | - | - | - | M1C | Color sprite 1 --+-------+----+----+----+----+-------------------+------------------------ 41| $d029 | - | - | - | - | M2C | Color sprite 2 --+-------+----+----+----+----+-------------------+------------------------ 42| $d02a | - | - | - | - | M3C | Color sprite 3 --+-------+----+----+----+----+-------------------+------------------------ 43| $d02b | - | - | - | - | M4C | Color sprite 4 --+-------+----+----+----+----+-------------------+------------------------ 44| $d02c | - | - | - | - | M5C | Color sprite 5 --+-------+----+----+----+----+-------------------+------------------------ 45| $d02d | - | - | - | - | M6C | Color sprite 6 --+-------+----+----+----+----+-------------------+------------------------ 46| $d02e | - | - | - | - | M7C | Color sprite 7 --+-------+----+----+----+----+-------------------+------------------------ Notes: · The bits marked with '-' are not connected and yield "1" on reading. · The VIC registers are repeated every 64 bytes in the area $d000-$d3ff, i.e. register 0 appears on addresses $d000, $d040, $d080 etc. · The unused addresses $d02f-$d03f yield $ff on reading, a write access is ignored. · The registers $d01e and $d01f cannot be written and are automatically cleared on reading. · The RES bit (bit 5) of register $d016 has no known function on the VIC types 6567/6569. On the 6566, this bit is used to stop the VIC. · Bit 7 in register $d011 (RST8) is bit 8 of register $d012. Together they are called "RASTER" in the following. A write access to these bits sets the comparison line for the raster interrupt (see section 3.12.). 3.3. Color palette ------------------ The VIC has a hard-wired palette of 16 colors that are encoded in 4 bits: 0 black 1 white 2 red 3 cyan 4 pink 5 green 6 blue 7 yellow 8 orange 9 brown 10 light red 11 dark gray 12 medium gray 13 light green 14 light blue 15 light gray The VIC does not use RGB color generation like modern graphics chips but is designed for analog color television, which represents color information as the phase shift and amplitude of a sinusoidal signal that is output on the COLOR pin of the VIC. Internally, the VIC generates this color signal by mixing a sine and a cosine wave, both derived from the ϕCOLOR input of the chip [6]. The amplitudes of these two signals are modulated according to the individual color value in order to produce the desired phase and amplitude of the output signal. There is some difference in the luminance levels generated for each of the 16 different colors depending on the VIC chip revision. Older revisions (6569R1) produced five distinct levels, while newer version increased this to nine levels. Presumably this was done to make the colors more distinguishable on monochrome displays. See [8] for an in-depth description and reverse engineering of the VIC color palette. 3.4. Display generation and display window dimensions ----------------------------------------------------- As usual for controlling CRT displays, the VIC builds up the video frame line by line. The line number and the number of clock cycles per line are constant for each VIC type. The VIC works character-based, with every character consisting of a matrix of 8×8 pixels, so a text line is made up of 8 pixel lines. 40×25 text characters are displayed in the text based modes, and 320×200 or 160×200 pixels in the bitmap modes. In this article, we will specify a position on the screen using the raster line number as the Y coordinate (RASTER, register $d011/$d012) and an X coordinate that corresponds to the sprite coordinate system. When specifying the time of a VIC memory access or an internal operation in the VIC, we will use the raster line number as the Y coordinate and the number of the clock cycle (and sometimes also the clock phase) within that line as the X coordinate. As previously mentioned, 8 pixels make one clock cycle, so the specification of a sprite X coordinate is eight times more precise than that of a cycle number. The VIC displays its main graphics in an unmovable window in the middle of the visible screen area, the "display window". The area outside the display window is covered by the screen border and is displayed in the border color (EC, register $d020). You can also turn off the border partially or completely with some programming tricks (see section 3.9.); then it becomes apparent that the display window is part of a "display column" which is the linear extension of the display window to the top and bottom. With this you can divide the border into an upper/lower border and a left/right border. The visible screen area is surrounded by blanking intervals in which the video signal is turned off and in which the raster beam returns to the start of the next line or the start of the frame, respectively. The following figure (not to scale) illustrates the last paragraph: Visible pixels/line ____________________|___________________ / \ +------------------------------------------------+ <- Raster line 0 (6569) | . . | | . Vertical blanking interval . | | . . | +---+---+--------------------------------+---+---+ \ | | | | | | | | H | | Upper border | | H | | | o | | | | o | | | r | +--------------------------------+ | r | | | i | | | | i | | | z | | | | z | | | o | | | | o | | | n | | | | n | | | t | | | | t | | | a | | | r | a | | | l | l | | i | l | | | | e | | g | | | | b | f | | h | b | | | l | t | | t | l | | | a | | Display window | | a | |- Visible lines | n | b | | b | n | | | k | o | | o | k | | | i | r | | r | i | | | n | d | | d | n | | | g | e | | e | g | | | | r | | r | | | | i | | | | i | | | n | | | | n | | | t | | | | t | | | e | | | | e | | | r | | | | r | | | v | +--------------------------------+ | v | | | a | | | | a | | | l | | Lower border | | l | | <- Raster line 0 (6567) | | | | | | | +---+---+--------------------------------+---+---+ / | . . | | . Vertical blanking interval . | | . . | +------------------------------------------------+ ^ \________________________________/ | | | Display column | X coordinate 0 The height and width of the display window can be separately set to two different values with the bits RSEL and CSEL in the registers $d011 and $d016: RSEL| Display window height | First line | Last line ----+--------------------------+-------------+---------- 0 | 24 text lines/192 pixels | 55 ($37) | 246 ($f6) 1 | 25 text lines/200 pixels | 51 ($33) | 250 ($fa) CSEL| Display window width | First X coo. | Last X coo. ----+--------------------------+--------------+------------ 0 | 38 characters/304 pixels | 31 ($1f) | 334 ($14e) 1 | 40 characters/320 pixels | 24 ($18) | 343 ($157) If RSEL=0 the upper and lower border each grow by 4 pixels into the display window. If CSEL=0 the left border grows by 7 pixels and the right one by 9 pixels. The position of the display window and its resolution do not change: RSEL/CSEL only switch the starting and ending position of the border display. The size of the video matrix also stays constantly at 40×25 characters. With XSCROLL (bits 0-2 of register $d016) and YSCROLL (bits 0-2 of register $d011) the position of the graphics inside the display window can be shifted in single-pixel units up to 7 pixels to the right and to the bottom. This can be used for soft scrolling. The position of the display window itself doesn't change. To align the graphics with the window, X/YSCROLL have to be set to 0 and 3 for 25 lines/40 columns, and to 7 each for 24 lines/38 columns. The dimensions of the video display for the different VIC types are as follows: | Video | # of | Visible | Cycles/ | Visible Type | system | lines | lines | line | pixels/line ---------+--------+-------+---------+---------+------------ 6567R56A | NTSC-M | 262 | 234 | 64 | 411 6567R8 | NTSC-M | 263 | 235 | 65 | 418 6569 | PAL-B | 312 | 284 | 63 | 403 | First | Last | | First | Last | vblank | vblank | First X coo. | visible | visible Type | line | line | of a line | X coo. | X coo. ---------+--------+--------+--------------+------------+----------- 6567R56A | 13 | 40 | 412 ($19c) | 488 ($1e8) | 388 ($184) 6567R8 | 13 | 40 | 412 ($19c) | 489 ($1e9) | 396 ($18c) 6569 | 300 | 15 | 404 ($194) | 480 ($1e0) | 380 ($17c) If you are wondering why the first visible X coordinates seem to come after the last visible ones: This is because as the reference point that marks the beginning of a raster line, we have chosen the occurrence of the raster IRQ which doesn't coincide with X coordinate 0 but with the coordinate given as "First X coo. of a line". The X coordinates run up to $1ff (only $1f7 on the 6569) within a line, then follows X coordinate 0. This is explained in more detail in the explanation of the structure of a raster line. 3.5. Bad Lines -------------- As already mentioned, the VIC needs 40 additional bus cycles when fetching the character pointers (i.e. the character codes of one text line from the video matrix), because the 63-65 bus cycles available for transparent access by the VIC during the first clock phases within a line are not sufficient to read both the character pointers and the pixel data for the characters from memory. For this reason, the VIC uses the mechanism described in section 2.4.3. to "stun" the processor for 40-43 cycles during the first pixel line of each text line to read the character pointers. The raster lines in which this happens are usually called "Bad Lines" ("bad" because they stop the processor and thus slow down the computer and lead to problems if the precise timing of a program is essential, e.g. for the transmission of data to/from a floppy drive). The character pointer access also happens in the bitmap modes, because the video matrix data is then used for color information. Normally, every eighth line inside the display window, starting with the very first line of the graphics, is a Bad Line, i.e. the first raster line of each text line. So the position of the Bad Lines depends on the YSCROLL. As you will see later, the entire graphics display and memory access scheme depend completely on the position of the Bad Lines. It is therefore necessary to introduce a more general definition, namely that of a "Bad Line Condition": A Bad Line Condition is given at any arbitrary clock cycle if, at the negative edge of ϕ0 at the beginning of the cycle, RASTER >= $30 and RASTER <= $f7 and the lower three bits of RASTER are equal to YSCROLL, and if the DEN bit was set during an arbitrary cycle of raster line $30. This definition has to be taken literally. You can produce or cancel a Bad Line Condition multiple times within an arbitrary raster line in the range of $30-$f7 by modifying YSCROLL, and thus make every raster line within the display window completely or partially a Bad Line, or trigger or suppress all the other functions that are connected with a Bad Line Condition. If YSCROLL=0, a Bad Line Condition occurs in raster line $30 as soon as the DEN bit (register $d011, bit 4) is set (for more about the DEN bit, see section 3.10.). The following three sections describe the function units that are used for displaying the graphics. Section 3.6. explains the the memory interface used to read the graphics data and the timing of the accesses within a raster line. Section 3.7. is about the display unit that converts the text and bitmap graphics data into colors and generates the addresses for the memory access. Section 3.8. covers the sprites and their address generation. 3.6. Memory access ------------------ 3.6.1. The X coordinates ------------------------ Before explaining the timing of memory accesses within a raster line, we will quickly explain how to obtain the X coordinates. This is necessary because the VIC doesn't have a counterpart to the RASTER register that holds the current X coordinate, so you cannot simply read it with the processor. But the VIC surely keeps track of the X coordinates internally as the horizontal sprite positions are based on them, and a pulse at the lightpen input LP latches the current X position in the register LPX ($d013). Determining the absolute X coordinates of events within a raster line is not trivial as you cannot e.g. simply put a sprite to a well-defined X coordinate and derive from the text characters displayed at the same X position the X coordinates of the memory accesses belonging to these characters. The memory access and the display are separate function units and the read graphics data is not immediately displayed on the screen (there is in fact a delay of 12 pixels). So we have taken a different approach: The absolute position of a single X coordinate within the raster line was measured with the LPX register and the other X coordinates were determined relative to this. To do that, the IRQ output of the VIC has been connected to the LP input and the VIC has been programmed for a raster line interrupt. As the negative edge of IRQ was defined to be the start of a raster line, the absolute X position of the line start could be determined in this way. The position of the negative edge of BA during a Bad Line was also measured with this method and the result was consistent with the relative distance of IRQ and BA to each other. Based on these two measurements, the X coordinates of all other events within a raster line have been determined (see [4]). Only now could the sprite X coordinates be used to determine the moment of the display generation of the text characters. This of course implicitly assumes that the LPX coordinates are the same as the sprite X coordinates. There is, however, no indication and thus no reason to suppose that they aren't (a direct correlation would also be the most simple solution in terms of circuit design). 3.6.2. Access types ------------------- The VIC generates two kinds of graphics that require access to memory: The text/bitmap graphics (also often called "background graphics" or simply "graphics") and the sprite graphics. Both require accesses to two separate memory areas: For the text/bitmap graphics: · The video matrix, an area of 1000 video addresses (40×25, 12 bits each) that can be moved in 1KB steps within the 16KB address space of the VIC with the bits VM10-VM13 of register $d018. It stores the character codes and their color for the text modes and some of the color information of 8×8 pixel blocks for the bitmap modes. The Color RAM is part of the video matrix and delivers the upper 4 bits of the 12-bit matrix. The data read from the video matrix is stored in an internal buffer in the VIC, the 40×12 bit video matrix/color line. · The character generator or, respectively, the bitmap: an area of 2048 bytes (bitmap: 8000 bytes) that can be moved in 2KB steps (bitmap: 8KB steps) within the VIC address space with the bits CB11-CB13 (bitmap: only CB13) of register $d018. It stores the pixel data of the characters for the text modes and the bitmap for the bitmap modes. The character generator is not to be confused with the Char ROM. The Char ROM contains prepared bit patterns that can be used as a character generator, but you can also put the character generator in normal RAM to define your own character images. For the sprites: · The sprite data pointers: 8 bytes after the end of the video matrix, that select one out of 256 blocks of 64 bytes within the VIC address space for each sprite. · The sprite data: an area of 63 bytes containing the pixel data of the sprites which can be moved in steps of 64 bytes with the sprite data pointers, independently for each sprite. Corresponding to these memory areas, the VIC performs 4 different kinds of graphics accesses: 1. To the video matrix ("c-access", 12 bits wide). 2. To the pixel data, i.e. character generator or bitmap ("g-access", 8 bits wide). 3. To the sprite data pointers ("p-access", 8 bits wide). 4. To the sprite data ("s-access", 8 bits wide). Moreover, the VIC does two additional types of accesses: 5. Accesses for refreshing the dynamic RAM: 5 read accesses per raster line. 6. Idle accesses. As described, the VIC accesses memory in every first clock phase although there are some cycles in which no other of the above mentioned accesses is pending. In this case, the VIC does an idle access: a read access to video address $3fff (i.e. to $3fff, $7fff, $bfff or $ffff depending on the VIC bank), the result of which is discarded. 3.6.3. Timing of a raster line ------------------------------ The sequence of VIC memory accesses within a raster line is hard-wired, independent of the graphics mode and the same for every raster line. The negative edge of IRQ on a raster interrupt has been used to define the beginning of a line (this is also the moment in which the RASTER register is incremented). Raster line 0 is, however, an exception: In this line, IRQ and incrementing (resp. resetting) of RASTER are performed one cycle later than in the other lines. But for simplicity we assume equal line lengths and define the beginning of raster line 0 to be one cycle before the occurrence of the IRQ. First the timing diagrams, the explanation will follow below: 6569, Bad Line, no sprites: Cycl-# 6 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 3 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ϕ0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ __ IRQ ________________________________________________________________________________________________________________________________ ________________________ ____________________ BA ______________________________________________________________________________________ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ AEC _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _________________________________________________________________________________ _ _ _ _ _ _ _ _ _ VIC i 3 i 4 i 5 i 6 i 7 i r r r r rcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcg i i 0 i 1 i 2 i 3 6510 x x x x x x x x x x x x X X X x x x x x x x x x x Graph. |===========01020304050607080910111213141516171819202122232425262728293031323334353637383940========= X coo. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111 89999aaaabbbbccccddddeeeeff0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff000011112222333344445555666677778888999 c048c048c048c048c048c048c04048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048c048 6569, no Bad Line, no sprites (shortened): Cycl-# 6 1 1 1 1 1 1 1 1 1 1 |5 5 5 5 5 5 5 6 6 6 6 3 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 |3 4 5 6 7 8 9 0 1 2 3 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| _ _ _ _ _ _ _ _ _ _ _ _ ϕ0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ __ | IRQ ______________________________________|________________________ ________________________________________|________________________ BA | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| _ _ _ _ _ _ _ _ _ _ _ _ AEC _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ | VIC i 3 i 4 i 5 i 6 i 7 i r r r r r g g g g |g g g i i 0 i 1 i 2 i 3 6510 x x x x x x x x x x x x x x x x x x x x| x x x x x x x x x x x x | Graph. |===========0102030|7383940========= | X coo. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\\\\\\\\\\\\\ 1111111111111111111111111110000000000000|111111111111111111111111 89999aaaabbbbccccddddeeeeff0000111122223|344445555666677778888999 c048c048c048c048c048c048c04048c048c048c0|c048c048c048c048c048c048 6567R56A, Bad Line, sprites 5-7 active in this line, sprite 0 in the next line (shortened): Cycl-# 6 1 1 1 1 1 1 1 1 1 1 |5 5 5 5 5 5 5 6 6 6 6 6 4 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 |3 4 5 6 7 8 9 0 1 2 3 4 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| _ _ _ _ _ _ _ _ _ _ _ _ _ ϕ0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ __ | IRQ ______________________________________|__________________________ ____ __ | __ __________ BA __________________ ________________|____ __________ _ _ _ _ _ _ _ _ _ | _ _ _ _ _ _ _ _ _ AEC _ _ _ _ _ _____________ _ _ _ __________|_____ _ _ _ _____ _ _ _ _ | VIC i 3 i 4 i 5sss6sss7sssr r r r rcgcgcgcgc|gcgcg i i i 0sss1 i 2 i 3 6510 x x X X X x X X X | x X X X x x x x x | Graph. |===========0102030|7383940=========== | X coo. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\\\\\\\\\\\\\\\ 1111111111111111111111111110000000000000|11111111111111111111111111 999aaaabbbbccccddddeeeeffff0000111122223|3444455556666777788889999a 48c048c048c048c048c048c048c048c048c048c0|c048c048c048c048c048c048c0 6567R8, no Bad Line, sprites 2-7 active in this line, sprites 0-4 in the next line (shortened): Cycl-# 6 1 1 1 1 1 1 1 1 1 1 |5 5 5 5 5 5 5 6 6 6 6 6 6 5 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 |3 4 5 6 7 8 9 0 1 2 3 4 5 1 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _| _ _ _ _ _ _ _ _ _ _ _ _ _ _ ϕ0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _ __ | IRQ ______________________________________|____________________________ __________________|________ BA ______________________ | ____________________ _ _ _ _ _ _ _ _ _| _ _ _ _ _ _ _ AEC _______________________ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ ______________ | VIC ss3sss4sss5sss6sss7sssr r r r r g g g g |g g g i i i i 0sss1sss2sss3s 6510 x x x x x x x x x| x x x x X X X | Graph. |===========0102030|7383940============ | X coo. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\\\\\\\\\\\\\\\\\ 1111111111111111111111111110000000000000|1111111111111111111111111111 999aaaabbbbccccddddeeeeffff0000111122223|344445555666677778888889999a 48c048c048c048c048c048c048c048c048c048c0|c048c048c048c048c04cccc048c0 The line "Cycl-#" shows the number of the clock cycle within the raster line. The line starts with cycle 1 and consists of 63 cycles on the 6569, 64 cycles on the 6567R56A, and 65 cycles on the 6567R8. The last cycle of the previous line and the first cycle of the next line have also been included in the diagrams to make things clearer. The lines "ϕ0", "IRQ", "BA" and "AEC" reflect the state of the respective bus signals. ϕ0 is low in the first phase and high in the second phase. The symbols in the lines "VIC" and "6510" show what kind of access VIC and 6510 do in the corresponding clock phase (for an explanation of the different access types of the VIC see section 3.6.2.): c Reading the video matrix and Color RAM (c-access) g Reading the character generator or bitmap (g-access) 0-7 Reading the sprite data pointer for sprite 0-7 (p-access) s Reading the sprite data (s-access) r DRAM refresh i Idle access x Read or write access of the processor X Processor may do write accesses, stops on first read (BA is low and so is RDY) The line "X coo." contains the X coordinates of the beginning of each clock phase (thus the "\\\" as a reminder) and the line "Graph." is a projection of the 40 column display window and the border to these coordinates, for positioning sprites. However, this doesn't correspond to the signal on the VIC video output. Also the "Graph." line doesn't show when the border unit generates the border. This happens approx. 8 pixels later than shown in the "Graph." line. To time the accesses of the processor within a raster line when programming, it's best to use the VIC g-accesses for orientation by changing a byte in graphics memory with the 6510 and watching on the screen for which character the change is first visible. The write access of the processor must then have occurred in the clock phase immediately before. Then you can use the diagrams to determine the clock cycle in which the access took place and count the other accesses relative to it. 3.7. Text/bitmap display ------------------------ 3.7.1. Idle state / display state --------------------------------- The text/bitmap display logic in the VIC is in one of two states at any time: the idle state or the display state. - In display state, c- and g-accesses take place, with the addresses and interpretation of the data depending on the selected display mode. - In idle state, only g-accesses occur. The access is always to address $3fff ($39ff when the ECM bit in register $d016 is set). The graphics are displayed by the sequencer exactly as in display state, but with the video matrix data treated as "0" bits. The transition from idle to display state occurs as soon as there is a Bad Line Condition (see section 3.5.). The transition from display to idle state occurs in cycle 58 of a line if the RC (see next section) contains the value 7 and there is no Bad Line Condition. As long as register $d011 is not modified in the middle of a frame, the display logic is in display state within the display window and in idle state outside of it. If you set a YSCROLL other than 3 in a 25 line display window and store a value other than zero in $3fff you can see the black stripes generated by the sequencer in idle state on the upper or lower edge of the window. In [4], idle accesses as well as g-accesses in idle state are called "idle bus cycle", but the two phenomena are not the same. The accesses marked with "+" in the diagrams of [4] are normal g-accesses. In this article, the term "idle access" is only used for the accesses marked with "i" in the diagrams in section 3.6.3., and not for the g-accesses during idle state. 3.7.2. VC and RC ---------------- Probably the most important result of the VIC examinations is the discovery of the function of the internal registers "VC" and "RC" of the VIC. These are used to generate the addresses for accessing the video matrix and the character generator/bitmap. Strictly speaking there are three registers: · "VC" (video counter) is a 10-bit counter that can be loaded with the value from VCBASE. · "VCBASE" (video counter base) is a 10-bit data register with reset input that can be loaded with the value from VC. · "RC" (row counter) is a 3-bit counter with reset input. Apart from these, there is a mechanism in the VIC which keeps track of the position within the internal 40×12 bit video matrix/color line where read character pointers are stored or read back from again, respectively. We will call this the "VMLI" (video matrix line index) here. On the VIC, the VMLI is actually implemented as a 40-bit shift register which under normal circumstances shifts along a single "1" bit that addresses exactly one cell of the video matrix line. To start this sequence in display state, a new "1" bit is shifted in at the input, and in idle state the register has run empty. This may be the reason why idle state graphics (see section 3.7.3.9.) are black - there is no color information coming in from the video matrix line. With programming tricks (see section 3.14.6.) is it possible to manipulate the shift register to contain more than one set bit, causing more than one memory cell to be addressed at the same time. This usually leads to highly unstable behavior and mixing of character and color data which varies among VIC chip revisions and can also depend on factors such as the chip temperature. As these effects are not of any known practical use on the C64, we will in the following simplify the description of the VMLI by treating it as an ordinary 6-bit counter with a reset input. These four registers behave according to the following rules: 1. Once somewhere outside of the range of raster lines $30-$f7 (i.e. outside of the Bad Line range), VCBASE is reset to zero. This is presumably done in raster line 0; the exact moment cannot be determined and is irrelevant. 2. In the first phase of cycle 14 of each line, VC is loaded from VCBASE (VCBASE->VC) and VMLI is cleared. If there is a Bad Line Condition in this phase, RC is also reset to zero. 3. If there is a Bad Line Condition in cycles 12-54, BA is set low and the c-accesses are started. Once started, one c-access is done in the second phase of every clock cycle in the range 15-54. The read data is stored in the video matrix/color line at the position specified by VMLI. This data is then also internally read from the position specified by VMLI on each g-access in display state. 4. VC and VMLI are incremented after each g-access in display state. 5. In the first phase of cycle 58, the VIC checks if RC=7. If so, the video logic goes to idle state and VCBASE is loaded from VC (VC->VCBASE). If the video logic is still in display state (which is always the case if there is a Bad Line Condition), then RC is incremented. These rules normally make VC count all 1000 addresses of the video matrix within the display frame, and RC count the 8 pixel lines of each text line. But this behavior of VC and RC is largely determined by Bad Line Conditions which you can affect with the processor via YSCROLL, giving you more control over VC and RC within certain limits. 3.7.3 Graphics modes -------------------- The graphics data sequencer provides eight different graphics modes that are selected by the bits ECM (Extended Color Mode), BMM (Bit Map Mode) and MCM (Multi Color Mode) in the registers $d011 and $d016. Of the eight possible bit combinations, three are "invalid" and generate the same output: the color black. The idle state is a bit special in that no c-accesses occur in it and the sequencer uses "0" bits for the video matrix data. The sequencer outputs the graphics data in every raster line in the area of the display column as long as the vertical border flip-flop is reset (see section 3.9.). Outside of the display column and if the flip-flop is set, the last current background color is displayed (this area is normally covered by the border). The heart of the sequencer is an 8-bit shift register that is shifted by 1 bit for every pixel, and reloaded with new graphics data after each g-access. With XSCROLL from register $d016 the reloading can be delayed by 0-7 pixels, thus shifting the display up to 7 pixels to the right. If XSCROLL is increased in the middle of a line, the sequencer produces additional background color pixels. If it is decreased, the display of the next set of 8 pixels starts early, aborting the currently displayed character. The address generator for the text/bitmap accesses (c- and g-accesses) basically has three modes for the g-accesses, while the c-accesses always follow the same address scheme. In display state, the BMM bit selects either character generator accesses (BMM=0) or bitmap accesses (BMM=1). In idle state, the g-accesses are always done at video address $3fff. If the ECM bit is set, the address generator always holds the address lines 9 and 10 low without any other changes to the addressing scheme (e.g. the g-accesses in idle state then occur at address $39ff). In the next sections we will cover the eight graphics modes separately, and describe the generated addresses and the interpretation of the read data on c- and g-accesses. This is followed by a description of the peculiarities of the idle state. For easy reference, the addresses are always given explicitly for each mode although, for example, the c-accesses are always identical. 3.7.3.1. Standard text mode (ECM/BMM/MCM=0/0/0) ----------------------------------------------- In this mode (as in all text modes), the VIC reads 8-bit character pointers from the video matrix that specify the address of the dot matrix of the character within the character generator. A character set of 256 characters is available, each consisting of 8×8 pixels which are stored in 8 successive bytes in the character generator. Both video matrix and character generator can be moved in memory with the bits VM10-VM13 and CB11-CB13 of register $d018. In standard text mode, each bit in the character generator directly corresponds to one pixel on the screen. The foreground color is given by the color nybble from the video matrix for each character, the background color is set globally with register $d021. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | Color of | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | | "1" pixels | | | | | | | | | +-------------------+----+----+----+----+----+----+----+----+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13|CB12|CB11| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | | "0": Background color 0 ($d021) | | "1": Color from bits 8-11 of c-data | +---------------------------------------+ 3.7.3.2. Multicolor text mode (ECM/BMM/MCM=0/0/1) ------------------------------------------------- This mode allows displaying four-colored characters at the expense of horizontal resolution. If bit 11 of the c-data is zero, the character is displayed as in standard text mode with only the colors 0-7 available for the foreground. If bit 11 is set, two adjacent bits each of the dot matrix form one pixel. In this way, the resolution of a character is effectively reduced to 4×8 pixels, with each pixel being twice as wide so the total width of the characters remains the same. It is interesting to note that not only the bit combination "00" but also "01" is regarded as "background" for the purposes of sprite priority and collision detection. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | MC | Color of | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |flag| "11" pixels | | | | | | | | | +----+--------------+----+----+----+----+----+----+----+----+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13|CB12|CB11| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | MC flag = 0 | "0": Background color 0 ($d021) | | "1": Color from bits 8-10 of c-data | +---------------------------------------+ | 4 pixels (2 bits/pixel) | | | | "00": Background color 0 ($d021) | MC flag = 1 | "01": Background color 1 ($d022) | | "10": Background color 2 ($d023) | | "11": Color from bits 8-10 of c-data | +---------------------------------------+ 3.7.3.3. Standard bitmap mode (ECM/BMM/MCM=0/1/0) ------------------------------------------------- In this mode, the VIC reads the graphics data from a 320×200 pixel bitmap in which each bit corresponds to one pixel on the screen. The data from the video matrix is used for color information. As the video matrix is still only a 40×25 matrix, you can only specify the colors for blocks of 8×8 pixels individually (sort of a YC 8:1 format). As the designers of the VIC wanted to realize the bitmap mode with as little additional circuitry as possible (the VIC-I didn't have a bitmap mode), the arrangement of the bitmap in memory is somewhat weird: In contrast to modern video chips that read the bitmap in a linear fashion from memory, the VIC forms an 8×8 pixel block on the screen from 8 successive bytes of the bitmap. The video matrix and the bitmap can be moved in memory with the bits VM10-VM13 and CB13 of register $d018. In standard bitmap mode, each bit in the bitmap directly corresponds to one pixel on the screen. Foreground and background color can be arbitrarily set for each 8×8 pixel block. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | unused | Color of | Color of | | | "1" pixels | "0" pixels | +-------------------+-------------------+-------------------+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | | "0": Color from bits 0-3 of c-data | | "1": Color from bits 4-7 of c-data | +---------------------------------------+ 3.7.3.4. Multicolor bitmap mode (ECM/BMM/MCM=0/1/1) --------------------------------------------------- Similar to the multicolor text mode, this mode also forms (twice as wide) pixels by combining two adjacent bits each. So the resolution is reduced to 160×200 pixels. The bit combination "01" is also treated as "background" for the sprite priority and collision detection, as in multicolor text mode. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | Color of | Color of | Color of | | "11 pixels" | "01" pixels | "10" pixels | +-------------------+-------------------+-------------------+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 4 pixels (2 bits/pixel) | | | | "00": Background color 0 ($d021) | | "01": Color from bits 4-7 of c-data | | "10": Color from bits 0-3 of c-data | | "11": Color from bits 8-11 of c-data | +---------------------------------------+ 3.7.3.5. ECM text mode (ECM/BMM/MCM=1/0/0) ------------------------------------------ This text mode is the same as the standard text mode, but it allows the selection of one of four background colors for every single character. The selection is made with the upper two bits of the character pointer. This, however, reduces the available character set from 256 to 64 characters. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | Color of |Back.col.| D5 | D4 | D3 | D2 | D1 | D0 | | "1" pixels |selection| | | | | | | +-------------------+---------+----+----+----+----+----+----+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13|CB12|CB11| 0 | 0 | D5 | D4 | D3 | D2 | D1 | D0 | RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | | "0": Depending on bits 6/7 of c-data | | 00: Background color 0 ($d021) | | 01: Background color 1 ($d022) | | 10: Background color 2 ($d023) | | 11: Background color 3 ($d024) | | "1": Color from bits 8-11 of c-data | +---------------------------------------+ 3.7.3.6. Invalid text mode (ECM/BMM/MCM=1/0/1) ---------------------------------------------- Setting the ECM and MCM bits simultaneously doesn't select one of the "official" graphics modes of the VIC but creates only black pixels. Nevertheless, the graphics data sequencer internally generates valid graphics data that can trigger sprite collisions even in this mode. By using sprite collisions you can also read out the generated data (but you cannot see anything; the screen is black). You can, however, only distinguish foreground and background pixels as you cannot get color information from sprite collisions. The generated graphics is similar to that of the multicolor text mode, but the character set is limited to 64 characters as in ECM mode. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | MC | unused | D5 | D4 | D3 | D2 | D1 | D0 | |flag| | | | | | | | +----+------------------------+----+----+----+----+----+----+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13|CB12|CB11| 0 | 0 | D5 | D4 | D3 | D2 | D1 | D0 | RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | MC flag = 0 | "0": Black (background) | | "1": Black (foreground) | +---------------------------------------+ | 4 pixels (2 bits/pixel) | | | | "00": Black (background) | MC flag = 1 | "01": Black (background) | | "10": Black (foreground) | | "11": Black (foreground) | +---------------------------------------+ 3.7.3.7. Invalid bitmap mode 1 (ECM/BMM/MCM=1/1/0) -------------------------------------------------- This mode also only displays a black screen, but the pixels can still be read out with the sprite collision trick. The structure of the graphics is basically as in standard bitmap mode, but the bits 9 and 10 of the g-addresses are always zero due to the set ECM bit and so the graphics are, roughly said, made up of four "sections" each of which is repeated four times. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | unused | +-----------------------------------------------------------+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13| VC9| VC8| 0 | 0 | VC5| VC4| VC3| VC2| VC1| VC0| RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | | "0": Black (background) | | "1": Black (foreground) | +---------------------------------------+ 3.7.3.8. Invalid bitmap mode 2 (ECM/BMM/MCM=1/1/1) -------------------------------------------------- The last invalid mode also creates a black screen that can be "scanned" with sprite-graphics collisions. The structure of the graphics is basically as in multicolor bitmap mode, but the bits 9 and 10 of the g-addresses are always zero due to the set ECM bit, with the same results as in the first invalid bitmap mode. As usual, the bit combination "01" is part of the background. c-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| VC9| VC8| VC7| VC6| VC5| VC4| VC3| VC2| VC1| VC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | unused | +-----------------------------------------------------------+ g-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |CB13| VC9| VC8| 0 | 0 | VC5| VC4| VC3| VC2| VC1| VC0| RC2| RC1| RC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 4 pixels (2 bits/pixel) | | | | "00": Black (background) | | "01": Black (background) | | "10": Black (foreground) | | "11": Black (foreground) | +---------------------------------------+ 3.7.3.9. Idle state ------------------- In idle state, the VIC reads the graphics data from address $3fff ($39ff if the ECM bit is set) and displays it in the selected graphics mode, but with the video matrix data, normally read in the c-accesses, being all "0" bits. So the byte at address $3fff/$39ff is output repeatedly. c-access No c-accesses occur. Data +----+----+----+----+----+----+----+----+----+----+----+----+ | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+ g-access Addresses (ECM=0) +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Addresses (ECM=1) +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | Standard text mode/ | | Multicolor text mode/ | "0": Background color 0 ($d021) | ECM text mode | "1": Black | +---------------------------------------+ | 8 pixels (1 bit/pixel) | Standard bitmap mode/ | | Invalid text mode/ | "0": Black (background) | Invalid bitmap mode 1 | "1": Black (foreground) | +---------------------------------------+ | 4 pixels (2 bits/pixel) | Multicolor bitmap mode | | | "00": Background color 0 ($d021) | | "01": Black (background) | | "10": Black (foreground) | | "11": Black (foreground) | +---------------------------------------+ | 4 pixels (2 bits/pixel) | Invalid bitmap mode 2 | | | "00": Black (background) | | "01": Black (background) | | "10": Black (foreground) | | "11": Black (foreground) | +---------------------------------------+ 3.8. Sprites ------------ Apart from the text/bitmap graphics, the VIC can display eight independent, 24×21 pixels large, freely movable objects, the "sprites" (called "MOBs" (Movable Object Blocks) in [2]). The sprites can have arbitrary positions on the screen, you can switch each of them on and off with the bits of register $d015 (MxE), expand them by the factor 2 in X and/or Y direction with registers $d017/$d01d (with their resolution still being 24×21 pixels), choose between standard and multicolor mode with register $d01c (MxMC), set the display priority with respect to the text/bitmap graphics with register $d01b (MxDP) and assign a different color to each sprite (registers $d027-$d02e). The VIC also has the ability to detect collisions between sprites among themselves or between sprites and text/bitmap graphics, and to trigger an interrupt on such collisions (see section 3.12.). The position of the top left corner of a sprite is specified via its respective coordinate registers (MxX, MxY). There are 8 bits for the Y coordinate and 9 bits for the X coordinate (the most significant bits of the X coordinates of all sprites are collected in register $d010). 3.8.1. Memory access and display -------------------------------- The 63 bytes of sprite data necessary for displaying 24×21 pixels are stored in memory in a linear fashion: 3 adjacent bytes form one line of the sprite. The start of the data block of each sprite can be chosen in steps of 64 bytes within the 16KB address space of the VIC. For this, the VIC reads a sprite data pointer for each sprite in every raster line from the very last 8 bytes of the video matrix (p-access) that is then used as the upper 8 bits of the address for sprite data accesses (s-accesses). The lower 6 bits come from a sprite data counter (MC0-MC7, one for each sprite) that plays a similar role for the sprites as VC does for the video matrix. As the p-accesses are performed in every raster line you can change the appearance of a sprite in the middle of its display by changing the sprite data pointer. When s-accesses are necessary for a sprite, they are performed in the three half-cycles directly after the sprite's p-access. The VIC also uses the BA and AEC signals (as for the Bad Lines) to access the bus in the second clock phase. BA will also go low three cycles before the proper access in this case. The s-accesses are done in every raster line in which the sprite is visible (for the sprites 0-2, this always happens the line before, see the timing diagrams in section 3.6.3.). Like the text and bitmap graphics, the sprites also have a standard mode and a multicolor mode. In standard mode, every bit directly corresponds to one pixel on the screen. A "0" pixel is transparent and the underlying graphics are visible below it, a "1" pixel is displayed in the respective sprite color defined by registers $d027-$d02e. In multicolor mode, two adjacent bits each form one pixel, thus reducing the resolution of the sprite to 12×21 pixels, with the pixels being twice as wide. Moreover, the sprites can be doubled in their size on the screen in X and/or Y direction (X/Y expansion). The sprite resolution doesn't change but each sprite pixel simply becomes twice as wide or tall. So a pixel of an x-expanded multicolor sprite is four times as wide as a pixel of an unexpanded standard-mode sprite. Although both expansions look similar, they are implemented completely differently in the VIC. The X expansion simply instructs the sprite data sequencer to output pixels at half frequency. But the Y expansion makes the sprite address generator read from the same addresses two lines in succession so that every sprite line is output twice. Each sprite has its own sprite data sequencer whose core is a 24-bit shift register. Apart from that, there are two internal registers for every sprite: · "MC" (MOB Data Counter) is a 6-bit counter that can be loaded from MCBASE. · "MCBASE" (MOB Data Counter Base) is a 6-bit register that can be loaded from MC or reset to zero. Apart from these, there is one “advance line” flip-flop per sprite that is used to control the Y expansion. The display of a sprite is handled according to the following rules (the cycle numbers are only valid for the 6569): 1. The advance line flip-flop is set as long as the corresponding MxYE bit in register $d017 is cleared. 2. In the first phases of cycle 55 and 56, the VIC checks for every sprite if the corresponding MxE bit in register $d015 is set and the Y coordinate of the sprite (odd registers $d001-$d00f) matches the lower 8 bits of RASTER. If this is the case and the DMA for the sprite is still off, the DMA is switched on, MCBASE is cleared, and the advance line flip-flop is set. 3. If the MxYE bit of a sprite is set in the second phase of cycle 56 and the DMA for that sprite is on, then the advance line flip-flop is inverted. 4. In the first phase of cycle 58, the MC of every sprite is loaded from its corresponding MCBASE (MCBASE->MC) and the VIC checks if the DMA for the sprite is turned on and the Y coordinate of the sprite matches the lower 8 bits of RASTER. If this is the case, the display of the sprite is turned on. Otherwise, if the DMA for the sprite is turned off, then its display is also turned off. 5. If the DMA for a sprite is turned on, three s-accesses are performed in sequence in the corresponding cycles assigned to the sprite (see the diagrams in section 3.6.3.). The p-accesses are always done, even if the sprite is turned off. The read data of the first access is stored in the upper 8 bits of the shift register, that of the second one in the middle 8 bits and that of the third one in the lower 8 bits. MC is incremented by one after each s-access. 6. If the sprite display for a sprite is turned on, the shift register is shifted left by one bit with every pixel as soon as the current X coordinate of the raster beam has matched the X coordinate of the sprite (even registers $d000-$d00e), and the bits that are shifted out are displayed. If the MxXE bit belonging to the sprite in register $d01d is set, the register is only shifted every second pixel and the sprite thus appears twice as wide. If the sprite is in multicolor mode, two adjacent bits each form one pixel. 7. In the first phase of cycle 16, the VIC checks for each sprite if its advance line flip-flop is set. If so, MCBASE is loaded from its corresponding MC (MC->MCBASE), thus advancing the sprite display to the next line. After that, the VIC checks if MCBASE is equal to 63 and turns off the DMA of the sprite if this is the case. 7a. As a special case, if the CPU has cleared one of the MxYE bits in cycle 15 and the advance line flip-flop of the corresponding sprite was not set, then the flip-flop is set and in the next cycle (16) the VIC sets MCBASE not to MC but to: MCBASE = ((101010 & (MCBASE & MC)) | (010101 & (MCBASE | MC)) As the test in rule 2 is performed at the end of a raster line, the sprite Y coordinates stored in the registers must be 1 less than the desired Y position of the first sprite line, as the sprite display will not start before the following line, after the first sprite data has been read (as long as the sprite is not positioned to the right of sprite X coordinate $164 (cycle 58, see rule 4)). Sprites can be "reused" vertically: If you change the Y coordinate of a sprite to a later raster line during or after its display has completed, so that the comparisons mentioned in rules 1 and 2 will match again, the sprite is displayed again at that Y coordinate (you may then of course also set a new X coordinate and sprite data pointer). It is therefore possible to display more than 8 sprites on the screen, which is usually referred to as "sprite multiplexing" and is an official design feature of the VIC. This is not possible in the horizontal direction. After 24 displayed pixels, the shift register has run empty and even if you change the X coordinate within a line so that the comparison in rule 6 will match again, no sprite data is displayed any more. So you can only display up to 8 sprites within one raster line at a time. Also note that on a 6569, sprites with an X position in the range $1f8-$1ff become invisible because these values are never reached by the X raster counter; to place a sprite one pixel to the left of X position 0 it has to be at X position $1f7. Here is an overview of the scheme of p- and s-accesses: p-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ |VM13|VM12|VM11|VM10| 1 | 1 | 1 | 1 | 1 | 1 | 1 |Sprite number | +----+----+----+----+----+----+----+----+----+----+----+--------------+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | MP7| MP6| MP5| MP4| MP3| MP2| MP1| MP0| +----+----+----+----+----+----+----+----+ s-access Addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | MP7| MP6| MP5| MP4| MP3| MP2| MP1| MP0| MC5| MC4| MC3| MC2| MC1| MC0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ Data +----+----+----+----+----+----+----+----+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+ | 8 pixels (1 bit/pixel) | | | MxMC = 0 | "0": Transparent | | "1": Sprite color ($d027-$d02e) | +---------------------------------------+ | 4 pixels (2 bits/pixel) | | | | "00": Transparent | MxMC = 1 | "01": Sprite multicolor 0 ($d025) | | "10": Sprite color ($d027-$d02e) | | "11": Sprite multicolor 1 ($d026) | +---------------------------------------+ 3.8.2. Priority and collision detection --------------------------------------- As soon as several graphics elements (sprites and text/bitmap graphics) overlap on the screen, the VIC has to decide which element shall be displayed in the foreground [7]. To do this, every element has an assigned priority, and only the element with highest priority is displayed. The sprites have a fixed hierarchy among themselves: Sprite 0 has the highest and sprite 7 the lowest priority. If two sprites overlap, the sprite with the higher number is displayed only where the other sprite has a transparent pixel. The priority of the sprites to the text/bitmap graphics can be controlled within some limits. First of all, the text/bitmap graphics are divided into foreground pixels and background pixels. Which bit combinations belong to the foreground or background is decided by the MCM bit in register $d016, independently of the state of the graphics data sequencer and of the BMM and ECM bits in register $d011: | MCM=0 | MCM=1 ------------+-------+----------- Bits/pixel | 1 | 2 Pixels/byte | 8 | 4 Background | "0" | "00", "01" Foreground | "1" | "10", "11" In multicolor mode (MCM=1), the bit combinations "00" and "01" belong to the background and "10" and "11" to the foreground, whereas in standard mode (MCM=0), "0" pixels belong to the background and "1" pixels to the foreground. It should be noted that this is also valid for the graphics generated in idle state. Of course, graphics elements with lower priority than any overlaid sprites are visible where all the sprites have transparent pixels. With the MxDP bits from register $d01b, you can separately specify for each sprite whether it shall be displayed in front of or behind the foreground pixels (the table in [2] is wrong): MxDP=0: +-----------------------+ | Background graphics | low priority +-----------------------+ | | Foreground graphics |-+ +-----------------------+ | | Sprite x |-+ +-----------------------+ | | Screen border |-+ | | high priority +-----------------------+ MxDP=1: +-----------------------+ | Background graphics | low priority +-----------------------+ | | Sprite x |-+ +-----------------------+ | | Foreground graphics |-+ +-----------------------+ | | Screen border |-+ | | high priority +-----------------------+ In the latter case, those foreground graphics pixels which overlap non-transparent sprite pixels "inherit" the priority of the sprite. For example, if sprite 0 is set to appear behind the foreground (M0DP=1) then the foreground pixels which overlap the image of sprite always stay visible, even if sprite 0 overlaps with lower-priority sprites which are configured to appear in front of the foreground (MxDP=0). This behavior is hard to represent consistently in a simple picture of stacked graphics layers: +-----------------------+ | Background graphics | low priority +-----------------------+ | | Foreground graphics |-+ | visible through | | sprites 1 and 0 | +-----------------------+ | | Sprite 1 |-+ +-----------------------+ | | Sprite 0 |-+ +-----------------------+ | | Foreground graphics |-+ | in front of sprite 0 | +-----------------------+ | | Screen border |-+ | | high priority +-----------------------+ One way to think about this is to imagine the foreground pixels which overlay a sprite becoming "baked into" the sprite image itself and be handled with the sprite's priority from then on. If you choose one of the invalid video modes only the sprites will be visible (fore- and background graphics will all become black, see sections 3.7.3.6.-3.7.3.8.), but by setting the sprites to appear behind the foreground graphics, the foreground graphics will actually become visible as black pixels overlaying the sprite image. In addition to the priority management, the VIC has the ability to detect collisions of sprites among themselves and of sprites and foreground pixels of the text/bitmap graphics. A collision of sprites among themselves is detected as soon as two or more sprite data sequencers output a non-transparent pixel in the course of display generation (this can also happen somewhere outside of the visible screen area). In this case, the MxM bits of any affected sprites are set in register $d01e and (if allowed, see section 3.12.), an interrupt is generated. The bits remain set until the register is read by the processor and are cleared automatically by the read access. A collision of sprites and other graphics data is detected as soon as one or more sprite data sequencers output a non-transparent pixel and the graphics data sequencer outputs a foreground pixel in the course of display generation. In this case, the MxD bits of the affected sprites are set in register $d01f and (if allowed, see section 3.12.), an interrupt is generated. As with the sprite-sprite collision, the bits remain set until the register is read by the processor. If the vertical border flip-flop is set (normally within the upper/lower border, see next section), the output of the graphics data sequencer is turned off and there are no collisions of sprites with graphics data. 3.9. The border unit -------------------- The VIC uses two flip-flops to generate the border around the display window: A main border flip-flop and a vertical border flip-flop. The main border flip-flop controls the border display. If it is set, the VIC displays the color stored in register $d020, otherwise it displays the color that the priority multiplexer switches through from the graphics or sprite data sequencer. So the border overlays the text/bitmap graphics as well as the sprites. It has the highest display priority. The vertical border flip-flop is for auxiliary control of the upper/lower border. If it is set, the main border flip-flop cannot be reset. Apart from that, the vertical border flip-flop controls the output of the graphics data sequencer. The sequencer only outputs data if the flip-flop is not set, otherwise it displays the background color. This was probably done to prevent sprite-graphics collisions in the border area. There are 2×2 comparators belonging to each of the two flip-flops. These comparators compare the X/Y position of the raster beam with one of two hardwired values, depending on the state of the CSEL and RSEL bits, to control the flip-flops. The comparisons only match if the values are reached precisely. There is no comparison with an interval. The horizontal comparison values are: | CSEL=0 | CSEL=1 ------+------------+----------- Left | 31 ($1f) | 24 ($18) Right | 335 ($14f) | 344 ($158) And the vertical ones are: | RSEL=0 | RSEL=1 -------+-----------+---------- Top | 55 ($37) | 51 ($33) Bottom | 247 ($f7) | 251 ($fb) The flip-flops are switched according to the following rules: 1. When the X coordinate reaches the right comparison value, the main border flip-flop is set. 2. When the Y coordinate reaches the bottom comparison value in cycle 63, the vertical border flip-flop is set. 3. When the Y coordinate reaches the top comparison value in cycle 63 and the DEN bit in register $d011 is set, the vertical border flip-flop is reset. 4. When the X coordinate reaches the left comparison value and the Y coordinate reaches the bottom one, the vertical border flip-flop is set. 5. When the X coordinate reaches the left comparison value and the Y coordinate reaches the top one and the DEN bit in register $d011 is set, the vertical border flip-flop is reset. 6. When the X coordinate reaches the left comparison value and the vertical border flip-flop is not set, the main flip-flop is reset. So the Y coordinate is checked once or twice within each raster line: In cycle 63 and when the X coordinate reaches the left comparison value. By appropriate switching of the CSEL/RSEL bits you can prevent the comparison values from being reached and thus turn off the border partly or completely (see 3.14.1.). 3.10. Display Enable -------------------- The DEN bit (Display Enable, register $d011, bit 4) allows switching the text/bitmap graphics on or off. It is normally set. The bit affects two functions of the VIC: The Bad Lines and the vertical border unit. - A Bad Line Condition can only occur if the DEN bit has been set for at least one cycle somewhere in raster line $30 (see section 3.5.). - If the DEN bit is cleared, the reset input of the vertical border flip-flop is deactivated (see section 3.9.). Thus the upper/lower border will never be turned off. So clearing the DEN bit will normally prevent Bad Lines (and thus c- and g-accesses) from ever occurring and make the whole screen display the border color. 3.11. Lightpen -------------- On a negative edge of the LP input, the current position of the raster beam is latched in the registers LPX ($d013) and LPY ($d014). LPX contains the upper 8 bits (of 9) of the X position and LPY the lower 8 bits (likewise of 9) of the Y position. So the horizontal resolution of the light pen is limited to 2 pixels. Only one negative edge on LP is recognized per frame. If multiple edges occur on LP, all following ones are ignored. The trigger is not released until the start of the next frame. As the LP input of the VIC is connected to the keyboard matrix, like the lines of the joystick ports, it can also be controlled by software. This is done via bit 4 of port B of CIA A ($dc01/$dc03). This allows determining the current X position of the raster beam by triggering an LP edge and reading from LPX afterwards (the VIC has no register that would allow reading the X position directly). This can, for example, be used for cycle-exact synchronization of raster interrupt routines. The values you get from the LPX register can be calculated from the sprite coordinates of the timing diagrams in section 3.6.3. The reference point is the end of the cycle in which the LP line is triggered. For example, if you trigger LP in cycle 20, you get the value $1e in LPX, corresponding to the sprite coordinate $03c (LPX contains the upper 8 bits of the 9 bit X coordinate). The VIC is also able to trigger an interrupt on a negative edge of the LP pin (see next section), likewise only once per frame. 3.12. VIC interrupts -------------------- The VIC has the possibility to generate interrupts for the processor when certain events occur. For this purpose, the IRQ output of the VIC is connected to the IRQ input of the 6510. The VIC interrupts are therefore maskable with the I flag in the processor status register. There are four interrupt sources in the VIC. Each source has a corresponding bit in the interrupt latch (register $d019) and a bit in the interrupt enable register ($d01a). When an interrupts occurs, the corresponding bit in the latch is set. To clear it, the processor has to write a "1" there "by hand". The VIC doesn't clear the latch on its own. If at least one latch bit and the corresponding bit in the enable register is set, the VIC holds the IRQ line low and thereby triggers an interrupt in the processor. So the four interrupt sources can be independently enabled and disabled using the enable bits. Since the VIC, as mentioned, doesn't clear the interrupt latch by itself, the processor has to do this before it resets the I flag or returns from the interrupt routine. Otherwise the interrupt will be re-triggered immediately (the IRQ input of the 6510 is state-sensitive). The following table describes the four interrupt sources and their bits in the latch and enable registers: Bit|Name| Trigger condition ---+----+----------------------------------------------------------------- 0 | RST| Reaching a certain raster line. The line is specified by writing | | to register $d012 and bit 7 of $d011, and internally stored by | | the VIC for the raster compare. The test for reaching the | | interrupt raster line is done in cycle 1 of every line (for line | | 0, in cycle 2). It is possible to trigger an interrupt | | immediately by writing to $d011/$d012, but the interrupt can | | never occur more than once per raster line. 1 | MBC| Collision of at least one sprite with the text/bitmap graphics | | (one sprite data sequencer outputs non-transparent pixel at the | | same time at which the graphics data sequencer outputs a | | foreground pixel) 2 | MMC| Collision of two or more sprites (two sprite data sequencers | | output a non-transparent pixel at the same time) 3 | LP | Negative edge on the LP input (lightpen) For the MBC and MMC interrupts, only the first collision will trigger an interrupt (i.e. if the collision registers $d01e or $d01f contained the value zero before the collision). To trigger further interrupts after a collision, the respective register has to be cleared first by reading from it. The bit 7 in the latch $d019 reflects the inverted state of the IRQ output of the VIC. 3.13. DRAM refresh ------------------ The VIC performs five read accesses in every raster line for refreshing the dynamic RAM. An 8-bit refresh counter (REF) is used to generate 256 DRAM row addresses. The counter is reset to $ff in raster line 0 and decremented by 1 after each refresh access. When the counter underflows, it wraps back to $ff. So the VIC will access addresses $3fff, $3ffe, $3ffd, $3ffc and $3ffb in line 0, addresses $3ffa, $3ff9, $3ff8, $3ff7 and $3ff6 in line 1 etc. Refresh addresses +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ | 1 | 1 | 1 | 1 | 1 | 1 |REF7|REF6|REF5|REF4|REF3|REF2|REF1|REF0| +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ 3.14. Effects and applications ------------------------------ The following sections will describe some graphical effects that can be achieved by applying the rules and mechanisms of the VIC described in the previous sections. 3.14.1. Hyperscreen ------------------- As explained in section 3.9. the VIC generates the screen border by comparing the beam coordinates with start and stop positions selected by the CSEL/RSEL bits. This basically means that the border is not necessarily displayed within a certain range of coordinates, but rather turned on and off at certain coordinates. If you now make sure by appropriately switching CSEL/RSEL that the coordinate comparison never matches, the border may for example never turn on, allowing graphics in the border area to become visible that would normally be covered. The technique is called "hyperscreen" or "opening the border". However, the graphics that can be displayed in the border area is mainly limited to sprites, since the graphics data sequencer is in idle state in this area as no Bad Lines can occur outside of Y coordinates $30-$f7 (see section 3.5.). But you can still make use of the graphics generated in idle state. To turn off the upper/lower border, you proceed as follows: 1. Somewhere in the upper part of the screen, switch to 25-line-border by setting the RSEL bit. 2. Now wait until RASTER has reached a value in the range of 248-250. The vertical border flip-flop is still cleared as the comparison value for RSEL=1 is raster line 251. 3. Then clear the RSEL bit. The comparator is switched and now sets the vertical flip-flop at line 247. But this line has already passed and thus the VIC "forgets" to turn on the vertical border. 4. After raster line 251, set the RSEL bit again and repeat from step 2. If you open the upper/lower border with this method, the left/right border still remains active in the "opened up" area. If you switch from RSEL=0 to RSEL=1 in the raster line range 52-54, the border never turns off and covers the whole screen (similar to when the DEN bit is cleared, but Bad Lines still occur). This is interesting but not very useful. The left/right border can be turned off with the CSEL bit in a similar way. However, the timing is much more critical here. Whereas for the vertical border, you have 4 raster lines for the switch, for the horizontal border the change from CSEL=1 to CSEL=0 has to be exactly in cycle 56. Likewise, the horizontal border can be prevented from turning off by switching from CSEL=0 to CSEL=1 in cycle 17. If you want to open the left/right border in the upper/lower border area, you must either start before the vertical border flip-flop is set (i.e. outside of the upper/lower border), or also open the upper/lower border. This is because the main border flip-flop can only be reset if the vertical flip-flop is not set. If you compare both methods, you can verify that the vertical flip-flop also controls the graphics data sequencer output: With the first method, only the background color is visible in the opened up upper/lower border area, whereas the second method displays the idle state graphics there. 3.14.2. FLD ----------- When building the graphics out of text lines, the VIC is directed exclusively by the occurrence of Bad Lines: A Bad Line gives the "start signal" for the display of one text line. By appropriately changing YSCROLL (in register $d011) you can suppress and arbitrarily delay the Bad Line Condition (see 3.5.). In this way you can exactly control in which raster lines Bad Lines should occur and thus from which raster lines the VIC should start to display one text line. The distance between two text lines can be arbitrarily increased if you hold back the next Bad Line long enough. This effect is called "Flexible Line Distance" (FLD). For example, if you only allow three Bad Lines on the screen at raster lines $50, $78 and $a0, the VIC will also only display three text lines at these positions. The sequencer is in idle state in the lines between. If you only delay the occurrence of the first Bad Line, you can scroll down the entire graphics display by large distances without moving a single byte in display memory. 3.14.3. FLI ----------- Instead of delaying the occurrence of Bad Lines as for the FLD effect, you can also artificially create an additional Bad Line before the VIC has completed the current text line. This is especially interesting for the bitmap modes, as these use the data from the video matrix (which is read in the Bad Lines) for color information, so normally only 8×8 pixel blocks each can be colored individually in bitmap modes. But if you make every raster line a Bad Line by appropriately modifying YSCROLL, then the VIC will read from the video matrix in every line and so it will also read new color information for each line. This way, each of the 4×8 pixels of a block in multicolor mode can have a different color. This software-generated new graphics mode is called "Flexible Line Interpretation" (FLI) and is probably the most outstanding example of "unconventional" VIC programming. There is however one problem: If you create a new Bad Line before the current text line has been finished, VCBASE is not incremented (see section 3.7.2.). The VIC therefore reads from the same addresses of the video matrix as in the previous line. Since you cannot change the video matrix fast enough from the processor, you have to switch the base address of the video matrix with the bits VM10-VM13 of register $d018 if you want different colors for each pixel line. Unfortunately the Color RAM cannot be switched, so the color selection of the pixels is not entirely free. Also, the access to $d011 to create the Bad Line must not happen until cycle 14 of each raster line, or otherwise the RC would be cleared in every line and the bitmap display would not be as desired. But this also implies that the first three c-accesses of the VIC in each line do not read valid data, because the first c-access in cycle 15 requires that BA should have gone low in cycle 12 so that AEC can stay low in cycle 15 (AEC doesn't stay low until three cycles after the negative edge of BA; there is no way around that). Since the Bad Line was generated in cycle 14, while BA is now low in cycle 15 on the first c-access, AEC is still high, however, and so the internal data bus gates D0-D7 of the VIC are closed and the chip reads the value $ff instead of the video matrix data (the data bus gates D8-D11 are indeed open, but this will be explained in section 3.14.6. in more detail). This stray data is visible as 24-pixel wide stripes on the left side of the screen. In practice, you place eight video matrices in memory that are used in the following way: In the first raster line the first line of the first matrix, in the second line the first line of the second matrix, etc., in the eighth line the first line of the eighth matrix, in the ninth line the second line of the first matrix, etc. With these eight matrices you can cover a complete bitmap in line-wise fashion. There are several flavors of the FLI mode, such as AFLI (Advanced FLI) which uses the standard bitmap mode and simulates color blends with similarly colored adjacent pixels, and IFLI (Interlaced FLI) that alternates between two frames in a simulated interlace mode. 3.14.4. Linecrunch ------------------ By manipulating YSCROLL, you have even more possibilities for controlling the Bad Lines. You may also abort a Bad Line before its correct completion by negating the Bad Line Condition within an already begun Bad Line before cycle 14. This his several consequences: · The graphics data is in display state, so graphics are displayed. · The RC is not reset. If you abort the very first line of a frame this way, the RC is still at 7 from the last line of the previous frame. · In cycle 58 of the line the RC is still 7, so the sequencer goes to idle state and VCBASE is loaded from VC. But since the sequencer has been in display state within the line, VC has been incremented after every g-access, so VCBASE has now been effectively increased by 40. The RC doesn't overflow, it stays at 7. With this procedure you have reduced the display of a text line to its last raster line, because as VCBASE has been incremented by 40, the VIC continues with the next line. This effect if therefore called "Linecrunch": You can "crunch" single text lines with it. If you now do this in every raster line, the RC will always stay at 7 and there will be no c-accesses, but VCBASE is incremented by 40 in every line. This eventually makes VCBASE cross the 1000 byte limit of the video matrix and the VIC will display the last, normally invisible, 24 bytes of the matrix (where also the sprite data pointers are stored). VCBASE wraps around to zero when reaching 1024. By crunching whole text lines to one raster line each you have the possibility to quickly scroll the screen contents upwards by large distances without moving bytes in the graphics memory, in a similar way as you can scroll it downwards with FLD. The only disturbing side effect is that the crunched lines will pile up near the upper screen border, looking awkward. Here it is possible to use one of the invalid graphics modes to blank out these lines. 3.14.5. Doubled text lines -------------------------- The display of a text line is normally finished after 8 raster lines, because then RC=7 and in cycle 58 of the last line the sequencer goes to idle state (see section 3.7.2.). But if you now assert a Bad Line Condition between cycles 54-57 of the last line, the sequencer stays in display state and the RC is incremented again (and thus overflows to zero). The VIC will then in the next line start again with the display of the previous text line. But as no new video matrix data has been read, the previous text line is simply displayed twice. 3.14.6. DMA delay / VSP ----------------------- The most sophisticated Bad Line manipulation is to create a Bad Line Condition in cycles 15-53 of a raster line of display window, in which the graphics data sequencer is in idle state, for example by modifying register $d011 so that YSCROLL is now equal to the lower three bits of RASTER. The VIC will then set BA low immediately in the next cycle, switch to display state and start reading from the video matrix. The processor is now stopped because BA is low and it wants to read the next opcode. However, AEC still follows ϕ0 for three cycles before also staying at the low state. This behavior (AEC not until three cycles after BA) is hardwired in the VIC and cannot be avoided. Nevertheless, the VIC accesses the video matrix, or at least it tries, because as long as AEC is still high in the second clock phase, the address and data bus lines D0-D7 of the VIC are in tri-state in the first three cycles, so the VIC reads the value $ff from D0-D7 instead of the data from the video matrix. The data lines D8-D13 of the VIC, however, don't have tri-state drivers and are always set to input. But the VIC doesn't get valid Color RAM data from there either, because with AEC being high, the 6510 is still considered the bus master and unless it by chance wants to read the next opcode from the Color RAM, the chip select input of the Color RAM is not active. Instead, a 4 bit analog (!) switch, U16, connects the data bits D0-D3 of the processor with the Color RAM data bits D8-D13. This connection is always in place when AEC is high and allows the processor to access the Color RAM if desired. To make a long story short: In the first three cycles after BA went low the VIC reads $ff for the character pointers, and for the color information the lower 4 bits of the opcode after the access to $d011. Only then can it read regular video matrix data. This data is stored just as usual at the start of the internal video matrix/color line, and VC gets incremented after each following g-access (with the generation of the Bad Line, the sequencer has also been put into display state). The c- and g-accesses are continued until cycle 54. But since the accesses started in the middle of a line, less than 40 of them took place so VC is no longer a multiple of 40 at the end of the line. Because of the way VC works (see section 3.7.2.), this "misalignment" continues for all following lines. So the entire screen appears scrolled to the right by as many characters as the number of cycles the $d011 access took place after cycle 14. As the c-accesses within this line started later than in a normal Bad Line, this procedure is called "DMA Delay" (or VSP for "Virtual Screen Position"). With this method it is possible to scroll the complete screen sideways by large distances (which also works with bitmap graphics in the same way because the VC is also used for accessing the bitmap data) without having to move the graphics memory with the processor. If you now combine DMA Delay with FLD and Linecrunch, you can scroll entire graphics screens by almost arbitrarily large distances in any direction, using very little computing time. Experimenting with DMA Delay (and with Bad Line effects in general) is also the best method for discovering the internal functions of the VIC, especially of RC and VC, and for determining in which cycles certain things happen inside the VIC. It should also be mentioned that DMA Delay can not only be achieved by manipulating YSCROLL but also with the DEN bit of register $d011. To do this, you have to set YSCROLL to zero so that raster line $30 becomes a bad line and switch DEN from "0" to "1" in the middle of that line. This is because Bad Lines can only occur if the DEN bit has been set for at least one cycle in line $30, and if YSCROLL is zero a Bad Line Condition will occur in line $30 as soon as DEN is set. 3.14.7 Sprite stretching / Sprite crunch ---------------------------------------- As the sprite circuitry is simpler than that for the text graphics, there are not as many special effects possible with sprites, but among them is a very interesting effect that takes advantage of the way the sprite Y expansion works: By modifying the MxYE bits in register $d017 it is not only possible to decide for each individual sprite line if it should be doubled, but you can also repeat a single line three or more times and therefore expand a sprite vertically by an arbitrary scaling factor. This effect can be explained as follows (see section 3.8.1.): Let's say that we are in cycle 55 of a raster line in which sprite 0 is turned on, and whose Y coordinate matches the Y coordinate of the sprite, so we are in the line before the sprite will be displayed. Suppose that the M0YE bit is set. The VIC will then turn on the DMA for sprite 0, clear MCBASE, and set the advance line flip-flop, which is then inverted in the next cycle because of the set M0YE bit. BA goes to low state to allow the VIC to access the sprite data in the second clock phases of cycles 58 and 59. In the first phase of cycle 58, MC is loaded from MCBASE (and so cleared to zero as well), and the p-access for the sprite is performed. The VIC then carries out the three s-accesses and increments MC after each access so it now has the value 3. Now you wait for cycle 16 of the following line. As the advance line flip-flop is not set, MCBASE will still stay at zero. Now you clear the M0YE bit, and thereby set the flip-flop, but then immediately set M0YE to "1" again. Because M0YE remains set, the flip-flop will be inverted again in cycle 56, and is therefore reset (if M0YE hadn't been cleared, the flip-flop would now be set). But this is exactly the same state in which the VIC already was in cycle 56 of the previous line. So the VIC "thinks" that it is still in the first raster line of an expanded sprite and, since MC is still zero, it will proceed to read the first sprite line twice more from memory, three times in total: The first sprite line has been tripled. Another interesting effect, known as "sprite crunch", can be achieved by proceeding exactly as outlined above, but not clearing the M0YE bit after cycle 16 but in the second phase of cycle 15. MCBASE will then be set to 1, and the next sprite line is read from memory with MC=1..3, which is one byte higher than normal. This misalignment continues in the entire display of the sprite. The condition MC=63 for turning off the sprite DMA in cycle 16 is therefore also not met, and the sprite is effectively displayed twice in a row. Not until the end of the second display does the VIC finally turn off the DMA when MC reaches 63. 4. The addresses 0 and 1 and the $de00 area ------------------------------------------- The address range $de00-$dfff of the 6510 (see 2.4.1.) is reserved for external expansions of the C64 and normally not connected to any other devices (RAM, I/O). A read access will fetch data that looks random at first sight. The same is true for the upper nybbles of the addresses $d800-$dbff (the Color RAM). But on some C64 models, this data turns out to be not "random" at all but rather identical to the data that the VIC has read from memory in the first phase of the clock cycle. This effect is however not reproducible on all machines and not always reliable. Apart from the fact that this opens up the possibility of measuring the VIC timing completely in software (the timing diagrams in [4] on which the diagrams in this article are based have, for example, been created using this method), you can also make the 6510 execute programs in the $de00-area or in the Color RAM if the VIC displays graphics in such a way that the 6510 always gets valid opcodes from the graphics data read by the VIC. With a similar effect you can also write to RAM addresses 0 and 1 from the processor. These are normally not accessible as the internal data direction register and data register of the 6510 I/O port are mapped to these addresses, and the data bus drivers of the processor stay in tri-state on a write access. But the R/W line is still set to low state, and so the byte read by the VIC in the first clock phase will be written to RAM. If you want to write a certain value to addresses 0 or 1 you only have to write an arbitrary value to these addresses, taking care that the VIC has read the value you want from RAM in the clock phase before. The addresses 0 and 1 can of course also be read by the processor, either via the $de00-area of with the aid of sprite collisions. For this, you make the VIC display a bitmap starting at address 0 and move a sprite consisting only of one pixel over the bits of the first two bytes of the bitmap. Depending on whether the VIC detects a collision or not, you can find out the state of the individual bits and put them together to one byte. 5. Revision history ------------------- 1996-08-28 - Original version 2000-02-10 - Corrected typos in sections 3.4, 3.6.3, and 3.8 - Minor editorial changes 2024-09-16 - Section 2.2: Added description of the ϕ0 pin - Section 2.3: Added descriptions of the S/LUM, COLOR, and ϕCOLOR pins - Section 3.3: Extended information about the VIC color palette - Section 3.7.2: Corrected the explanation of the VMLI - Sections 3.8.1 and 3.14.7: Corrected the explanation of sprite Y expansion - Section 3.8.2: More detailed description of the interaction between sprite-sprite and sprite-foreground priorities - Complete editorial revision 2024-09-29 - Section 3.12: Clarify raster interrupt behavior Appendix A: Bibliography ------------------------ [1] Commodore Business Machines, "C64 Programmers Reference Guide", Appendix L: "6510 microprocessor data sheet", 1984 [2] ditto, Appendix N: "6566/6567 Video Interface Controller (VIC-II) Chip Specifications" [3] ditto, Chapter 5, Section "Memory management on the Commodore 64" [4] Marko Mäkelä, "The memory accesses of the MOS 6569 VIC-II and MOS 8566 VIC-IIe Video Interface Controller" (AKA: Pal timing), 1994-07-15 [5] John West, Marko Mäkelä, "Documentation for the NMOS 65xx/85xx Instruction Set" (AKA: 64doc), 1994-06-03 [6] Albert J. Charpentier et al., "Digital sine-cosine generator", United States Patent 4,551,682, Nov. 5, 1985 [7] James W. Redfield, Albert J. Charpentier, "Display logic circuit for multiple object priority", United States Patent 4,561,659, Dec. 31, 1985 [8] Philip Timmermann, "Calculating the color palette of the VIC II" https://www.pepto.de/projects/colorvic/ (accessed 2024-09-06) Appendix B: Acknowledgments --------------------------- I want to thank - Marko Mäkelä, Andreas Boose, Pasi Ojala and Wolfgang Lorenz for the amount of work they put into the examination of the VIC - Kaspar Jensen for proof-reading the English version of this document and for his suggestions - Adam Vardy for pointing out that my description of idle state graphics display was wrong - Thilo Girmann for explaining the actual implementation of the VMLI on the VIC - Daniel Kahlin, Hannu Nuotio, Antti Lankila and Andreas Matthies for figuring out how sprite Y expansion really works