VI Registers Detailed

From en64 wiki
Jump to navigation Jump to search

Most of this was from Lac's hw-dox.

  The video interface performs the following functions.

  1. Creates signal timing when outputting to television monitor.
  2. Specifies resolution and depth of color frame buffer.
  3. Transfers from color frame buffer to video DAC (D-A converter) and specifies filters used during transfers.
  4. Provides current display position information.

Note: The video DAC creates analog data from digital data and outputs said data as a television video image.


The video hardware is initialized by simply writing all the necessary values to the vi regs. I'm only going to discuss one mode here, but you can easily find the values for other modes by just printing out reg values to the screen after initiating your favorite mode with libultra. You can also alter values of course to make your own modes. That I will not discuss here. The one i'm discussing is the simple 320x240 RGBA 16bit non-antialiasing mode.

     The base address of the VI regs are mapped at 0xa4400000, so to simply
     write a value to a reg in r4300 asm would be like this:
       ;;this is just an example but it happens to be the write to the
       ;;VI_H_WIDTH_REG defined in RCP.H
       lui      at,0xa440     ;at=0xa4400000
       li       t0,0x140      ;t0=0x140
       sw       t0,0x8(at)    ;write t0 to reg at $at+0x8 

      in C:

       IO_WRITE(VI_H_WIDTH_REG,0x140);  //IO_WRITE and IO_READ are in r4300.h

      Ok to initialize this mode here are the values to write for each reg:

  -> (VI_CONTROL_REG, 0x0000320e)
  -> (VI_H_WIDTH_REG,0x140)
  -> (VI_V_INTR_REG,0x2)
  -> (VI_TIMING_REG,0x03e52239) 
  -> (VI_V_SYNC_REG,0x0000020d)
  -> (VI_H_SYNC_REG,0x00000c15)
  -> (VI_H_SYNC_LEAP_REG,0x0c150c15)
  -> (VI_H_VIDEO_REG,0x006c02ec)
  -> (VI_V_VIDEO_REG,0x002501ff)
  -> (VI_V_BURST_REG,0x000e0204)
  -> (VI_X_SCALE_REG,0x200);
  -> (VI_Y_SCALE_REG,0x01000400)

All the values are pretty obvious and don't need much explaining. And with a little hacking you can come up with your own modes. If you've managed to get this far you know that the video screen is just showing zebra stripes or some garbage. The reason for this is you have not set the frame buffer for this video mode. This is what osViSwapBuffer() is for (if you have used libultra). To set your frame buffer simply write the rdram address of the buffer to VI_DRAM_ADDR_REG or just change the init code to have it set it up. Now you can simply write your graphics to the buffer and they'll be updated.
NOTE: There is A LOT of other things you can twek, change, by messing with the VI regs... mess around... figure it out. If you are getting strange corrupted data make sure you are invalidating cache lines etc... (read the r4300i docs on the cache). If you are confused about physical and virtual addresses or how the cache works, read the r4300 man... and if you still don't understand it totally, join the club.

Vertical Retrace Wait

     (Cheap Way) normally you would wait on the interrupt.
     The VI interrupt occurs on every v_blank
     while ( VI_CURRENT_REG != 512 )  {wait}  

VI Registers (detailed)

The VI registers can be found at the memory address 0x04400000. I will append their location in accordance to their start point will in the headers. All registers are listed with their mnemonic first, followed by their offset from the video interface base address.
_- Register bit patterns and operational description -_


