Last modified on 29 December 2018, at 07:31

V9990

Revision as of 07:31, 29 December 2018 by Arnoldemu (Talk | contribs) (LMMV)

The V9990 E-VDP-III or 'Graphics 9000' is a Video Display Processor (VDP) by Yamaha which is based on an unreleased VDP (V9978) that was intended for the unreleased MSX3.

This chip is available on graphics cards for the MSX and CPC.

Documentation

Programming Documentation Manual for the V9990 VDP from Yamaha as a PDF.

File:Yamaha v9990.pdf

Technical

The official 'Application Manual' describes the V9990 ports, registers and commands. These additional notes apply to the V9990 used in the CPC Powergraph video board and explain details that are not clearly described or are omitted from the manual. Note, these notes do not describe 100% of the operation, they describe the features and combinations tested to date.

In the following "databus" means that the v9990 doesn't assert the bus, it doesn't provide data, so the value read is "floating-bus" or the value on the z80 databus at the time. More commonly on CPC it's &ff, but there are some configurations of CPC where this differs.

In the following "width" and "height" refer to "image space width" and "image space height" which differ from display width and height.

Kanji ROM

Testing is limited because the powergraph doesn't have a Kanji ROM.

  • Reading from the Kanji ROM ports without a Kanji ROM returns databus values

Ports and registers

  • Reading from the write only ports (or unused ports) returns databus values.
  • Palette red data has a mask of &9F (bits red and color key bit), blue and green have a mask of 0x01f.

e.g.

LD BC,&FF64
LD A,14
OUT (C),C
LD BC,&FF63
LD A,0
OUT (C),A

LD BC,&FF61
LD A,&FF ;; <- reading this component back returns &9F
OUT (C),A 
LD A,&FF ;; <- reading this component back returns &1F
OUT (C),A 
LD A,&FF ;; <- reading this component back returns &1F
OUT (C),A 
  • Bits 1,0 of R14 define a ternary counter. If counter is set to 3 then reads will return 0 and writes are ignored. If auto increment is enabled, it will increment as normal. When ternary counter is 2 or 3, then palette index will increment and ternary counter will go to 0.

e.g.

LD BC,&FF64
LD A,14
OUT (C),C
LD BC,&FF63
LD A,3
OUT (C),A

LD BC,&FF61
LD A,&FF 
OUT (C),A ;; write is not done.
LD BC,&FF64
LD A,14
OUT (C),C
LD BC,&FF63
LD A,3
OUT (C),A

LD BC,&FF61
IN A,(C)  ;; read returns last value written to port

e.g.

LD BC,&FF64
LD A,14
OUT (C),C
LD BC,&FF63
LD A,2
OUT (C),A

LD BC,&FF61
LD A,&FF 
OUT (C),A  ;; palette index will be 1, and ternary counter is 0.
  • If a command transfers data, and it requests data to be written to the command data port, then you can't use a read of this port to clear the data request. It must be a write. Similarly, if a command requests data to be read then you can use a write to clear the data request.
  • If you read from a write-only register you will see data-bus value because the V9990 doesn't assert data on the bus.
  • Some registers have additional bits which are not documented. The mask describes which bits are read/write and which are unchanged.

Where the mask has a '1' bit, this bit is read/write. Where the mask has a '0' bit this bit remains at 0 and can't be changed.

  - Register 7 (SCREEN MODE): Mask is &FF.
  - Register 9 (INTERRUPT READ/WRITE): Mask is &87
  - Register 15 (BACK DROP COLOR): Mask is &FF 
  - Register 22 (SCROLL CONTROL): Mask is &C1
  - Register 25 (SPRITE PATTERN GENERATOR TABLE BASE ADDRESS): Mask is &CF
  - Register 26 (LCD CONTROL): Mask is &FF
  - Register 27 (PRIORITY CONTROL): Mask is &FF
  • VRAM read/write via registers, 0,1,2 and 3,4,5 and port 0 use *logical* addresses and not physical addresses. Writing in one mode, and then reading back in another can yield different data because the addresses are translated from physical to logical based on the mode.
  • Register index is masked with &3f. This means reading 64 is the same as reading 0.

