Video (TG-16): Difference between revisions

From Data Crystal
Jump to navigation Jump to search
mNo edit summary
Line 1: Line 1:
The TG-16 has two video control chips, a display controller and a color encoder.
[code]
*****************************************************************
*      PC-Engine Video Display Controller Documentation        *
*      .                                              .        *
*  ---+----------------------------------------------+---      *
*      |  MOST COMPLETE HuC6270 INTERNAL WORKINGS    |        *
*      |    DOCUMENT. IF YOU HAPPEN TO FIND *ANY*    |        *
*      |  WRONG INFORMATION, PLEASE CONTACT ME VIA  |        *
*      |  EMAIL AS SOON AS POSSIBLE SO I CAN FIX IT.  |        *
*  ---+----------------------------------------------+---      *
*      :                                              :        *
*                                                              *
*      document revision 0.3 (3rd release)                      *
*                                                              *
*      written by Emanuel Schleussinger in Feb 1998            *
*                ( eschleus@luva.lb.bawue.de )                *
*      Thanks to:                                              *
*          DAVID MICHEL for LOTS of information!!!!!!!!!! ;)    *
*          JENS CHR. RESTEMEIER for his EXCELLENT PCE-docu      *
*          DAVE SHADOFF for his emails and his TGSim source    *
*          NIMAI MALLE for his VDC explanations                *
*          VIDEOMAN for his excellent Hacking Web-Page          *
*                  and some documents in there                *
*          PAUL CLIFFORD for an excellent HuC6270 register docu *
*****************************************************************
 
Revision reference:
-----+-----------------------------+---------------+----------
| rev 0.3:
|  - improved the VDC register table A LOT thanks to the
|    help of PAUL CLIFFORD. Thx for that cool doc, dude!
|      (all those nasty 'unknown's are now eliminated)
|  - more examples here and there.
|  - fixed some docu bugs with help of David Michel.
|  - Added Video Color Encoder reference.
|  - Sprite storage description was WRONG, corrected now.
|
| rev 0.2:
|  - Added Sprite information.
|  - Fixed some major bugs in the docu.
|  - Registers updated.
|
| rev 0.1 (initial release):
|  - Still missing sprite docu, lots of undocumented registers.
-----+-----------------------------+---------------+----------
 
 
Document preface:
 
  This document has been created for both beginners and advanced
  programmers. There may be some information that you may well
  consider 'unnecessary' (such as the introduction to planar image
  storage), but please think of people who would really like to
  program the PC-Engine, but dont have a clue on how some basic
  techniques (like planar) work.
 
  This document is in very early state and may well contain
  a lot of information not being correct. For any wrong in-
  formation in this document you may discover, please write
  me a mail at eschleus@luva.lb.bawue.de so I can fix it and
  release a new version.
  The latest version of this document can always be obtained
  at my homepage located at:
 
          www.classicgaming.com/aec/
 
  or just write me an email and ask me to send you the latest
  revision.
 
  Any help on improving this document is highly appreciated!
 
  I think its the most complete one out there at this time.
 
  Yours,
  Manuel
 
  eschleus@luva.lb.bawue.de
  www.classicgaming.com/aec/
 
--------------------------------------------------------
-----          T  O  P  I  C  S      -------------
--------------------------------------------------------
 
 
        +-------------------------------------------+
        | 1. Purpose of the VDC / General info      |
        |                                          |
        | 2. The VRAM structure / encoding VRAM data|
        |                                          |
        | 3. Accessing the VDC from the CPU        |
        |                                          |
        | 4. The VDC registers in detail            |
        |                                          |
        | 5. The Sprites in the VRAM                |
        |                                          |
        | 6. The Sprite attribute table (SATB)      |
        |                                          |
        | 7. The Video Color Encoder                |
        |                                          |
        +-------------------------------------------+


Information on this page excerpted from http://archaicpixels.com/HuC6270 and http://archaicpixels.com/HuC6260.
--------------------------------------------------------
----- 1. Purpose of the VDC / General info -------------
--------------------------------------------------------


= Video Display Controller (HuC6270) =
  The VDC (Video Display Processor), also known as the HuC6270,
  is the main graphics processing unit in the PC-Engine. Despite
  the CPU of the PC-Engine is only 8-bit, the VDC is a full 16-bit
  processor with very powerful capabilities. Its accessible from
  the main system via 3 special opcodes that write/read data from/
  into the Video Display. The VDC is connected to another chip known
  as the HuC6260 VCE (Video Color Encoder), which supplies the color
  palette data for the Video System.


The video display controller (VDC) is the graphical workhorse of the TG-16.
  The VDC in the PC-Engine has two modes of operation:


Note: all VDC registers accept 16 bit values.
    1. Background character processing
    2. Sprite processing


=== Specifications ===
  The 64 kB VRAM that the VDC is connected to does NOT contain one big
  bitmap with all the display information stored pixel by pixel like
  on a Amiga or PC, the Graphics are stored tile-based. In case you do
  not know what tile-based graphics are, be sure to read section 2
  very carefully.


* 64 simultaneous sprites
* 64KB of VRAM
* a background and foreground layer


= Interface =
== 2. The VRAM structure / encoding VRAM data ==


[code]
[code]
  The VRAM of the PC-Engine is 64 kBytes in size. No chip other
  than the VDC can access it. It contains all the important data
  needed for the display generation.
  The way graphical data is organized in the VRAM is called 'tile
  based'. This means there is NOT a huge bitmap containing a color
  index for every pixel, but only a list of pointers to small,
  rectangular areas in the VRAM that will, aligned to each other,
  make up the display. Explanation follows.
  Think of it like this:
  We have a 512*256 pixel 256 color screen. On a PC, for instance,
  we would have to have the following VRAM structure:
        +---------------------------------------+
        |      <--512 pixels across -->        |
        |                                      |
        |                                      | 256
        |                                      | pixels
        |                                      | down
        |                                      |
        |                                      |
        |                                      |
        |                                      |
        |                                      |
        +---------------------------------------+
  The color depth is 8 bit ^= 256 colors
  This would result in        512 * 256 * 8
                            = 1.048.576 bit
                            = 130 kbytes (roughly)
  So, if the PC-Engine would do it the same way, it would not be
  able to have such high resolutions due to the lack of VRAM.
  Thats why data is stored in the VRAM as follows:
  The screen background area is made up out of 8*8 pixels large
  blocks, called the 'tiles', each tile having a color palette of
  16 colors. There are 16 different palettes to choose from for
  each tile, resulting in 256 different colors for the background
  generation (the other 256 colors are reserved for sprite usage
  which will be described later).
  In the background, colour 0 of all palettes are equal. Colour 0 of
  palette 0 determines colour 0 of all the background palettes. Even
  though these colour CAN be set independently, the screen will not
  reflect these settings.
-----Now, how are those tiles aligned to each other?
  Starting at the very beginning of the VRAM ($0) there is the so-
  called BAT (Block Attribute Table), which is a list of pointers
  to tiles stored in the Video RAM. The amount of pointers varies
  depending on how big the actual screen is. (As I told you, you
  have 8*8 pixel tiles, so if the screen is larger, theres more
  tiles). For our test screen (512*256), we would need:
                            512 / 8    = 64 tiles per line
                            256 / 8    = 32 tiles vertical
                              64 * 32    = 2048 tiles
  That means, we would be in need of a BAT 2048 words in lenght.
-----WHY WORDS? How does a BAT pointer to the VRAM look like?
  A Pointer to a tile in the VRAM must contain palette information
  as well as the actual VRAM address where to find the tile. This
  ONE WORD LONG index pointer looks like this:
      PPPPAAAAAAAAAAAA
      |      |
      |      |
      |      +------- 12 lower bits:  Index of the tile
      |
      +--------------- 4  upper bits:  Palette number (0-15)
  If you multiply the tile index by 32 (LSL #5 ;-), you will get
  the actual VRAM pointer address.
  The pointers in the BAT are ordered from the left to the right
  and top to down.
  ----->Small example:<-----
  Here is the first few words of data in the VRAM of HATRIS, just having
  the intro screen up. If you look closely, note how VRAM was saved using
  the same tiles over and over again in the BAT:
-----HOW CAN I SET THE SIZE OF THE BAT?
  Easy, theres a VDC register dedicated to it, called the MWR register.
  (find more about the MWR in SECTION 4)
    MWR register mask:
  xxxxxxxxxxHWWxxx (16 bits)
                  | |
                  | +--- width in tiles/pixels
                  |      00 = 32/256
                  |      01 = 64/512
                  |      10 = 128/1024
                  |      11 = 128/1024
                  |
                  +----- height in tiles/pixels
                        0  = 32/256
                        1  = 64/512
  If you understood everything, you should now be asking:
  "No TV can display a resolution of 1024 pixels across, so whats
    this mode for?"
  Answer: Check out the BXR and BYR registers used for SCROLLING ;)
          (see SECTION 4)