Register # 0

        REGISTER #0 VI_CONTROL_REG [16:0]
        [1:0]   pixel_size
                  0: blank (no data, no sync)
                  1: reserved
                  10: 5/5/5/3 ("16" bit - really 18 bit)
                  11: 8/8/8/8 (32 bit)
        [2]     gamma_dither_enable (normally on, unless "special effect") //No effect if Gamma Boost is unset
        [3]     gamma_enable (normally on, unless MPEG/JPEG) //Gamma Boost
        [4]     divot_enable (normally on if antialiased, unless decal lines) //Usually used in conjunction with anti-aliasing
        [5]     vbus_clock_enable (off always)
        [6]     serrate (always on if interlaced, off if not) //Set if running in interlaced mode
        [7]     test_mode (for diagnostics, not used in normal operation) //Always zero
        [9:8]   aa_mode[1:0] (anti-alias mode) 
          0: aa & resamp (always fetch extra lines) //Anti-aliasing and resampling, always fetch extra lines
          1: aa & resamp (fetch extra lines if needed) //Same as above, fetch when needed
          2: resamp only (treat as all fully covered) //Resampling only
          3: neither (replicate pixels, no interpolate) //Neither (replicate pixels, no interpolation)
        [10]    Undefined //Always zero
        [11]    kill_we (for diagnostics, not used in normal operation) //Always zero
        [15:12] pixel_advance (always 3 for optimal operation) //Typical observed value is 0x3
        [16]    dither_filter_enable (normally on for 16 bit, off for 32 bit) //Enable dither filter

The gamma dither and gamma boost bits are somewhat curious. It is recommended to leave these on, but a better picture color wise can be produced when disabling them. Many games leave the gamma boost bit off as disabling it gives smoother gradient of colors, and enabling gives a washed out image. NOTE: anti=aliasing must be enabled when running in 320x240 16bpp mode. It will corrupt the video otherwise.



        [23:0]  Frame buffer offset

VI_DRAM_ADDR_REG allows a frame buffer in memory to be assigned as the current used frame buffer. This allows a programmer to design a single, double or even triple buffered system without relying on hardware. This should be updated when the system is in vertical bank to swap buffers for the smoothest possible video update. Frame buffers can be anywhere in memory, but it's a good idea to have them aligned on a boundary that makes it possible to DMA to them. Also, be sure to pass the uncached memory address to the VI by or'ing with the bit mask 0xA0000000 (MIPS kseg1) or you will have to manually invalidate the cache before swapping buffers.



        [11:0]  Frame buffer line width in pixels

VI_H_WIDTH_REG controls the horizontal width in pixels of the frame buffer. Typical values are 0x0140 for 320x240 video, or 0x0280 for 640x480 video. This value, combined with the horizontal and vertical scaling, allows for the video mode width and height to be set.



        [9:0]   Interrupt when current half-line = V_INTR

VI_V_INTR_REG works in combination with the MI to assert an interrupt when the video hardware hits a certain line (usually used to set up a vblank interrupt). Don't forget to set the associated mask bits in the MI or you will not get an interrupt. To clear the interrupt once it is asserted, write to the VI_V_CURRENT_LINE_REG.


"current half line, sampled once per line (the lsb of V_CURRENT is constant within a field, and interlaced modes gives the field number - which is constant for non-interlaced modes) - Any write to this register will clear interrupt line"

        [9:0]   Current half line, sampled once per line (the lsb of v_current is constant within a field, and in interlaced modes gives the field number - this is 
                constant for non-interlaced modes). Writing to this will clear the interrupt line.

VI_V_CURRENT_LINE_REG is updated once per vertical line to reflect the current line being displayed. In interlaced mode, the least significant bit will be constant for the current field. A write to this register does not change the vertical line currently being displayed. Instead, it clears any currently asserted interrupt caused by VI_V_INTR_REG.



        [29:20] burst_start (Start of color burst in pixels from hsync) //Specifies the color burst start as an offset in pixels from the horizontal sync.
        [19:16] vsync_width (Width if vsync in half-lines) //Specifies the vertical sync height in pixels.
        [15:8]  burst_width (Width of color burst in pixels) //Specifies the color burst width in pixels
        [7:0]   hsync_width (Width of hsync in pixels) //Specifies the horizontal sync width in pixels.

VI_TIMING_REG controls several timing aspects of the video signal.

VI_V_SYNC_REG (0x18)


        [9:0]   Number of half-lines per field