Interrupts

  • Powergraph generates a maskable interrupt on CPC.

LOP

  • Testing indicates TP operates on the data that will be written (SC) and happens *before* write mask.
  • DC is read from physical VRAM. WC is the result of LOP on SC and DC and is the data that will be written. WC is the data that is masked using the write mask.

Write Mask

  • R46 (Write mask) is used when writing to physical VRAM0.
  • R47 (Write mask) is used when writing to physical VRAM1.
  • Testing indicates write mask takes effect just before write to VRAM is committed.

So you need to know when each mask register is used and you need to understand the logical->physical VRAM mapping.

Write Mask in bitmap and standby modes

  • Even logical VRAM addresses (logical address bit 0 = 0) map to VRAM0 and use R46 for masking.
  • Odd logical VRAM address (logical address bit = 1) map to VRAM1 use R47 for masking.

Reset

The following has been tested using the "software reset" which is triggered through the control port.

  • After rest all registers are reset to 0.
  • The elected register (port 4) is set to 0 and port read and write increment is not inhibited.
  • If reset is held using the "software reset" then reading or writing to the VRAM data port will cause the CPC to hang. I believe the V9990 is continuously asserting /WAIT but I can't confirm through code.
  • Reset will stop any commands in progress and will clear pending interrupts.
  • If reset is held: 3,4,7,8,9,10,11,12,13,14 and 15 reads databus, port 1 reads 0, port 5 and 6 read status,

Coordinates

  • sx,sy,dx and dy use image space coordinates. Image space width can be 256, 512, 1024 or 2048 pixels. Image space coordinates are not the same as the display coordinates and display width and height. Coordinate masking is based on image space coordinates.
  • the image space height is computed from the vram size, bits per pixel and image space width based on an equation like this:
 image space width in bytes = (image size width * image bpp)/8
 height = vram size bytes / image space width in bytes;

For a vram size of 512 this gives image space heights of &1000,&800,&400,&200,&100 and &80.

VRAM

  • In terms of physical addresses, if you consider physical address 0-&3ffff to be VRAM0 and physical address &40000-&7ffff to be VRAM1, then in P1 mode, the physical address equals logical address and in bitmap modes, every even address maps to &0-&3ffff (ever even is VRAM0) and every odd address maps to &40000-&7ffff (VRAM 1). This is described in the PDF.
  • setting a partial vram write address is ok. e.g. set r0,r1,r2 to &80000, now set r1 to 1. Write will go to 000001. Now set r2 to 2. Write will go to 000201. Now set R3 to 3. Write will go to 030201. There doesn't appear to be a write buffer.
  • v9990 seems to have a 1 byte read buffer. Setting a partial address and the reading vram will return the data from the previous address for the first read, but further reads returns correct values. This is only true of r3 and r4. e.g. set r3,r4,r5 to &80000. Now set r3 to 1. First read will come from &000000, second and subsequent reads now come from &000001. Now set r4 to 2. First read comes from &000001, second and subsequent reads come from &000201. Now set r5 to 3. First read comes from &030201, subsequent reads come from &030201.
  • If vram is set to 128KB a byte is written and then vram is set to 512KB you can read the same byte in multiple locations. The written data is effectively mirrored in the address space.

POINT

  • When reading 2bpp and 4bpp data with the POINT command in bitmap modes the pixel is moved into the topmost bits.

e.g.

if byte in vram is aabbccdd, bitmap mode and 2bpp:
 
x = 0
aaxxxxx
x = 1
bbxxxxx
x = 2 
ccxxxxx
x = 3
ddxxxxx 

The undefined 'x' appear to be from the upper byte when doing a 16-bit pixel read but more testing needs to be done to confirm this.

