Video Interface

From N64brew Wiki
Jump to navigation Jump to search

The Video Interface (or VI) is one of multiple I/O interfaces in the RCP, which configures different parts of the console's video rendering and output. It provides significant flexibility to support NTSC, PAL, and M-PAL, using the same chips and registers. All memory-mapped registers are 32 bits wide and should always be written a full word (32 bits) at a time.

Video DAC

The Video DAC has a 7 bit multiplexed data bus that is used to generate the video signal from the RCP. This allows the N64 to output a 21 bit color output even thou internally it can do 24 bits. Why this lower bit was not used is not explained in any documentation found at this moment.

Video DAC Bus and waveform. Image from: http://members.optusnet.com.au/eviltim/n64rgb/n64rgb.html


The Video DAC clock runs at 4 times the speed of the internal pixel clock so the multiplexing can happen on the VI bus. This 4 clock process outputs the RGB colors and the VSync, Hsync, Clamp and Csync and is reset using a dsync reset signal. This is driven by a pixel clock provided by the MX8330 (IC U7)

Cycle 1 Cycle 2 Cycle 3 Cycle 4
D0 Red 0 Green 0 Blue 0 !Csync
D1 Red 1 Green 1 Blue 1 !Hsync
D2 Red 2 Green 2 Blue 2 !Clamp
D3 Red 3 Green 3 Blue 3 !Vsync
D4 Red 4 Green 4 Blue 4 NA
D5 Red 5 Green 5 Blue 5 NA
D6 Red 6 Green 6 Blue 6 NA
DSYNC HIGH HIGH HIGH Low

There are 3 different pixel clocks that are used in the N64 for the 3 TV standards:

TV Signal Type Pixel Clock MX8330 Input clock MX8330 FSEL input Clock Maths using datasheet
NTSC 48.62Mhz 14.3Mhz HIGH ((14.3 * 4 )* 17) / 5
PAL 48.72Mhz 17.4Mhz LOW ((17.4 * 4 )* 14) / 5
MPAL To be advised To be advised To be advised

Video Standards

The video standards send more than just pixel data both on each line and with additional lines at the top and bottom of a frame.

Hardware

Most of the Video Interface is implemented inside the RCP (Reality CoProcessor), although there is a Video DAC (Digital Analog Converter) on the mainboard, and another encoder IC (ENC-NUS) which which appears to manage some of the signal differences between Composite and S-Video Output.

Configuration Registers

Memory mapped registers are used to configure the Video Interface. The base address for these registers is 0x0440 0000, also known as VI_BASE. However, because all memory accesses in the CPU are made using virtual addresses, the following addresses must be offset appropriately. For non-cached reads/writes, add 0xA000 0000 to the address. As an example, to directly write to the VI_CTRL register, use address 0xA440 0000.

Table Notation:

R = Readable bit
W = Writable bit
U = Undefined/Unused bit
-n = Default value n at power on
<x:y> = Specifies bits x to y, inclusively

0x0440 0000 - VI_CTRL


VI_CTRL 0x0440 0000
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 RW-0
DITHER_FILTER_ENABLE
15:8 RW-0 RW-0 RW-0 RW-0 RW-0 U-0 RW-0 RW-0
PIXEL_ADVANCE<3:0> KILL_WE AA_MODE<1:0>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
TEST_MODE SERRATE VBUS_CLOCK_ENABLE DIVOT_ENABLE GAMMA_ENABLE GAMMA_DITHER_ENABLE TYPE<1:0>
bit 31-17 Undefined: Initialized to 0
bit 16 DITHER_FILTER_ENABLE: Dither Enable bit
1 = Dither filter is enabled (normally used for 16-bit color; this may cause vertical banding if anti-aliasing is disabled as seen here)
0 = Dither filter is disabled (normally used for 32-bit color)
bit 15-12 PIXEL_ADVANCE<3:0>: Use 0b0011 for most effective behavior
bit 11 KILL_WE: Diagnostics only
bit 10 Undefined: Initialized to 0
bit 9-8 AA_MODE<1:0>: Anti-Alias Mode
11 = AA and resampling disabled, replicate pixels without interpolation
10 = AA disabled, resampling enabled, and operate as if everything is covered
01 = AA enabled, resampling enabled, and only fetches extra lines as needed
00 = AA enabled, resampling enabled, and will always fetch extra lines
bit 7 TEST_MODE: Diagnostics only
bit 6 SERRATE: Normally enabled if interlacing, otherwise disabled
1 = Enabled
0 = Disabled
bit 5 VBUS_CLOCK_ENABLE: Vbus Clock Enable
1 = Enabled
0 = Disabled
    Warning: Always leave disabled! Setting this bit enables a second driver, which will output on the same pin as another driver, possibly causing physical console damage.