VI_V_SYNC_REG controls the number of lines per frame (or the number of half-lines per field). Values are typically taken straight from the NTSC or PAL specifications. For NTSC, this is 0x20D (525 lines). For PAL, this is 0x271 (625 lines).



        [20:16] 5-bit pattern used for PAL video only (Leap pattern)
        [11:0]  h_sync_period (the total duration of a line in 1/4 pixels)



        [27:16] Identical to h_sync_period (except for PAL) //Duplicate of data represented in 11:0 of VI_H_SYNC_LEAP_REG
        [11:0]  hsync_leap_b  (identical to h_sync_period (except for PAL)) //Same as above.



        [25:16] h_video_start (start of active video in screen pixels) //Start of active video measured in pixels.
        [9:0]   h_video_end   (end of active video in pixels from hsync) //End of active video measured in pixels.

VI_H_VIDEO_REG is split into two fields that specify the start and end of the active horizontal video in pixels. The difference between these values is normally 640 pixels.



        [25:16] v_video_start  (start of active video in screen half-lines) 
        [9:0]   v_video_end    (end of active video in half-lines from vsync)

VI_V_VIDEO_REG specifies the start and end of the active vertical video in lines. The difference between these values is normally 474 lines.



        [25:16] v_burst_start  (start of color burst enable in lines)
        [9:0]   v_burst_end    (end of color burst enable in lines)

VI_V_BURST_REG specifies the start and end of the color burst in vertical lines. This in combination with VI_TIMING_REG allows for full control over video timing.



        [27:16] x_offset       (horizontal subpixel offset (2.10 format) //Horizontal subpixel offset.
        [11:0]  x_scale        (1/horizontal scale up factor (2.10 format) //Reciprocal of the horizontal scale value.

VI_X_SCALE_REG controls the horizontal scaling up factor between the frame buffer and the final image as displayed on the screen. It also controls the horizontal subpixel offset. Both values are specified as 2.10 fixed point integers.



        [27:16] y_offset        (vertical subpixel offset (2.10 format)) //Vertical subpixel offset.
        [11:0]  y_scale         (1/vertical scale up factor (2.10 format)) //Reciprocal of the vertical scale value.

VI_Y_SCALE_REG controls the horizontal scaling up factor between the frame buffer and the final image as displayed on the screen. It also controls the vertical subpixel offset. Both values are specified as 2.10 fixed point integers.



        [6:0]   test_addr       (diagnostic use only)



        [31:0]  test_data       (diagnostic use only)

NOTE: remember that all numerical values for the regs described above start with the first value being 0!

    _- Register Initial value -_

       All registers have initial value when the n64 is turned on except for these:

       VI_V_INTR_REG  =  0x3FF
       VI_H_SYNC_REG  =  0xD1
       VI_H_SYNC_REG  =  0xD2047

Setting up a Frame Buffer

The proper way to setup a frame buffer on the Nintendo 64 is simple. Most register values can be simply copied from the main VI register page as they relate to the actual generation of the video signal and not software parameters relating to a buffer in memory. The VI_DRAM_ADDR_REG register is normally left set to zero while setting other registers to avoid flicker or garbage appearing on the screen. The Nintendo 64 will output video at 640x480 regardless of the frame buffer width or height. The VI_X_SCALE_REG and VI_Y_SCALE_REG registers allow one to scale an arbitrary buffer in memory to fit in the 640x480 region. The VI_H_WIDTH_REG register allows the frame buffer to be set to arbitrary width and only serves to allow the Nintendo 64 to determine where a particular line ends in the frame buffer.

Curiously, the N64 treats the vertical component as half-lines and as such scaling factor of 1X will set the vertical resolution to 240 pixels. A horizontal scaling factor of 1X will set the horizontal resolution to 640 pixels as expected. The horizontal and vertical scaling factors can be set independent of the VI_H_WIDTH_REG register. No vertical component is specified for the frame buffer as the N64 hardware will use as many lines in the frame buffer as are needed to render the image to the screen. Double or triple buffering is archived in software by changing the VI_DRAM_ADDR_REG register while the vertical retrace period. An interrupt is generated on the half-line specified by the VI_V_INTR_REG register. This means that even on interlaced televisions, an interrupt will only happen once the entire scene has been drawn (even and odd lines). It is recommended to pass the VI_DRAM_ADDR_REG register the uncached memory address of the frame buffer as this prevents artifacts due to caching problems. When using the RDP to render a scene, the Color Image Buffer is set to these addresses.