BMLL

  • When DIX=0, then both source and destination vram addresses are incremented. When DIX=1, then both source and destination vram addresses are decremented.
  • I can't currently see what DIY does in respect of BMLL. It seems it has no effect.

SRCH

  • When searching sx and sy are masked before use. If the width is 256, then sx is masked with 255 before use.
  • The resulting "found" x position is based on the unmasked value. i.e. if a matching pixel is at 30, and you specify sx>width, then the match is reported to be found at width+30.
  • When searching in reverse, if no match is found a X coordinate of 0x07ff will be reported.
  • When searching forwards, if no match is found then a multiple of the width is reported depending on sx. If sx<width then width is reported, if width<sx<width*2 then width*2 is reported etc. The "match" flag will not be set as expected.

BMXL

  • When the vram address is calculated from dx and dy, dx and dy are masked before use.

This means that transfer wraps within the same line and column. i.e. if drawing in reverse and overlapping x=0, then pixels will wrap to 256-x on the same line and if overlapping x=256 then pixels will wrap to 0.

LMMV

  • FC low then FC high are used to write pixels. The order is not reversed when DIX=1.

e.g. When 8bpp, fc=&1234, DIX=0 and nx=4 then after LMMV has executed we will see the bytes in vram: &34,&12,&34,&12. When 8bpp mode, fc=&1234, DIX=1 and nx=4 then after LMMB has executed we will see the bytes in vram: &12,&34,&12,&34.

  • FC bits do not correspond directly to physical VRAM but do match the pixels in each byte.

e.g. if x=0, 4bpp,fc=&1234 and nx=4 we will see in vram &34,&12. If x = 1, 4bpp, fc=&1234 and nx=4 we will see in vram &x4,&32,&1x where 'x' is the data that is already in vram.

Both of these explain why it is best that FC is filled with the same data for each pixel.

ADVN

  • The use of internal x,y coordinates and when they are updated is the same as PSET.

PSET

  • PSET seems to use an internal x and y coordinate.
  • The internal x and y coordinates are validated before use by masking them. When width is 256 then mask is 0x0ff, when 512 the mask is 0x01ff, when 1024 the mask is 0x03ff and when 2048 the mask is 0x07ff. Therefore attempting to write to x=300 on a 256 width screen actually writes to pixel x=0x02c because it is masked before use.
  • During testing I found that the 'advance' part of the PSET command differs slightly from the official document.

- The document claims that when AXE and AXM bits of the command are 0 then both DX and DY registers are read and the internal x and y are updated. I didn't find this. I found that when AXE and AXM are 0 then only the DX register is read and the internal x coordinate is updated from it. DY is not read at this time. Testing also shows this happens at the start of PSET command execution.

- If DY register is written the internal y coordinate is updated immediately. Writing to both register 38 and 39 update the internal y coordinate.

- The internal X and Y coordinates are incremented or decremented based on AXE, AXM, AYE and AYM. The internal x and y will wrap within the width and height of the display. The width is wrapped using masking. e.g. When the screen is 256 pixels wide, x is 0 and is decremented it will wrap to 255 and if x is 255 and is incremented it will wrap to 0. The same is true of the Y coordinate.

  • With the command engine, tested on PSET currently (but to be confirmed for other commands):
1) P1 mode is always 256 pixels wide, 4bpp (width (XIMM) and bpp (CLRM) settings with R6 are ignored). Therefore logical vram address of Y is defined as: (256/2)*y. 
2) P2 mode is always 512 pixels wide, 4bpp (width (XIMM) and bpp (CLRM) settings with R6 are ignored). Therefore logical vram address of Y is defined as: (512/2)*y
3) In bitmap mode width (XIMM) and bpp (CLRM) settings are used. HSCN and C25M are ignored. Therefore logical vram address of Y is defined as: XIMM/4 when 2bpp, XIMM/2 when 4bpp, XIMM when 8bpp and XIMM*2 when 16bpp.
4) For the command-engine, standby mode is identical in operation to the bitmap mode.