-----HOW DOES THE TILE ITSELF LOOK LIKE IN THE VRAM?
  Well, the tile itself is a piece of memory sized like this:
      8 * 8 * 4 bits = 256 bits
      |  |  |
      |  |  +------- color index (4 bits per pixel)
      |  +----------- height in pixels
      +--------------- width in pixels
  On this issue, David Michel posted me a VERY good explanation on
  how the data of a single tile is organized in the VRAM:
    The PC-Engine use a planar mode rather than the well known chunky
    mode of PCs, if you already have some experience decoding Atari ST
    or Amiga gfx, you should easily understand the following.
    In planar mode the 4 bits that form the color index are stored
    in 4 separate bytes, let's say that we want to extract the color
    index for the third pixel from the left :
      color index
      3rd pixel
    +---+---+---+---+          +---+---+---+---+---+---+---+---+
    | 3 | 2 | 1 | 0 |  byte 1  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
    +---+---+---+---+          +---+---+---+---+---+---+---+---+
      |  |  |  |                      |
      |  |  |  +-----------------------+
      |  |  |
      |  |  |                +---+---+---+---+---+---+---+---+
      |  |  |        byte 2  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
      |  |  |                +---+---+---+---+---+---+---+---+
      |  |  |                          |
      |  |  +---------------------------+
      |  |
      |  |                    +---+---+---+---+---+---+---+---+
      |  |            byte 3  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
      |  |                    +---+---+---+---+---+---+---+---+
      |  |                              |
      |  +-------------------------------+
      |
      |                        +---+---+---+---+---+---+---+---+
      |                byte 4  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
      |                        +---+---+---+---+---+---+---+---+
      |                                  |
      +-----------------------------------+
    It's as simple as that :)
    The funny part is that those 4 bytes are not placed in order,
    they are interleaved. Byte 1 & 2 are stored first, and bytes
    2 & 3 are stored 16 bytes after, here is another nice drawing:
  VRAM OFFSET
        on pointer
        (in bytes)
                  +---------------------+
              0    | byte 1 & 2 of line 1|
                  +---------------------+
              2    | byte 1 & 2 of line 2|
                  +---------------------+
              4    | byte 1 & 2 of line 3|
                  +---------------------+
              6    | byte 1 & 2 of line 4|
                  +---------------------+
              8    | byte 1 & 2 of line 5|
                  +---------------------+
            10    | byte 1 & 2 of line 6|
                  +---------------------+
            12    | byte 1 & 2 of line 7|
                  +---------------------+
            14    | byte 1 & 2 of line 8|
                  +---------------------+
                  +---------------------+
            16    | byte 3 & 4 of line 1|
                  +---------------------+
            18    | byte 3 & 4 of line 2|
                  +---------------------+
            20    | byte 3 & 4 of line 3|
                  +---------------------+
            22    | byte 3 & 4 of line 4|
                  +---------------------+
            24    | byte 3 & 4 of line 5|
                  +---------------------+
            26    | byte 3 & 4 of line 6|
                  +---------------------+
            28    | byte 3 & 4 of line 7|
                  +---------------------+
            30    | byte 3 & 4 of line 8|
                  +---------------------+
  I think everyone should have got that right now. Thx David!
  If you ask yourself what this was about, consider reading part
  2 again. Part 3 won't be better ;-)
--------------------------------------------------------
== 3. Accessing the VDC from the CPU ==
--------------------------------------------------------
---HOW CAN I TRANSFER DATA INTO THE VRAM?
  Well, there are three memory locations involved that can be read/
  written by the CPU to supply the VDC with data / read data from
  the VDC (all in the I/O Memory Segment $FF):
  Full address    Access Purpose
  $1FE000          R/W    VDC Register select
  $1FE002    R/W    Low Data register
  $1FE003          R/W    High Data register
    The first of the three locations here is the so-called REGISTER
    SELECT. The VDC has 19 Registers (several of them being totally
    unknown, btw) to access. To tell the VDC to which register you
    want to write the value contained in $1FE002 (and $1FE003), simply
    write the number of the register to write to into the low 5 bits
    of $1FE000. As the VDC is a 16 bit processor (ALL VDC registers
    are one word wide) in most cases you will need to supply both
    of the data values.
    Detailed description of the VDC ports by Videoman (slightly changed):
     Address  | Access | Description
     Address  | Access | Description
     (Mapped  |  mode  |
     (Mapped  |  mode  |
Line 61: Line 412:
               |        |        an immediate value.)
               |        |        an immediate value.)


[/code]


== Status Register ==
  ----->One short example on this one:<------
A bit corresponding to one of interruption jobs is set to be "H" in the status register
to make the interruption active when a cause of the interruption which is enabled by an
interruption permission bit of a control register and DMA control register as showing in
Figures 3G and 3Q is occurred.  When the status is read from the status register, the
corresponding bit is cleared automatically.


The status indicating bits are as follows.
    To read the contents of Register 2 (VRAM-Read-Register) simply use the
    following line of code:


=== Read Behavior ===
      ST0 #2
{| class="wikitable"
      ...and then the two data values will sort of 'mirror' the value in
|-
      this VDC register.
! Bit(s) !! Name !! Description !! Details
|-
| 0 || CR
| width="190px" | sprite collision || Sprite #0 has collided with another sprite (1 to 63).
|-
| 1 || OR || sprite overflow ||
* More than 17 sprites are detected on a single raster line.
* Data of a sprites which is designated are not transferred to a data buffer in a horizontal trance period.
* A bit of CGX in control data of a sprite by which two sprites are joined in a horizontal direction is set so that data of the sprites are not transferred to a data buffer.
|-
| 2 || PR || scanline interrupt || A value of a raster counter becomes a predetermined value of a raster detecting register.
|-
| 3 || DS || VRAM to SATB end of transfer. || Data transfer between the VRAM and sprite attribute table buffer is finished.
|-
| 4 || DV || VRAM DMA end of transfer || Data transfer between two regions of VRAM has finished.
|-
| 5 || VD || vertical blanking || The VRAM accessed for the writing or reading of data by the CPU so that the BUSY terminals is "0".
|-
| 6 || BSY || DMA busy || A DMA transfer is in progress.
|-
| 7 - 15 || || (unused) ||
|}


=== Write Behavior ===
{| class="wikitable"
|-
! Bit(s) !! Description
|-
| 0 - 4 || VDC register index (0-19)
|-
| 5 - 15 || (unused)
|}


== Address Register ==
--------------------------------------------------------
== 4. The VDC registers ==
--------------------------------------------------------


A register number "AR" is exclusive written into the address register designating one of
This huge and very complete list has been taken from Videomans
the memory address write register to DMA VRAM-SATB source address register as shown in
hardware map document,  Jens' PCE documentation, and some information
Figures 3C to 3U so that data are writing into the video display controller(1) under the
to it was added by me.
condition that the A1 and CS terminals thereof are "L".


In a case where 16 bit data bus is selected, the EX 8/16 terminal is "0", the A1
terminal is "0", the "R/W" terminal is "W", and the A0 terminal is no matter.


In a case where 8 bit data bus is selected, the EX 8/16 terminal is "1", the A0
REG    ACCESS  DESCRIPTION+
and A1 terminals are "0", and the "R/W" terminal is "W".
NO.    MODE    DETAILS
------------------------------------------------------------------------
0       R?/W   MAWR - 'Memory Address Write Register'


== Data Register ==
        b 15-0  this is the internal
                register used as an address-counter when writing to VRAM.
                All bits used (although no VRAM above $7FFF).


Read/Write register.
1      R?/W    MARR - 'Memory Address Read Register'


Data in the VDC register selected/indexed by the Status Register.
        b 15-0  this is the internal
                register used as an address-counter when reading from VRAM.
                All bits used (although no VRAM above $7FFF).


== VRAM Registers ==
2      R      VRR - 'VRAM Read Register'
=== $00 - MAWR - Memory Address Write Register (VRAM Write Address) ===


A starting address "MAWR" is written into the memory address write register so that the
        b 15-0  this is the only valid read-access
writing of data begins at the starting address of the VRAM(7).
                from the data port.  It reads the value from VRAM at the
                address specified by the MARR.  When the value is read
                from the second byte-port at $0003, the MARR register
                (ie. the 'address to read from') is auto-incremented
                (although this may be a configurable behaviour).
                All bits used.


MAWR specifies a word offset into VRAM for writing. Subsequent writes to register $02 (VWR) will store data at the offset specified by MAWR. After each write, MAWR is incremented by the amount specified in the IW bits of CR. MAWR wraps back to zero when it's value exceeds $FFFF.
2      W      VWR - 'VRAM Write Register'


The LSB and MSB of MAWR can be updated independently of each other; accessing either half directly updates the MAWR register rather than any temporary storage. This allows quick non-sequential addressing of VRAM without having to set the entire address every time.
                (write-access version of the above)
        b 15-0  Write value to VRAM at the address specified by the MAWR.
                When the value is written to the second byte-port at $0003,
                the MAWR register (ie. the 'address to write to') is
                auto-incremented (although this may be a configurable behaviour).


=== $01 - MARR - Memory Address Read Register (VRAM Read Address) ===
3      ?      (unused) ?


A starting address "MARR" is written into the memory address read register.  When the
4      ?      (unused) ?
upper byte of the starting address is written thereinto, data are begun to be read from
the starting address of the VRAM(7) so that data thus read are written into a VRAM data
read register as showing in Figure 3F.  There after, the starting address "MARR" is
automatically incremented by one.


MARR specifies a word offset into VRAM for reading. When the MSB is written, VRAM data from the current offset is transferred into a read buffer, and then MARR is incremented by the amount specified in the IW bits of CR. For any following VRR reads, the buffered value is immediately returned and this process repeats; the buffer is loaded from data at the current offset and MARR is incremented again.
5      ?      CR - 'Control Register'


The LSB of MARR can be updated independently of the MSB. This does not cause the buffer to be loaded, only a write to the MSB will do that.
                    b 15-13 unused
                    b 12-11 'IW' Address register auto-Increment
                                  of the MAWR register
                                  00 - normal increment (+1)
                                  01 - +32
                                  10 - +64
                                  11 - +128