bit 4 DIVOT_ENABLE: Fixes minor artifacts left over from anti-aliasing (more details below)
1 = Enabled (usually used if AA is enabled)
0 = Disabled
bit 3 GAMMA_ENABLE: Fixes non-linear gamma in TV screens (more details below)
1 = Enabled
0 = Disabled
bit 2 GAMMA_DITHER_ENABLE: Adds randomized noise to the video output, in the least significant bits to remove mach banding artifacts
1 = Enabled (usually set unless banding artifacts are desired for extra effect)
0 = Disabled
bit 1-0 TYPE<1:0>: Video pixel size, also known as color bit depth
11 = 8/8/8/8 (32 bit color)
10 = 5/5/5/3 (16 bit color, technically 18 bits wide)
01 = reserved
00 = blank (no data and no sync, TV screens will either show static or nothing)

Extra Details:

DIVOT_ENABLE
When enabled, this feature fixes artifacts that the anti-aliasing algorithm leaves behind. The median color of three neighboring pixels, from any pixels on or next to silhouette edges, is selected to be displayed in place of the center pixel. Effectively removing any one pixel divots that can be seen in some fractal-based terrains. The anti-aliasing function encounters issues when multiple fragments occur on a single pixel. Since this filter is only used on edges, and not the surface of an object, texture details will not be affected. Be aware that bad quality effects can occur when the decal line rendering mode is used in conjunction with this filter, as the rendering mode generates edges that the filter can detect.
GAMMA_ENABLE
This feature is used to correct non-linear gamma found in TV screens (although this may have changed in modern TV's). To do this, the feature square roots the linear color space that the rendering pipeline uses. TV screens will raise these color values to the power of 2.2 to 2.4, which leaves a residual gamma behind of around 1.1 to 1.2. This residual value is actually preferred as a gamma slightly above 1.0 will generate more color accurate images when the TV is in darker than normal rooms. When using MPEG or JPG images, the gamma correction is included in the image data, so this feature should be turned off accordingly.

0x0440 0004 - VI_ORIGIN


VI_ORIGIN 0x0440 0004
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
ORIGIN<23:16>
15:8 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
ORIGIN<15:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
ORIGIN<7:0>
bit 31-24 Undefined: Initialized to 0
bit 23-0 ORIGIN<23:0>: RDRAM base address of the video output Frame Buffer. This can be changed as needed to implement double or triple buffering.

0x0440 0008 - VI_WIDTH


VI_WIDTH 0x0440 0008
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
15:8 U-0 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0
WIDTH<11:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
WIDTH<7:0>
bit 31-12 Undefined: Initialized to 0
bit 11-0 WIDTH<11:0>: This is the width in pixels of the frame buffer if you draw to the frame buffer based on a different width than what is given here the image will drift with each line to the left or right. The common values are 320 and 640, the maximum value is 640. The minimum value depends on the TV set, 160 would probably be a safe minimum but no guarantee. The same value would also be used on drawing commands for clipping or scissors. This can also be used with High Res interlacing modes to change the odd and even lines of the frame buffer to be drawn to screen by doubling the width of this value and changing the VI_ORIGIN register to the odd or even field being displayed.

0x0440 000C - VI_V_INTR


VI_V_INTR 0x0440 000C
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-1 RW-1
V_INTR<9:8>
7:0 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1
V_INTR<7:0>
bit 31-10 Undefined: Initialized to 0
bit 9-0 V_INTR<9:0>: When VI_V_CURRENT reaches this half-line number, a VI Interrupt is triggered. Usually set to the last line containing pixel data.
Default value of 0x3FF

0x0440 0010 - VI_V_CURRENT


VI_V_CURRENT 0x0440 0010
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_CURRENT<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_CURRENT<7:0>
bit 31-10 Undefined: Initialized to 0
bit 9-0 V_CURRENT<9:0>: The current half line, sampled once per line. Bit 0 is constant for non-interlaced modes. In interlaced modes, bit 0 gives the field number. Writing anything to this register clears the currently triggered VI Interrupt.

0x0440 0014 - VI_BURST


VI_BURST 0x0440 0014
31:24 U-0 U-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
BURST_START<9:4>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
BURST_START<3:0> VSYNC_WIDTH<3:0>
15:8 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
BURST_WIDTH<7:0>
7:0 RW-1 RW-1 RW-0 RW-1 RW-0 RW-0 RW-0 RW-1
HSYNC_WIDTH<7:0>
bit 31-30 Undefined: Initialized to 0
bit 29-20 BURST_START<9:0>: Start of color burst in pixels from hsync
bit 19-16 VSYNC_WIDTH<3:0>: Vertical sync width in half lines
bit 15-8 BURST_WIDTH<7:0>: Color burst width in pixels
bit 7-0 HSYNC_WIDTH<7:0>: Horizontal sync width in pixels
Default value of 0x01

Examples:

NTSC @ any resolution is 0x03E52239
  • horizontal sync width in pixels: 57 (decimal)
  • color burst width in pixels: 34 (decimal)
  • vertical sync width in half lines: 5 (decimal)
  • start of color burst in pixels from h-sync: 62 (decimal)
PAL @ any resolution is 0x0404233A
  • horizontal sync width in pixels: 58 (decimal)
  • color burst width in pixels: 35 (decimal)
  • vertical sync width in half lines: 4 (decimal)
  • start of color burst in pixels from h-sync: 64 (decimal)

0x0440 0018 - VI_V_SYNC


VI_V_SYNC 0x0440 0018
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_SYNC<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_SYNC<7:0>
bit 31-10 Undefined: Initialized to 0
bit 9-0 V_SYNC<9:0>: Total visible and non-visible lines. This should match either NTSC (non-interlaced: 0x20D, interlaced: 0x20C) or PAL (non-interlaced: 0x271, interlaced: 0x270)

0x0440 001C - VI_H_SYNC


VI_H_SYNC 0x0440 001C
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0 RW-0
LEAP<4:0>
15:8 U-0 U-0 U-0 U-0 RW-1 RW-1 RW-1 RW-1
H_SYNC<11:8>
7:0 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1 RW-1
H_SYNC<7:0>
bit 31-21 Undefined: Initialized to 0
bit 20-16 LEAP<4:0>: 5-bit leap pattern used only for PAL. Should always use standard value of 0x15
bit 15-12 Undefined: Initialized to 0
bit 11-0 H_SYNC<11:0>: Total width of a line, in 1/4 pixel units. Should always use standard values: NTSC (0xC15) or PAL (0xC69)
Default value of 0x7FF

0x0440 0020 - VI_H_SYNC_LEAP


VI_H_SYNC_LEAP 0x0440 0020
31:24 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
LEAP_A<9:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
LEAP_A<7:0>
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
LEAP_B<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
LEAP_B<7:0>
bit 31-26 Undefined: Initialized to 0
bit 25-16 LEAP_A<9:0>: NTSC: Identical to H_SYNC width. PAL: 0xC6E
bit 15-10 Undefined: Initialized to 0
bit 9-0 LEAP_B<9:0>: NTSC: Identical to H_SYNC width. PAL: 0xC6F

0x0440 0024 - VI_H_VIDEO


VI_H_VIDEO 0x0440 0024
31:24 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
H_START<9:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
H_START<7:0>
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
H_END<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
H_END<7:0>
bit 31-26 Undefined: Initialized to 0
bit 25-16 H_START<9:0>: Start of the active video image, in screen pixels. Typical values: NTSC (0x06C) or PAL (0x080)
bit 15-10 Undefined: Initialized to 0
bit 9-0 H_END<9:0>: End of the active video image, in screen pixels from hsync. Typical values: NTSC (0x2EC) or PAL (0x300)

0x0440 0028 - VI_V_VIDEO


VI_V_VIDEO 0x0440 0028
31:24 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_START<9:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_START<7:0>
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_END<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_END<7:0>
bit 31-26 Undefined: Initialized to 0
bit 25-16 V_START<9:0>: Start of the active video image, in screen half-lines. Typical values: NTSC (0x025) or PAL (0x05F)
bit 15-10 Undefined: Initialized to 0
bit 9-0 V_END<9:0>: End of the active video image, in screen half-lines from vsync. Typical values: NTSC (0x1FF) or PAL (0x239)

0x0440 002C - VI_V_BURST


VI_V_BURST 0x0440 002C
31:24 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_BURST_START<9:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_BURST_START<7:0>
15:8 U-0 U-0 U-0 U-0 U-0 U-0 RW-0 RW-0
V_BURST_END<9:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
V_BURST_END<7:0>
bit 31-26 Undefined: Initialized to 0
bit 25-16 V_BURST_START<9:0>: Start of the color burst enable, in half-lines. Typical values: NTSC (0x00E) or PAL (0x009)
bit 15-10 Undefined: Initialized to 0
bit 9-0 V_BURST_END<9:0>: End of the color burst enable, in half-lines. Typical values: NTSC (0x204) or PAL (0x26B)

0x0440 0030 - VI_X_SCALE


VI_X_SCALE 0x0440 0030
31:24 U-0 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0
X_OFFSET<11:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
X_OFFSET<7:0>
15:8 U-0 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0
X_SCALE<11:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
X_SCALE<7:0>
bit 31-28 Undefined: Initialized to 0
bit 27-16 X_OFFSET<9:0>: Horizontal subpixel offset (2.10 format)
bit 15-12 Undefined: Initialized to 0
bit 11-0 X_SCALE<9:0>: 1/horizontal scale up factor (2.10 format)

0x0440 0034 - VI_Y_SCALE


VI_Y_SCALE 0x0440 0034
31:24 U-0 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0
Y_OFFSET<11:8>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
Y_OFFSET<7:0>
15:8 U-0 U-0 U-0 U-0 RW-0 RW-0 RW-0 RW-0
Y_SCALE<11:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
Y_SCALE<7:0>
bit 31-28 Undefined: Initialized to 0
bit 27-16 Y_OFFSET<9:0>: Vertical subpixel offset (2.10 format)
bit 15-12 Undefined: Initialized to 0
bit 11-0 Y_SCALE<9:0>: 1/vertical scale up factor (2.10 format)

0x0440 0038 - VI_TEST_ADDR


VI_TEST_ADDR 0x0440 0038
31:24 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
23:16 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
15:8 U-0 U-0 U-0 U-0 U-0 U-0 U-0 U-0
7:0 U-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
TEST_ADDR<6:0>
bit 31-7 Undefined: Initialized to 0
bit 6-0 TEST_ADDR<6:0>: Diagnostics only, usage unknown

0x0440 003C - VI_STAGED_DATA


VI_STAGED_DATA 0x0440 003C
31:24 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
STAGED_DATA<31:24>
23:16 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
STAGED_DATA<23:16>
15:8 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
STAGED_DATA<15:8>
7:0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0 RW-0
STAGED_DATA<7:0>
bit 31-0 STAGED_DATA<31:0>: Diagnostics only, usage unknown

How to use this information

Interlace Mode

The NTSC (and PAL) standard support interlace mode which is commonly associated with high resolution, but it can be used for more than that.

  • High Resolution Mode
  • Improve the visible detail of the image
  • 60 frames per second in low resolution

High Resolution Mode

High resolution mode supports up to 480 lines (NTSC) while low resolution is 240 lines (NTSC). The Image doesn't magically grow or shrink because first the even lines are drawn on the screen, then it goes back to the top and draws the odd lines. If your game only draws the even lines then on a larger display you may have the image scanlines with smaller black lines visible between them.

In order to implement this feature it requires the VI_V_START_REG to be modified on every VI Interrupt, so that it outputs even lines then odd lines as needed.

NTSC Alternates between: 0x002301fd and 0x002501ff

PAL Alternates between: 0x005f0239 and 0x005d0237

Once this is explained I believe it will be fixed soon, so this is explained as an example of what the difference can be. The libdragon homebrew library doesn't actually support High Resolution Mode, because it doesn't implement this register value change. To be fair this is very easy to overlook, it works fine in every emulator and would at least look OK on a console. The difference is that emulators present the framebuffer memory as a single block of data. While the VI Interface and Video DAC see the 1 framebuffer as even lines top to bottom, then odd lines top to bottom.

Learnt from Factor 5 games(Mazamars312):

The VI_DRAM_ADDR_REG address is set to the odd or even line of the framebuffer and the VI_H_WIDTH_REG value is doubled to help skip to the next Odd or Even field line for the VI core to process.

Once the odd or even field has been displayed the VI_DRAM_ADDR_REG is updated to the other field's address. Also the VI_Y_SCALE_REG.Subpixel is changed between fields with the values 12'h0100 and 12'h0200 to help the scaling and AA calculations (Need to find out which one is Odd and Even based as this could be game based)

The real width and height values are calculated by the following calculations

Width: C programming(float) ((VI_H_START_REG.END - VI_H_START_REG.START) * (VI_X_SCALE_REG.ScaleUp / 1024))

Height: C programming(float) (((VI_H_START_REG.END - VI_H_START_REG.START) >> 1) * (VI_X_SCALE_REG.ScaleUp / 1024))

Improve visible detail in low resolution

60 Frames per second in Low Resolution mode

This is the easiest mode to use if your frame processing time is very low, because you simply swap the frame buffer 60 times per second inside the VI Interrupt, no other register changes are needed.

Letter Boxing

This is a fairly common effect that is nice for cut scenes or to indicate overworld vs a level.

VI_V_START_REG

Pillar Boxing

This feature is almost the default now since the N64 is intended for a 4:3 screen but is commonly played on 16:9 ratio screens.

VI_H_START_REG

Reduce both Height and Width

Reducing the display size by just a few pixels also reduces the size of the world view that the player has, while usually improving performance. Especially if the purpose of this is to improve performance I recommend doing it in increments of 8 pixels, for example either 4 or 8 pixels off each side and my increasing the size of the player status bars at either the top or bottom of the screen can also reduce the number of objects to draw on the screen.

Use the same techniques mentioned above for Letter Boxing and Pillar Boxing.

Advanced version of this is to reduce either the height or width and to increase the scaling so it still fits the screen but stretches the image out to fill the screen.