=== $02 - VRR - VRAM Data Read Register ===
                    b 10    'DR' Dynamic RAM refresh (unknown by me though)
                    b 9-8  'TE' Selection of DISP terminal outputs
                                  00 - DISP output "H" during display
                                  01 - BURST colour burst inserting position is
                                      indicated by output "L"
                                  10 - INTHSYNC internal horizontal synchronous
                                      signal
                                  11 - not used
                    b 7    'BB' background (on/off)    --+
                                  1  - display background  |
                                  0  - no background        > gets effective in
                    b 6    'SB' sprites (on/off)          |  next horizontal
                                  1  - display sprites      |  display period.
                                  0  - no sprites        --+
                    b 5-4  'EX' (name unknown by me)
                                  00 - vsync and hsync inputs
                                  01 - vsync input, hsync output
                                  10 - not used
                                  11 - vsync and hsync outputs


A starting address "MARR" is written into the memory address read register. When the
                    b 3    irq (on/off)
upper byte of the starting address is written thereinto, data are begun to be read from
                                  0 = disabled
the starting address of the VRAM(7) so that data thus read are written into a VRAM data
                                  1 = enabled
read register as showing in Figure 3F. There after, the starting address "MARR" is
                    b 2    rcr  (on/off)
automatically incremented by one.
                                  0 = disabled
                                  1 = enabled
                    b 1    Enable interrupt for excess number detection of
                            sprites.
                                  0 = disabled
                                  1 = enabled
                    b 0    Enable interrupt for sprite collision detection.
                                  0 = disabled
                                  1 = enabled


Reading the LSB of VRR returns the LSB of the read buffer. Reading the MSB returns the MSB of the read buffer immediately, then loads the buffer with VRAM from the current offset MARR represents and increments MARR by the value specified by the IW bits of CR. To read only the MSB of multiple words, the MSB of VRR can be repeatedly read instead of reading both the LSB and MSB.
                    Editor's note: bits 3-0 sound suspiciously like
                    interrupt-enable flags. Given what we know about
                    the interrupt vector table, is it logical to assume
                    that the remaining two IE bits stand for the
                    remaining two interrupt vectors? Then again, maybe not.
                      $FFFC-$FFFD    NMI Vector
                      $FFFA-$FFFB    TIMER Vector
                      $FFF8-$FFF9    IRQ1 Vector (for Video)
                      $FFF6-$FFF7    IRQ2 Vector (for BRK)


Note: when reading from VDC addresses $0002 or $0003 when VRR is not selected, the buffer will not be reloaded nor will MARR increment when the MSB is read. The buffer contents will always return the last-loaded value but never update.


=== $02 - VWR - VRAM Data Write Register ===
6      R      RCR - 'Raster Counter Register'


Data which are transferred from the CPU(2) to the VRAM(7) are written into the VRAM data
                    b 15-10 ?
write register. When the upper byte of the data "VWR" is written thereinto, the video
                    b 9-0  The rcr bit controls the generation of a raster
display controller(1) begins to write the data into the VRAM(7) and the address "MAWR" of
                            counter IRQ. The VDC generates an IRQ, when the
the memory address write register is automatically incremented by one upon writing of the
                            scanline specified in the RCR register is displayed.
data.
                            You need to add 64 to the RCR register to get the
                            correct scanline.


When writing to VWR, the LSB is stored in a latch rather than VRAM. Any additional writes to the LSB only update the latch contents and do not affect VRAM. When the MSB is written to, the latched LSB and new MSB data are stored to VRAM at the current offset specified by MAWR. By loading the LSB with a given value and writing to the MSB repeatedly, you can fill VRAM with a constant LSB value and variable MSB value.
7      R?/W    BXR - 'Background X-Scroll Register'


=== $05 - CR - Control Register ===
                    b 15-10 (not used)
                    b 9-0  when the background map is a larger virtual
                            size than the viewing screen shows, this is
                            the viewing screen's x-offset (in pixels)
                            from the origin of the virtual background map.


{| class="wikitable"
8      R?/W    BYR - 'Background Y-Scroll Register'
|-
! Bit(s) !! Description
|-
| 0 - 3 || (IE) enable/[o]disable[/o] interrupt flags
|-
| 0 || collision detection (between sprite #0 and any other sprites).
|-
| 1 || sprite overflow, more than 16 sprites on a scanline.
|-
| 2 || scanline match flag.
|-
| 3 || vertical blanking.
|-
| 4 || (EX) [o]input[/o]/output hsync signal
|-
| 5 || (EX) [o]input[/o]/output vsync signal
|-
| 6 || (SB) sprites enable/[o]disable[/o] flag
|-
| 7 || (BB) background enable/[o]disable[/o] flag
|-
| 8 - 9 || (DR) selects DISP terminal output (pin 27)
{| class="wikitable"
! 9 !! 8 !! Output !! DISP Content
|-
| 0 || 0 || DISP || output "H" during display
|-
| 0 || 1 || BURST || color burst inserting position is indicated by output "L"
|-
| 1 || 0 || INTHSYNC || internal horizontal synchronous signal
|-
| 1 || 1 || || not used
|}
|-
| 10 || (DR) dynamic RAM refresh enable/[o]disable[/o] flag
Refresh address MA0-MA15 upon setting the flag in a case where a


VRAM pixel width (see [[HuC6270#.2409_-_MWR_-_Memory_Width_Register | register $09]]) is of 2 pixels or 4 pixels in a Memory Width Register ($09)
                    b 15-9  (not used)
|-
                    b 8-0  when the background map is a larger virtual
| 11 - 12 || read/write address auto-increment
                            size than the viewing screen shows, this is
                            the viewing screen's y-offset (in pixels)
                            from the origin of the virtual background map.


{| class="wikitable"
9      R?/W    MWR - 'Memory-access Width Register'
! 12 !! 11 !! Increment
|-
| 0 || 0 || 0x01
|-
| 0 || 1 || 0x20
|-
| 1 || 0 || 0x40
|-
| 1 || 1 || 0x80
|}
Affect by how much are incremented the address register $00 and $01.
|-
| 13 - 15 || (unused)
|}


=== $06 - RCR - Raster Counter Register ===
                    Used to configure the size of the virtual background
                    map.


A raster number "RCR" at which an interruption job is performed is written into the
                    b 15-8  (not used)
raster detecting register. An interruption signal is produced when a value of a raster
                    b 7    'CM' (unknown - presumably 'Color Mode')
counter is equal to the raster number "RCR".  The raster counter is preset to be "64" at
                    b 6-4  'SCREEN' These bits control virtual map size
a preceding scanning raster line to a display starting raster line as described in more
                            as noted below.
detail later, and is increased at each raster line by one.
                            b 6    virtual screen height
                                    0 = 256 pixels / 32 tiles
                                    1 = 512 pixels / 64 tiles
                            b 5-4  virtual screen width
                                    00 = 256 pixels / 32 tiles
                                    01 = 512 pixels / 64 tiles
                                    10 = 1024 pixels / 128 tiles
                                    11 = 1024 pixels / 128 tiles


{| class="wikitable"
                                      Complete lookup of
|-
                                      available sizes in tiles:
! Bit(s) !! Description
                                      -------------------------
|-
                                        000 -  32 x 32
| 0 - 9 || The rcr bit controls the generation of a raster counter IRQ. The VDC generates an IRQ, when the scanline specified in the RCR register is displayed. You need to add 64 to the RCR register to get the correct scanline.
                                        001 - 64 x 32
|-
                                        010 - 128 x 32
| 10 - 15 || (unused)
                                        011 - 128 x 32
|}
                                        100 -  32 x 64
                                        101 - 64 x 64
                                        111 - 128 x 64


A raster number "RCR" at which an interruption job is performed is written into the raster detecting register.
An interruption signal is produced when a value of a raster counter is equal to the raster number "RCR".  The raster counter is preset to be "64" at a preceding scanning raster line to a display starting raster line as described in more detail later, and is increased at each raster line by one.


=== $07 - BXR - Background X-Scroll Register ===
                    b 3-2  Sprite pixel width
The BGX scroll register is used for a horizontal scroll of background on a screen.  When
                    b 1-0  VRAM pixel width
a content "BXR" is rewritten therein, the content is effective in the following raster
line.


The value written to BXR is latched on each scanline, preventing mid-scanline changes to BXR. Further changes to BXR will not change the display until the next scanline is displayed. When the VDC generates synchronization signals this duration is in units of VDC scanlines, and when the VDC inputs external synchronization signals this is in units of VCE scanlines.
10($A)  ?      HSR - 'Horizontal Sync Register'


For example if the VDC displays multiple VDC scanlines in one VCE scanline, the same BXR value applies to all VDC scanlines until the current VCE scanline ends.
                    b 15    (not used)
                    b 14-8  'HDS' Horizontal display start position -1.
                    b 7-5  (not used)
                    b 4-0  'HSW' Horizontal synchronous pulse width.


=== $08 - BYR - Background Y-Scroll Register ===
                    Mask = $7F1F


The BGY scroll register is used for a vertical scroll of background on a screen. When a
11($B) ?      HDR - 'Horizontal Display Register'
content "BYR" is rewritten therein, the content is effective to be as "BYR+1" in the
following raster line.


The value written to BYR is latched on each scanline, preventing mid-scanline changes to BYR. Further changes to BYR will not change the display until the next scanline is displayed. When the VDC generates synchronization signals this duration is in units of VDC scanlines, and when the VDC inputs external synchronization signals this is in units of VCE scanlines.
                    b 15    (not used)
                    b 14-8  'HDE' Horizontal display ending period -1.
                    b 7    (not used)
                    b 6-0  'HDW' Horizontal display width in tiles -1.


For example if the VDC displays multiple VDC scanlines in one VCE scanline, the same BYR value applies to all VDC scanlines until the current VCE scanline ends.
                    Mask = $7F7F


=== $09 - MWR - Memory Width Register ===
                    added from Jens' PCE-documentation: Lower half of HDR:
                      It controls the horizontal width of display generation.
                      The value in this register is the number of horizontal
                      tiles minus one. Normal values are 31, for 32 tiles
                      and 256 pixel horizontally, 39, for 40 tiles or 320
                      pixel and 63, for 64 tiles or 512 pixel.


==== Character cycles ====
12($C)  ?      VPR - 'Vertical synchronous register'
The fundamental unit of time observed by the VDC is the duration of one pixel clock cycle. The pixel clock is output by the VCE and can be any of the following:


* 5.3693175 MHz - ~186 ns per pixel
                    b 15-8  'VDS' Vertical display start position -2.
* 7.15909 MHz - ~140 ns per pixel
                    b 7-5  (not used)
* 10.738635 MHz - ~93 ns per pixel
                    b 4-0  'VSW' Vertical synchronous pulse width.


The VDC accesses VRAM in groups called character cycles. Each character cycle can be split into eight slots, which have a duration of one pixel clock each. The actual VRAM read or write cycle spans one or more slots, selectable in units of 1, 2, or 4 slots each.
                    Mask = $FF1F


Here's a diagram showing the number of VRAM accesses that can be made in one character cycle depending on number of slots allocated to read or write cycle:


{| class="wikitable"
13($D)  ?      VDW - 'Vertical display register'
|-
! VRAM cycle width !! Slot 1 !! Slot 2 !! Slot 3 !! Slot 4 !! Slot 5 !! Slot 6 !! Slot 7 !! Slot 8
|-
! 1 slot
| 1 || 2 || 3 || 4 || 5 || 6 || 7 || 8
|-
! 2 slots
| colspan="2" | 1
| colspan="2" | 2
| colspan="2" | 3
| colspan="2" | 4
|-
! 4 slots
| colspan="4" | 1
| colspan="4" | 2
|}


Within the same period of time a character cycle spans, up to 8 accesses can be done when the VRAM access cycle width is 1 slot, 4 accesses can be done when cycle width is  2 slots, and only two access can be done when the cycle width is 4 slots.
                    b 15-9  (not used)
                    b 8-0  Vertical display width in pixels -1.


The PCE uses 100ns SRAM chips as it's video RAM, so the only situation that is problematic is using a 1-cycle pixel width along with the 10.738635 MHz pixel clock. In this case each cycle is ~93 ns which violates the minimum access time requirements of the SRAM. In practice this does not cause any problems, however it does mean operating the memory 7% faster than it's guaranteed to work. This can be remedied by using a pixel width mode with longer cycles.
                    NOTE:
                        Unlike the HDR register, the information on the
                        vertical display width is split up in two registers,
                        this one storing the vertical width, and the next one
                        (VCR) containing the vertical display end position.


==== VRAM pixel width ====
The VDC will make as many sequential character cycles as the screen is wide as specified in the HDW field plus two, regardless of any horizontal scroll setting. These occur back-to-back in realtime as the display is rendered (I think there is a 1 or 2 character pipeline before any pixels are actually output). For example if the screen is 32 characters wide, 34 character cycles occur.


Bits 1, 0 of MWR set the VRAM access cycle grouping, referred to as the 'VRAM pixel width'. Bit 7 sets the character generator read mode when only two of four bitplanes can be read, due to insufficient VRAM access cycles available.
14($E)  ?      VCR - 'Vertical display END position register'


Bits 1-0&nbsp;: VRAM pixel width.
                    b 15-8  (not used)
                    b 7-0   Vertical display end position.


{| class="wikitable"
15($F) ?      DCR - 'DMA Control Register'
|-
! D1-D0 !! Slot 1 !! Slot 2 !! Slot 3 !! Slot 4 !! Slot 5 !! Slot 6 !! Slot 7 !! Slot 8
|-
! 00
| CPU || BAT || CPU || || CPU || CG0 || CPU || CG1
|-
! 01
| colspan="2" | BAT
| colspan="2" | CPU
| colspan="2" | CG0
| colspan="2" | CG1
|-
! 10
| colspan="2" | BAT
| colspan="2" | CPU
| colspan="2" | CG0
| colspan="2" | CG1
|-
! 11
| colspan="4" | BAT
| colspan="4" | CG0 / CG1
|}


* BAT is a read from the BAT region of VRAM. (word contains palette, character name)
    The DCR, SOUR, DESR and LENR registers control
* CPU is a CPU access, either read or write.
    DMA operations.
* CG0 is a read from the character generator region of VRAM. (word contains bitplane 0 and 1 bytes)
    The DMA operation starts, as soon as the length
* CG1 is a read from the character generator region of VRAM. (word contains bitplane 2 and 3 bytes)
    is written into the LENR register


The first three modes function identically. The last mode only has enough spare time in each character cycle to read CG0 or CG1, but not both. Selection of either bitplane group is done by the character generator mode bit (CM), which is bit 7 of MWR. It specifies 0= CG0 or 1= CG1. Internally, the VDC assumes the missing bitplane data is forced to zero. This means that tiles displayed when CM=0 use colors 0,1,2,3, and tiles displayed when CM=1 use colors 0,4,8,C.
                                              b 15-5 (not used)
                                              b 4 -  DSR DMA (VRAM-SATB transfer repetition)
                    b 3 -  Increment (0)/decrement (1) of
                          destination address.
                    b 2 -  Increment (0)/decrement (1) of
                          source address.
                    b 1 -  Enable interrupt at completion of
                          VRAM-VRAM transfer.
                          Checked on completion of transfer.
                    b 0 -  Enable interrupt at completion of
                          VRAM-SATB transfer.
                          Checked on completion of transfer.


==== Sprite pixel width ====
16($10) R?/W    SOUR - '(DMA) Source Address Register'
During the horizontal blanking period, the VDC fetches character generator data for the sprites (up to 16) that passed y-evaluation and have their respective data buffered in the VDC's on-chip sprite storage. The bitplane data is loaded into shift registers and will be output serially during the next scanline.


The duration of the fetch period directly relates to how much horizontal blanking time is available, as defined by the HSW, HDS, HDW, and HDE registers. If the period is too short, the process is aborted. It seems the sprites that weren't loaded have their shift registers reset to zero, as previously loaded sprite data or garbage data is not shown (this needs more testing).
                    b 15-0  This register sets the source address
                            for DMA transfers.
                            All bits used (address pointer).


Much like background rendering, bits 3-2 of MWR set the character cycle allocation for sprites, referred to as the 'Sprite pixel width'.
17($11) R?/W    DESR - '(DMA) Destination Address Register'


===== Bits 3-2&nbsp;: Sprite pixel width. =====
                    b 15-0  This register sets the destination
                            address for DMA transfers.
                            All bits used
                            (although no VRAM above $7FFF).


{| class="wikitable"
18($12) R?/W    LENR - '(DMA) Block Length Register'
|-
! D1-D0 !! Slot 1 !! Slot 2 !! Slot 3 !! Slot 4 !! Slot 5 !! Slot 6 !! Slot 7 !! Slot 8
|-
! 00
| SP0 || SP1 || SP2 || SP3 || SP0 || SP1 || SP2 || SP3
|-
! 01
| colspan="2" | SP0, SP2
| colspan="2" | SP1, SP3
| colspan="2" | SP0, SP2
| colspan="2" | SP1, SP3
|-
! 10
| colspan="2" | SP0
| colspan="2" | SP1
| colspan="2" | SP2
| colspan="2" | SP3
|-
! 11
| colspan="4" | SP0 / SP2
| colspan="4" | SP1 / SP3
|}


* SP0-3 are sprite bitplanes 0,1,2,3.
                    b 15-0 This register sets the length of
* 00b reads data for two sprites in one cycle.
                            the DMA transfer.
* 01b reads data for two sprites in two cycles (bitplanes 0,1 for sprites 1,2 in cycle, bitplanes 2,3 for sprites 1,2 in the next).
                            All bits used
* 10b reads data for one sprite in one cycle.
                            (although no VRAM above $7FFF).
* 11b reads data for one sprite in one cycle, but only bitplanes 0,1 or 2,3 can be read.


Bit 0 of the pattern code field of each sprite entry specifies which bitplanes are read for a sprite pixel width setting of 11b. It can be 0= SP0,SP1 or 1= SP2,SP3. The unused bitplanes are forced to zero so that the colors used out of a 16-color palette are 0,1,2,3 when SP0,SP1 are read, or 0,4,8,C when SP2,SP3 are read.
19($13) R?/W    SATB - 'Sprite Attribute Table'


== Display Registers ==
                    b 15-0  This register points to the start address
                            of the sprite attribute table.
                            All bits used
                            (although no VRAM above $7FFF).


=== $0A - HPR - Horizontal Synchronous Register ===


Bits 0-4&nbsp;: Horizontal Sync Width (HSW)<br>
Bits 8-14&nbsp;: Horizontal Display Start (HDS)


HSW defines the width of the horizontal sync pulse in 8-pixel (character) units. The range is 1 to 32 characters.
--------------------------------------------------------
== 5. The Sprites in the VRAM ==
--------------------------------------------------------


HDS defines the interval after the horizontal sync pulse to the start of the horizontal display period in character units. The range is 1 to 128 characters.
  Well, I will not try to explain what Sprites are here. Basically,
  all of the PC-Engines' sprites are 16*16 to 32*64 pixels in size,
  and have a sprite palette of 16 colors.
  There are 16 separate sprite palettes available. (remember, there
  was 16*16 colors for the background processing, those colors are
  INDEPENDENT from the sprite palettes).


  In the Sprites colours, colour 0 is transparent in all palettes,
  although it does peek it's head in a peculiar place; beyond the display
  width of the BG.
  Explanation:
  The background display area (in it's most often used setting) is 256x216.
  The display width of a television may be adjusted to squash the screen
  vertically, or horizontally. Even normal TVs show a little more that 256
  TG-16 pixel wide, leaving a black border on the sides. This border colour is
  actually controlled by sprite colour 0. The programmer can actually set the
  screen width more narrow or vertically shorter, showing more of this area.
  It's only use that I've ever implemented was in measuring the CPU load of
  the TG-16 during development.


When the VDC inputs external synchronization signals, the function of HSW changes. It no longer affects the width of the horizontal sync pulse. Instead, if during the processing of any VDC-generated scanline the HDE state expires prior to an external HSYNC pulse, the number of characters as specified by HSW are taken up before the next VDC-generated scanline starts.
--HOW ARE SPRITES STORED IN THE VRAM?


This distinction is important; increasing values of HSW do not displace the horizontal display area immediately following an external HSYNC pulse, but they will for all subsequent VDC-generated scanlines before /HSYNC occurs again.
  For the sprite characters the principe is the same as for the background
  tiles, but in place of using bytes (8 pixels) they use words (16 pixels).
  Note that the words still use the same encoding as all word data within the
  PC-Engine, this means that the first byte of the word is the lower byte.
  Sprite data is stored like in the following drawing:


=== $0B - HDR - Horizontal Display Register ===
            Byte          Data
          offset


Bits 0-6&nbsp;: Horizontal Display Width (HDW)<br>
                  +-------------------+
Bits 8-14&nbsp;: Horizontal Display End (HDE)
              0   | plane 1 of line 1 |
                  +-------------------+
              2    | plane 1 of line 2 |
                  +-------------------+
                  .                  .
                  :                  :
            30    | plane 1 of line 16|
                  +-------------------|
            32    | plane 2 of line 1 |
                  +-------------------+
            34    | plane 2 of line 2 |
                  +-------------------+
            36    | plane 2 of line 3 |
                  +-------------------+
                  .                  .
                  :                   :
            46    | plane 2 of line 16|
                  +-------------------+
            48    | plane 3 of line 1 |
                      ......and so on.


HDW defines the width of the horizontal active display period in character units. The range is 1 to 128 characters.


HDE defines the interval following HDE to the end of the scanline, at which poinst the HSW state is entered and a horizontal sync pulse is generated. The range is 1 to 128 characters. It should be set to the remainder from the desired number of characters per scanline, minus HSW, HDS, and HDW.


=== $0C - VSR - Vertical Synchronous Register ===
  Not only you can display sprites, you can do some sort of funny stuff
         
  with them, like mirroring, for instance. All this is controlled in the
==== Bits 0 to 4 (VSW) - vertical synchronous pulse width ====
  so-called SPRITE ATTRIBUTE TABLE.
A pulse width of a vertical synchronous signal is decided in a width of "L" level
as a unit of a raster line. One of 1 to 32 is selected to comply with a
specification of a CRT display.


==== Bits 8 to 15 (VDS) - vertical display starting position ====
A period between a rising edge of a vertical synchronous signal and a vertical
synchronous starting position is set as an unit of a raster line.  When it is assumed
that a vertical display starting position (vertical back porch) is "N", "N-2" is
written into the bits.


=== $0D - VDR - Vertical Display Register ===


A vertical display period (display region) is set as an unit of a raster line.  A
--------------------------------------------------------
vertical display width is decided in accordance with the number of raster lines to be
== 6. The Sprite attribute table (SATB) ==
displayed on a CRT display which is defined by a content of the 9 bits. When it is
  --------------------------------------------------------
assumed that a vertical display width is "N", "N-1" is written into the VDW bits.


=== $0E - VCR - Vertical Display Ending Postition Register ===
  The sprites' positions and attributes are defined in the so-
A period between a vertical display ending position and a rising edge of a vertical
  called SPRITE ATTRIBUTE TABLE (SATB). The SATB can be contained any-
synchronous signal is set as an unit of a raster line. When it is assumed that a
  where in the VRAM ($0000-$7FFF).
vertical optimum position (vertical front porch) is "N" to be defined by the 8 bits, "N" is written into the VCR bits.


==== Bits 7-0&nbsp;: Vertical Display Position End (VCR) ====
--HOW DOES THE VDC KNOW WHERE THE SATB IS TO BE FOUND?


VCR defines the interval following VDW to the end of the frame, at which point the VSW state is entered and a vertical sync pulse is generated. The range is 0 to 255 scanlines. It should be set to the remainder from the desired number of scanlines per frame, minus VSW, VDS, and VDW.
  The VDC has a special register containing nothing but the start
  address of the SATB in the VRAM. This is register 19 (SEE SECTION 4)


When the VDC inputs external synchronization signals, VCR should be set to a value equal to or larger than the number of scanlines the hardware generates from one edge of /VSYNC to the next. Otherwise the VDC will start generating another frame within the current display frame. This can be used to arbitrarily force additional VD interrupts and VRAM to SAT DMA transfers within a single VCE-defined frame.
  The actual sprite attributes are stored at the address mentioned
  above. For aech sprite, there is a 4 word long attribute section,
  which looks as follows:


== DMA Registers ==
  Word  | Access | Description
  offset |  mode  |
--------------------------------------------------------------------------
    0    R/W    Y position


The Dynamic Memory Access subsystem (DMA) is the means by which the VDC reads directly from RAM.
                b 15-10  (unused)
                b 9-0    y position (relative to
                              virtual-screen origin)


=== $0F - DCR - DMA Control Register ===
    1    R/W    X position


{| class="wikitable"
                b 15-19  (unused?)
|-
                b 9-0     x position (relative to
|bit 0 (DSC)
                              virtual-screen origin)
|enable interruption at the completion of transfer between the VRAM and sprite attribute table buffer
|0 - disable
1 - enabled
|-
|bit 1 (DVC)
|Enable interruption at the completion of transfer between two regions of the VRAM
|0 - disable
1 - enabled
|-
|bit 2 (SI/D)
|Automatic increment or decrement of a source address selected in a transfer between two regions of VRAM
|0 - disable
1 - enabled
|-
|bit 5 (DSR)
|repetition of a transfer between the VRAM(7) and the sprite attribute table buffer is enabled.
|0 - disable
1 - enabled
|}


=== $10 - SOUR - Source Address Register ===
    2    R/W    Pattern address


          A starting address of a source address is allocated in a transfer between two regions
                b 15-11  (unused?)
          of the VRAM(7).
                b 10-0      sprite data VRAM address shifted
                              right 5 bits(Shift left 6 bits to
                              get real VRAM address)


== $11 - DESR - DMA Destination Address Register ==
    3    R/W    Sprite attributes


          A starting address of a destination address is allocated in a transfer between two
                b 15    y-invert flag (upside-down)
          regions of the VRAM(7).
                b 14    unused
                b 13-12  'CGY'
                      00 = sprite is 1 'cell' (16 pixels) high
                      01 = sprite is 2 cells high (32 pixels)
                      10 = invalid
                      11 = sprite is 4 cells high (64 pixels)
                b 11    x-invert flag (left-right invert)
                b 10-9    unused
                b 8  'CGX'
                    0 = sprite is 1 'cell' wide (16 pixels)
                    1 = sprite is 2 cells wide (32 pixels)
                b 7  'SPBG'; is sprite in foreground (in front
                                    of CG) or background (behind CG)
                b 6-4    unused
                b 3-0    sprite colour (i.e. which of 16 sprite
                                            palettes to use)


== $12 - LENR - DMA Block Length Register ==
[/code]
          A length of a block is defined in a transfer between two regions of the VRAM(7).
 
== $13 - SATB - Sprite Attribute Table Address Register ==
 
The address of the Sprite Attribute Table.  This is the only address used for access to the SATB.
 
=== VRAM Access ===
Typically when loading large amounts of data into VRAM the screen is turned off for several frames. In most video hardware turning the screen off stops display related DMA and gives the CPU full access to VRAM.
 
The VDC handles things a bit differently. BURST mode is when the color bus outputs $0100 on VD8-VD0 (sprite palette #0, color #0), display DMA is stopped (no fetching of BAT data, background patterns, sprite patterns), and the CPU has unrestricted access to VRAM regardless of the MWR settings. BURST mode is enabled in two situations:
 
*1. Any display state outside of VDW is considered to be in the BURST mode. A possible exception is that display DMA needs to be done on line 262 or 263 (depending on the frame height) for graphics to be displayed on scanline 0.
 
*2. If bits 7 and 6 of CR are reset prior to VDW occurring, BURST mode is forcibly entered for the entire duration of VDW. Any changes to bits 7 and 6 have no effect until the next transition into VDW, at which point they are sampled again.
 
Note that this means simply turning off the background and/or sprites during VDW does *not* select BURST mode, and VRAM access is still restricted. When the background is turned off during the display, the color bus outputs $0000 on VD8-VD0 (background palette #0, color #0).
 
To maximize VRAM throughput, it isn't necessary to force a BURST-in-VDW display condition. The duration of VDW can just be shortened to letterbox the screen and allocate more scanlines to to the other display periods, giving more BURST time.
 
A MWR setting of $00 gives the CPU the largest amount of access cycles (twice per 8 pixels) which seems to be exactly equal to the amount of accesses available during BURST mode.
 
= Video Color Encoder (HuC6260) =
 
The VCE has two functions:
*supply the picture on your television.
*define the location of the palettes in memory.
 
All VCE registers are 16 bit.
 
== $0400 - CR - Control Register ==
 
Write only register.
 
{| class="wikitable"
|-
! Bit(s) || Description || Values
|-
| 0 - 1 || PCC - Pixel Clock Control ||
00 = 5.3693175 MHz
 
01 = 7.15909 MHz
 
10 = 10.738635 MHz
 
11 = 10.738635 MHz
|-
| 2 || Frame/Field Configuration ||
0 = 262-line frame
 
1 = 263-line frame
|-
| 3 - 6 || ??? || ???
|-
| 7 || Strip Colorburst ||
0 = Colorburst intact
 
1 = Strip colorburst
|-
| 8 - 15 || (unused) ||
|}
 
== $0402 - CTA - Color Table Address Register ==
 
Write only register.
 
{| class="wikitable"
|-
! Bit(s) || Description || Values
|-
| 0 - 8 || index for the color table || 0 to 511
|-
| 9 - 15 || (unused) ||
|}
 
Note: This register is auto-incremented after each access to the color data register.
 
== $0404 - CTW - Color Table Write Register / CTR - Color Table Read Register ==
 
Write/Read register.
 
Access (read/write) to this register causes CTA register to increment.
 
{| class="wikitable"
|-
! Bit(s) || Description
|-
| 0 - 2 || Blue
|-
| 3 - 5 || Red
|-
| 6 - 8 || Green
|-
| 9 - 15 || (unused)
|}

Revision as of 02:09, 3 March 2016

[code]

*****************************************************************
*      PC-Engine Video Display Controller Documentation         *
*      .                                              .         *
*   ---+----------------------------------------------+---      *
*      |   MOST COMPLETE HuC6270 INTERNAL WORKINGS    |         *
*      |    DOCUMENT. IF YOU HAPPEN TO FIND *ANY*     |         *
*      |   WRONG INFORMATION, PLEASE CONTACT ME VIA   |         *
*      |  EMAIL AS SOON AS POSSIBLE SO I CAN FIX IT.  |         *
*   ---+----------------------------------------------+---      *
*      :                                              :         *
*                                                               *
*      document revision 0.3 (3rd release)                      *
*                                                               *
*      written by Emanuel Schleussinger in Feb 1998             *
*                 ( eschleus@luva.lb.bawue.de )                 *
*      Thanks to:                                               *
*          DAVID MICHEL for LOTS of information!!!!!!!!!! ;)    *
*          JENS CHR. RESTEMEIER for his EXCELLENT PCE-docu      *
*          DAVE SHADOFF for his emails and his TGSim source     *
*          NIMAI MALLE for his VDC explanations                 *
*          VIDEOMAN for his excellent Hacking Web-Page          *
*                   and some documents in there                 *
*          PAUL CLIFFORD for an excellent HuC6270 register docu *
*****************************************************************
Revision reference:
-----+-----------------------------+---------------+----------
| rev 0.3:
|   - improved the VDC register table A LOT thanks to the
|     help of PAUL CLIFFORD. Thx for that cool doc, dude!
|       (all those nasty 'unknown's are now eliminated)
|   - more examples here and there.
|   - fixed some docu bugs with help of David Michel.
|   - Added Video Color Encoder reference.
|   - Sprite storage description was WRONG, corrected now.
|
| rev 0.2:
|   - Added Sprite information.
|   - Fixed some major bugs in the docu.
|   - Registers updated.
|
| rev 0.1 (initial release):
|   - Still missing sprite docu, lots of undocumented registers.
-----+-----------------------------+---------------+----------


Document preface:
  This document has been created for both beginners and advanced
  programmers. There may be some information that you may well
  consider 'unnecessary' (such as the introduction to planar image
  storage), but please think of people who would really like to
  program the PC-Engine, but dont have a clue on how some basic
  techniques (like planar) work.
  This document is in very early state and may well contain
  a lot of information not being correct. For any wrong in-
  formation in this document you may discover, please write
  me a mail at eschleus@luva.lb.bawue.de so I can fix it and
  release a new version.
  The latest version of this document can always be obtained
  at my homepage located at:
         www.classicgaming.com/aec/
  or just write me an email and ask me to send you the latest
  revision.
  Any help on improving this document is highly appreciated!
  I think its the most complete one out there at this time.
  Yours,
  Manuel
  eschleus@luva.lb.bawue.de
  www.classicgaming.com/aec/
--------------------------------------------------------
-----           T   O   P   I   C   S      -------------
--------------------------------------------------------


       +-------------------------------------------+
       | 1. Purpose of the VDC / General info      |
       |                                           |
       | 2. The VRAM structure / encoding VRAM data|
       |                                           |
       | 3. Accessing the VDC from the CPU         |
       |                                           |
       | 4. The VDC registers in detail            |
       |                                           |
       | 5. The Sprites in the VRAM                |
       |                                           |
       | 6. The Sprite attribute table (SATB)      |
       |                                           |
       | 7. The Video Color Encoder                |
       |                                           |
       +-------------------------------------------+
--------------------------------------------------------
----- 1. Purpose of the VDC / General info -------------
--------------------------------------------------------
 The VDC (Video Display Processor), also known as the HuC6270,
 is the main graphics processing unit in the PC-Engine. Despite
 the CPU of the PC-Engine is only 8-bit, the VDC is a full 16-bit
 processor with very powerful capabilities. Its accessible from
 the main system via 3 special opcodes that write/read data from/
 into the Video Display. The VDC is connected to another chip known
 as the HuC6260 VCE (Video Color Encoder), which supplies the color
 palette data for the Video System.
 The VDC in the PC-Engine has two modes of operation:
   1. Background character processing
   2. Sprite processing
 The 64 kB VRAM that the VDC is connected to does NOT contain one big
 bitmap with all the display information stored pixel by pixel like
 on a Amiga or PC, the Graphics are stored tile-based. In case you do
 not know what tile-based graphics are, be sure to read section 2
 very carefully.


== 2. The VRAM structure / encoding VRAM data ==

[code]

 The VRAM of the PC-Engine is 64 kBytes in size. No chip other
 than the VDC can access it. It contains all the important data
 needed for the display generation.
 The way graphical data is organized in the VRAM is called 'tile
 based'. This means there is NOT a huge bitmap containing a color
 index for every pixel, but only a list of pointers to small,
 rectangular areas in the VRAM that will, aligned to each other,
 make up the display. Explanation follows.
 Think of it like this:
 We have a 512*256 pixel 256 color screen. On a PC, for instance,
 we would have to have the following VRAM structure:
        +---------------------------------------+
        |       <--512 pixels across -->        |
        |                                       |
        |                                       | 256
        |                                       | pixels
        |                                       | down
        |                                       |
        |                                       |
        |                                       |
        |                                       |
        |                                       |
        +---------------------------------------+
 The color depth is 8 bit ^= 256 colors
 This would result in        512 * 256 * 8
                           = 1.048.576 bit
                           = 130 kbytes (roughly)
 So, if the PC-Engine would do it the same way, it would not be
 able to have such high resolutions due to the lack of VRAM.
 Thats why data is stored in the VRAM as follows:
 The screen background area is made up out of 8*8 pixels large
 blocks, called the 'tiles', each tile having a color palette of
 16 colors. There are 16 different palettes to choose from for
 each tile, resulting in 256 different colors for the background
 generation (the other 256 colors are reserved for sprite usage
 which will be described later).
 In the background, colour 0 of all palettes are equal. Colour 0 of
 palette 0 determines colour 0 of all the background palettes. Even
 though these colour CAN be set independently, the screen will not
 reflect these settings.

Now, how are those tiles aligned to each other?

 Starting at the very beginning of the VRAM ($0) there is the so-
 called BAT (Block Attribute Table), which is a list of pointers
 to tiles stored in the Video RAM. The amount of pointers varies
 depending on how big the actual screen is. (As I told you, you
 have 8*8 pixel tiles, so if the screen is larger, theres more
 tiles). For our test screen (512*256), we would need:
                            512 / 8     = 64 tiles per line
                            256 / 8     = 32 tiles vertical
                             64 * 32    = 2048 tiles
 That means, we would be in need of a BAT 2048 words in lenght.

WHY WORDS? How does a BAT pointer to the VRAM look like?

 A Pointer to a tile in the VRAM must contain palette information
 as well as the actual VRAM address where to find the tile. This
 ONE WORD LONG index pointer looks like this:
     PPPPAAAAAAAAAAAA
      |       |
      |       |
      |       +------- 12 lower bits:   Index of the tile
      |
      +--------------- 4  upper bits:   Palette number (0-15)
 If you multiply the tile index by 32 (LSL #5 ;-), you will get
 the actual VRAM pointer address.
 The pointers in the BAT are ordered from the left to the right
 and top to down.
 ----->Small example:<-----
 Here is the first few words of data in the VRAM of HATRIS, just having
 the intro screen up. If you look closely, note how VRAM was saved using
 the same tiles over and over again in the BAT:



HOW CAN I SET THE SIZE OF THE BAT?

 Easy, theres a VDC register dedicated to it, called the MWR register.
 (find more about the MWR in SECTION 4)
   MWR register mask:
 xxxxxxxxxxHWWxxx (16 bits)
                 | |
                 | +--- width in tiles/pixels
                 |      00 = 32/256
                 |      01 = 64/512
                 |      10 = 128/1024
                 |      11 = 128/1024
                 |
                 +----- height in tiles/pixels
                        0  = 32/256
                        1  = 64/512
 If you understood everything, you should now be asking:
  "No TV can display a resolution of 1024 pixels across, so whats
   this mode for?"
 Answer: Check out the BXR and BYR registers used for SCROLLING ;)
         (see SECTION 4)



HOW DOES THE TILE ITSELF LOOK LIKE IN THE VRAM?

 Well, the tile itself is a piece of memory sized like this:
      8 * 8 * 4 bits = 256 bits
      |   |   |
      |   |   +------- color index (4 bits per pixel)
      |   +----------- height in pixels
      +--------------- width in pixels
 On this issue, David Michel posted me a VERY good explanation on
 how the data of a single tile is organized in the VRAM:
   The PC-Engine use a planar mode rather than the well known chunky
   mode of PCs, if you already have some experience decoding Atari ST
   or Amiga gfx, you should easily understand the following.
   In planar mode the 4 bits that form the color index are stored
   in 4 separate bytes, let's say that we want to extract the color
   index for the third pixel from the left :
     color index
      3rd pixel
   +---+---+---+---+           +---+---+---+---+---+---+---+---+
   | 3 | 2 | 1 | 0 |   byte 1  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
   +---+---+---+---+           +---+---+---+---+---+---+---+---+
     |   |   |   |                       |
     |   |   |   +-----------------------+
     |   |   |
     |   |   |                 +---+---+---+---+---+---+---+---+
     |   |   |         byte 2  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
     |   |   |                 +---+---+---+---+---+---+---+---+
     |   |   |                           |
     |   |   +---------------------------+
     |   |
     |   |                     +---+---+---+---+---+---+---+---+
     |   |             byte 3  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
     |   |                     +---+---+---+---+---+---+---+---+
     |   |                               |
     |   +-------------------------------+
     |
     |                         +---+---+---+---+---+---+---+---+
     |                 byte 4  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
     |                         +---+---+---+---+---+---+---+---+
     |                                   |
     +-----------------------------------+
   It's as simple as that :)
   The funny part is that those 4 bytes are not placed in order,
   they are interleaved. Byte 1 & 2 are stored first, and bytes
   2 & 3 are stored 16 bytes after, here is another nice drawing:
 VRAM OFFSET
       on pointer
       (in bytes)
                  +---------------------+
             0    | byte 1 & 2 of line 1|
                  +---------------------+
             2    | byte 1 & 2 of line 2|
                  +---------------------+
             4    | byte 1 & 2 of line 3|
                  +---------------------+
             6    | byte 1 & 2 of line 4|
                  +---------------------+
             8    | byte 1 & 2 of line 5|
                  +---------------------+
            10    | byte 1 & 2 of line 6|
                  +---------------------+
            12    | byte 1 & 2 of line 7|
                  +---------------------+
            14    | byte 1 & 2 of line 8|
                  +---------------------+
                  +---------------------+
            16    | byte 3 & 4 of line 1|
                  +---------------------+
            18    | byte 3 & 4 of line 2|
                  +---------------------+
            20    | byte 3 & 4 of line 3|
                  +---------------------+
            22    | byte 3 & 4 of line 4|
                  +---------------------+
            24    | byte 3 & 4 of line 5|
                  +---------------------+
            26    | byte 3 & 4 of line 6|
                  +---------------------+
            28    | byte 3 & 4 of line 7|
                  +---------------------+
            30    | byte 3 & 4 of line 8|
                  +---------------------+
  I think everyone should have got that right now. Thx David!
  If you ask yourself what this was about, consider reading part
  2 again. Part 3 won't be better ;-)


--------------------------------------------------------

3. Accessing the VDC from the CPU

--------------------------------------------------------

---HOW CAN I TRANSFER DATA INTO THE VRAM?

 Well, there are three memory locations involved that can be read/
 written by the CPU to supply the VDC with data / read data from
 the VDC (all in the I/O Memory Segment $FF):
 Full address    Access Purpose
 $1FE000          R/W    VDC Register select
 $1FE002    R/W    Low Data register
 $1FE003          R/W    High Data register
   The first of the three locations here is the so-called REGISTER
   SELECT. The VDC has 19 Registers (several of them being totally
   unknown, btw) to access. To tell the VDC to which register you
   want to write the value contained in $1FE002 (and $1FE003), simply
   write the number of the register to write to into the low 5 bits
   of $1FE000. As the VDC is a 16 bit processor (ALL VDC registers
   are one word wide) in most cases you will need to supply both
   of the data values.
   Detailed description of the VDC ports by Videoman (slightly changed):
   Address   | Access | Description
   (Mapped   |  mode  |
    to $FF)  |        |
     $0000   |   R    | 6270 Status register
             |        |
             |        |   Different bits flag different conditions.
             |        |   Not all are known.
             |        |   (Note: can use special ST0 opcode to store
             |        |   an immediate value.)
             |        |     b 7 = 0
             |        |     b 6 = 'BSY' flag
             |        |         I believe this is '1' when a DMA transfer
             |        |         is happening
             |        |     b 5 = 'VD' flag
             |        |         I believe this is a '1' when Vertical Sync
             |        |         happens, otherwise a '0' (uncertain)
             |        |     b 4 = 'DV' flag (unknown)
             |        |     b 3 = 'DS' flag (unknown)
             |        |     b 2 = 'RR' flag
             |        |         Set during a Scanline interrupt (see RCR
             |        |         register)otherwise '0'
             |        |     b 1 = 'OR' flag (unknown)
             |        |     b 0 = 'CR' flag (unknown)
             |        |
     $0000   |   W    | 6270 Address register
             |        |
             |        |     b 7-5 = ignored
             |        |     b 4-0 = 6270 register number to access using
             |        |             the 6270 data registers
             |        |             ($0002 and $0003). Please see 6270
             |        |             register list (SECTION 4) for details.
             |        |
     $0002   |  R/W   | 6270 data LSB
             |        |
             |        |   Note: can use special ST1 opcode to store
             |        |         an immediate value.)
             |        |
     $0003   |  R/W   | 6270 data MSB
             |        |
             |        |   Note: can use special ST2 opcode to store
             |        |         an immediate value.)


 ----->One short example on this one:<------
   To read the contents of Register 2 (VRAM-Read-Register) simply use the
   following line of code:
      ST0 #2
      ...and then the two data values will sort of 'mirror' the value in
      this VDC register.


--------------------------------------------------------
== 4. The VDC registers ==
--------------------------------------------------------
This huge and very complete list has been taken from Videomans
hardware map document,  Jens' PCE documentation, and some information
to it was added by me.


REG ACCESS DESCRIPTION+ NO. MODE DETAILS


0 R?/W MAWR - 'Memory Address Write Register'

       b 15-0  this is the internal
               register used as an address-counter when writing to VRAM.
               All bits used (although no VRAM above $7FFF).

1 R?/W MARR - 'Memory Address Read Register'

       b 15-0  this is the internal
               register used as an address-counter when reading from VRAM.
               All bits used (although no VRAM above $7FFF).

2 R VRR - 'VRAM Read Register'

       b 15-0  this is the only valid read-access
               from the data port.  It reads the value from VRAM at the
               address specified by the MARR.  When the value is read
               from the second byte-port at $0003, the MARR register
               (ie. the 'address to read from') is auto-incremented
               (although this may be a configurable behaviour).
               All bits used.

2 W VWR - 'VRAM Write Register'

               (write-access version of the above)
       b 15-0  Write value to VRAM at the address specified by the MAWR.
               When the value is written to the second byte-port at $0003,
               the MAWR register (ie. the 'address to write to') is
               auto-incremented (although this may be a configurable behaviour).

3  ? (unused) ?

4  ? (unused) ?

5  ? CR - 'Control Register'

                    b 15-13 unused
                    b 12-11 'IW' Address register auto-Increment
                                 of the MAWR register
                                 00 - normal increment (+1)
                                 01 - +32
                                 10 - +64
                                 11 - +128
                    b 10    'DR' Dynamic RAM refresh (unknown by me though)
                    b 9-8   'TE' Selection of DISP terminal outputs
                                 00 - DISP output "H" during display
                                 01 - BURST colour burst inserting position is
                                      indicated by output "L"
                                 10 - INTHSYNC internal horizontal synchronous
                                      signal
                                 11 - not used
                    b 7     'BB' background (on/off)     --+
                                 1  - display background   |
                                 0  - no background         > gets effective in
                    b 6     'SB' sprites (on/off)          |  next horizontal
                                 1  - display sprites      |  display period.
                                 0  - no sprites         --+
                    b 5-4   'EX' (name unknown by me)
                                 00 - vsync and hsync inputs
                                 01 - vsync input, hsync output
                                 10 - not used
                                 11 - vsync and hsync outputs
                    b 3     irq  (on/off)
                                 0 = disabled
                                 1 = enabled
                    b 2     rcr  (on/off)
                                 0 = disabled
                                 1 = enabled
                    b 1     Enable interrupt for excess number detection of
                            sprites.
                                 0 = disabled
                                 1 = enabled
                    b 0     Enable interrupt for sprite collision detection.
                                 0 = disabled
                                 1 = enabled
                    Editor's note: bits 3-0 sound suspiciously like
                    interrupt-enable flags. Given what we know about
                    the interrupt vector table, is it logical to assume
                    that the remaining two IE bits stand for the
                    remaining two interrupt vectors? Then again, maybe not.
                     $FFFC-$FFFD     NMI Vector
                     $FFFA-$FFFB     TIMER Vector
                     $FFF8-$FFF9     IRQ1 Vector (for Video)
                     $FFF6-$FFF7     IRQ2 Vector (for BRK)


6 R RCR - 'Raster Counter Register'

                   b 15-10 ?
                   b 9-0   The rcr bit controls the generation of a raster
                           counter IRQ. The VDC generates an IRQ, when the
                           scanline specified in the RCR register is displayed.
                           You need to add 64 to the RCR register to get the
                           correct scanline.

7 R?/W BXR - 'Background X-Scroll Register'

                   b 15-10 (not used)
                   b 9-0   when the background map is a larger virtual
                           size than the viewing screen shows, this is
                           the viewing screen's x-offset (in pixels)
                           from the origin of the virtual background map.

8 R?/W BYR - 'Background Y-Scroll Register'

                   b 15-9  (not used)
                   b 8-0   when the background map is a larger virtual
                           size than the viewing screen shows, this is
                           the viewing screen's y-offset (in pixels)
                           from the origin of the virtual background map.

9 R?/W MWR - 'Memory-access Width Register'

                   Used to configure the size of the virtual background
                   map.
                   b 15-8  (not used)
                   b 7     'CM' (unknown - presumably 'Color Mode')
                   b 6-4   'SCREEN' These bits control virtual map size
                           as noted below.
                           b 6     virtual screen height
                                   0 = 256 pixels / 32 tiles
                                   1 = 512 pixels / 64 tiles
                           b 5-4   virtual screen width
                                   00 = 256 pixels / 32 tiles
                                   01 = 512 pixels / 64 tiles
                                   10 = 1024 pixels / 128 tiles
                                   11 = 1024 pixels / 128 tiles
                                      Complete lookup of
                                      available sizes in tiles:
                                      -------------------------
                                        000 -  32 x 32
                                        001 -  64 x 32
                                        010 - 128 x 32
                                        011 - 128 x 32
                                        100 -  32 x 64
                                        101 -  64 x 64
                                        111 - 128 x 64


                   b 3-2   Sprite pixel width
                   b 1-0   VRAM pixel width

10($A)  ? HSR - 'Horizontal Sync Register'

                   b 15    (not used)
                   b 14-8  'HDS' Horizontal display start position -1.
                   b 7-5   (not used)
                   b 4-0   'HSW' Horizontal synchronous pulse width.
                   Mask = $7F1F

11($B)  ? HDR - 'Horizontal Display Register'

                   b 15    (not used)
                   b 14-8  'HDE' Horizontal display ending period -1.
                   b 7     (not used)
                   b 6-0   'HDW' Horizontal display width in tiles -1.
                   Mask = $7F7F
                   added from Jens' PCE-documentation: Lower half of HDR:
                     It controls the horizontal width of display generation.
                     The value in this register is the number of horizontal
                     tiles minus one. Normal values are 31, for 32 tiles
                     and 256 pixel horizontally, 39, for 40 tiles or 320
                     pixel and 63, for 64 tiles or 512 pixel.

12($C)  ? VPR - 'Vertical synchronous register'

                   b 15-8  'VDS' Vertical display start position -2.
                   b 7-5   (not used)
                   b 4-0   'VSW' Vertical synchronous pulse width.
                   Mask = $FF1F


13($D)  ? VDW - 'Vertical display register'

                   b 15-9  (not used)
                   b 8-0   Vertical display width in pixels -1.
                   NOTE:
                       Unlike the HDR register, the information on the
                       vertical display width is split up in two registers,
                       this one storing the vertical width, and the next one
                       (VCR) containing the vertical display end position.


14($E)  ? VCR - 'Vertical display END position register'

                   b 15-8  (not used)
                   b 7-0   Vertical display end position.

15($F)  ? DCR - 'DMA Control Register'

   The DCR, SOUR, DESR and LENR registers control
   DMA operations.
   The DMA operation starts, as soon as the length
   is written into the LENR register
                                              b 15-5 (not used)
                                              b 4 -  DSR DMA (VRAM-SATB transfer repetition)
                   b 3 -  Increment (0)/decrement (1) of
                          destination address.
                   b 2 -  Increment (0)/decrement (1) of
                          source address.
                   b 1 -  Enable interrupt at completion of
                          VRAM-VRAM transfer.
                          Checked on completion of transfer.
                   b 0 -  Enable interrupt at completion of
                          VRAM-SATB transfer.
                          Checked on completion of transfer.

16($10) R?/W SOUR - '(DMA) Source Address Register'

                    b 15-0  This register sets the source address
                            for DMA transfers.
                            All bits used (address pointer).

17($11) R?/W DESR - '(DMA) Destination Address Register'

                    b 15-0  This register sets the destination
                            address for DMA transfers.
                            All bits used
                            (although no VRAM above $7FFF).

18($12) R?/W LENR - '(DMA) Block Length Register'

                    b 15-0  This register sets the length of
                            the DMA transfer.
                            All bits used
                            (although no VRAM above $7FFF).

19($13) R?/W SATB - 'Sprite Attribute Table'

                    b 15-0  This register points to the start address
                            of the sprite attribute table.
                            All bits used
                            (although no VRAM above $7FFF).


--------------------------------------------------------
== 5. The Sprites in the VRAM ==
--------------------------------------------------------
 Well, I will not try to explain what Sprites are here. Basically,
 all of the PC-Engines' sprites are 16*16 to 32*64 pixels in size,
 and have a sprite palette of 16 colors.
 There are 16 separate sprite palettes available. (remember, there
 was 16*16 colors for the background processing, those colors are
 INDEPENDENT from the sprite palettes).
 In the Sprites colours, colour 0 is transparent in all palettes,
 although it does peek it's head in a peculiar place; beyond the display
 width of the BG.
 Explanation:
 The background display area (in it's most often used setting) is 256x216.
 The display width of a television may be adjusted to squash the screen
 vertically, or horizontally. Even normal TVs show a little more that 256
 TG-16 pixel wide, leaving a black border on the sides. This border colour is
 actually controlled by sprite colour 0. The programmer can actually set the
 screen width more narrow or vertically shorter, showing more of this area.
 It's only use that I've ever implemented was in measuring the CPU load of
 the TG-16 during development.

--HOW ARE SPRITES STORED IN THE VRAM?

 For the sprite characters the principe is the same as for the background
 tiles, but in place of using bytes (8 pixels) they use words (16 pixels).
 Note that the words still use the same encoding as all word data within the
 PC-Engine, this means that the first byte of the word is the lower byte.
 Sprite data is stored like in the following drawing:
           Byte           Data
          offset
                  +-------------------+
             0    | plane 1 of line 1 |
                  +-------------------+
             2    | plane 1 of line 2 |
                  +-------------------+
                  .                   .
                  :                   :
            30    | plane 1 of line 16|
                  +-------------------|
            32    | plane 2 of line 1 |
                  +-------------------+
            34    | plane 2 of line 2 |
                  +-------------------+
            36    | plane 2 of line 3 |
                  +-------------------+
                  .                   .
                  :                   :
            46    | plane 2 of line 16|
                  +-------------------+
            48    | plane 3 of line 1 |
                     ......and so on.


 Not only you can display sprites, you can do some sort of funny stuff
 with them, like mirroring, for instance. All this is controlled in the
 so-called SPRITE ATTRIBUTE TABLE.


--------------------------------------------------------
== 6. The Sprite attribute table (SATB) ==
--------------------------------------------------------
 The sprites' positions and attributes are defined in the so-
 called SPRITE ATTRIBUTE TABLE (SATB). The SATB can be contained any-
 where in the VRAM ($0000-$7FFF).

--HOW DOES THE VDC KNOW WHERE THE SATB IS TO BE FOUND?

 The VDC has a special register containing nothing but the start
 address of the SATB in the VRAM. This is register 19 (SEE SECTION 4)
 The actual sprite attributes are stored at the address mentioned
 above. For aech sprite, there is a 4 word long attribute section,
 which looks as follows:
 Word   | Access | Description
 offset |  mode  |

   0     R/W     Y position
               b 15-10   (unused)
               b 9-0     y position (relative to
                              virtual-screen origin)
   1     R/W     X position
               b 15-19   (unused?)
               b 9-0     x position (relative to
                              virtual-screen origin)
   2     R/W     Pattern address
               b 15-11   (unused?)
               b 10-0      sprite data VRAM address shifted
                              right 5 bits(Shift left 6 bits to
                              get real VRAM address)
   3     R/W     Sprite attributes
               b 15     y-invert flag (upside-down)
               b 14     unused
               b 13-12  'CGY'
                     00 = sprite is 1 'cell' (16 pixels) high
                     01 = sprite is 2 cells high (32 pixels)
                     10 = invalid
                     11 = sprite is 4 cells high (64 pixels)
               b 11     x-invert flag (left-right invert)
               b 10-9     unused
               b 8   'CGX'
                    0 = sprite is 1 'cell' wide (16 pixels)
                    1 = sprite is 2 cells wide (32 pixels)
               b 7   'SPBG'; is sprite in foreground (in front
                                    of CG) or background (behind CG)
               b 6-4    unused
               b 3-0    sprite colour (i.e. which of 16 sprite
                                            palettes to use)

[/code]