-
Notifications
You must be signed in to change notification settings - Fork 60
(VERA 0.8) The Composer
This page refers to the VERA 0.8 and needs to be updated for emulator versions r37 and later.
VRAM Address | Description |
---|---|
$0F0000 | Output mode and chroma toggle |
$0F0001 | Horizontal scale (HSCALE) |
$0F0002 | Vertical scale (VSCALE) |
$0F0003 | Border color palette index |
$0F0004 | Horizontal start of display area, bits 0-7 |
$0F0005 | Horizontal end of display area, bits 0-7 |
$0F0006 | Vertical start of display area, bits 0-7 |
$0F0007 | Vertical end of display area, bits 0-7 |
$0F0008 | Horizontal and vertical display area, various high bits |
$0F0009 | Scanline IRQ timing, bits 0-7 |
$0F000A | Scanline IRQ timing, bit 8 |
The composer is responsible for outputting the results of all other video settings and memory to the display. It controls the video signal, coloration, image scaling, and display borders.
The display mode and color are set via $0F0000. It follows the format %C0000BAA:
Field | Description |
---|---|
AA | Display mode. See the table "Display Modes", below. |
B | Chroma disable bit. If set, the NTSC composite mode will output a monochrome signal, which will improve image quality on monochrome displays. |
C | Current NTSC field. This is a read-only bit. |
Mode | Description |
---|---|
0 | Video disabled |
1 | VGA mode |
2 | NTSC composite mode |
3 | RGB interlaced, composite sync (via VGA output) |
The NTSC standard is an interlaced video signal, meaning it alternates between drawing the even-numbered lines ("field 0") of the display and the odd-numbered lines ("field 1"). This mean it's technically only drawing half of the lines each frame, even though it's operating at 60Hz. NTSC modes allow for clever color logic that can mimick transparency effects as well as create colors not otherwise possible for the display signal. Reading this bit, then, can be a signal to flip certain tiles or toggle sprites to create transparency effects or otherwise create more subtle color gradations than the VERA would otherwise be capable of in progressive display mode.
The Composer treats a horizontal or vertical scale of 128 to mean "1x". This outputs square pixels at a 640x480 resolution. The emulator (and perhaps the final hardware as well) is only capable of scaling the display by nearest-neighbor approximation. It cannot output, for instance, 5:4 pixels at a SNES-like 512x448 resolution; the display is 640x480, and the scale parameters only influence the which pixel from the internal 640x480 logic that the composer maps to an output 640x480 pixel.
The easiest way to work with the scale parameters is to use factors of 2 to cleanly multiply the size of pixels by a factor of 2:
HSCALE | Size factor | Width |
---|---|---|
128 | 1x | 640 |
64 | 2x | 320 |
32 | 4x | 160 |
16 | 8x | 80 |
VSCALE | Size factor | Height |
---|---|---|
128 | 1x | 480 |
64 | 2x | 240 |
32 | 4x | 120 |
16 | 8x | 60 |
For a more advanced treatment of scale, think of some "screen position" X and Y, where X and Y are each fixed-point binary numbers; let's also say they're each 16 bits wide, with an 11-bit significand followed by a 7-bit mantissa. This means a value of "1.0" would be represented as $0080, which in decimal is 128; "0.5" is represented at $0040, or 64; "0.25" is $0020, or 32; and so on. The highest value this could represent is 1023.9921875, which is more than we need to represent 640 pixels.
As the composer outputs each pixel of a given line to the display, it increments its "screen position" X by the HSCALE. When deciding which pixel to output next, simply ignores the mantissa. At the end of the line, it similarly increments its "screen position" Y by VSCALE, and ignores the mantissa of the result when choosing which line to output next.
The display area is where the VERA will output image data, starting with the coordinates (0, 0) in the upper-left of the display area, and ending at whatever (X,Y) is in the bottom-right corner of the display area. In effect, the composer pretends that anything outside of the display area no longer exists as part of the display, asides from filling in the space with whatever color is at the palette index specified in the "border color palette index" address, $0F0003.
The actual display area box is defined as 4 fields which are spread across 5 VRAM addresses:
Field | Size in bits | Description |
---|---|---|
HSTART | 10 | The horizontal screen coordinate where the display area should start |
HSTOP | 10 | The horizontal screen coordinate where the display area should stop |
VSTART | 9 | The vertical screen coordinate where the display area should start |
VSTOP | 9 | The vertical screen coordinate where the display area should stop |
The least significant byte (e.g. the lowest byte) of each of these values is contained in the VRAM addresses $0F0004-$0F0007, respectively. That leaves $0F0008, which is special. It follows the bit format %00DCBBAA:
Field | Description |
---|---|
AA | 2 most significant bits of HSTART |
BB | 2 most significant bits of HSTOP |
C | Most significant bit of VSTART |
D | Most significant bit of VSTOP |
The video signal is always 640x480, regardless of any other settings, so if you wanted a display border that was 10 pixels wide on all edges, you would set the HSTART to 10, HSTOP to 630 (e.g. "640-10"), VSTART to 10, and VSTOP to 470 ("480-10").
That's $00A, $276, $00A, $1D6, respectively, so you would write $0A, $76, $0A, and $D6 to VRAM addresses $0F0004-$0F0007. That leaves $0F0008 with its special format, which becomes %00101000, or $28, specifying all the most-significant bits that didn't fit into the other addresses.
Try this example:
10 VPOKE $0F,$04,$0A
20 VPOKE $0F,$05,$76
30 VPOKE $0F,$06,$0A
40 VPOKE $0F,$07,$D6
50 VPOKE $0F,$08,$28
; Configure $9F23 to $0F0004, with an address auto-increment of 1
lda #$04
sta $9F20
stz $9F21
lda #$1F
sta $9F22
; Setup box
lda #$0A
sta $9F23
lda #$76
sta $9F23
lda #$0A
sta $9F23
lda #$D6
sta $9F23
lda #$28
sta $9F23
The display area is then the 620x460 pixel area not taken up by the border. A sprite placed at (0, 0) will be completely visible, located at the top-left corner of the display area and not overlapping with the border. If the sprite were moved left one pixel, it would become truncated because nothing is shown on top of the system border. The tilemaps also respect this. As previously stated, asides from having a system border color, this is analogous to reducing the display size to a bespoke resolution, and pretending that the border area no longer exists.
For an introduction to interrupts, see the wiki entry on ASM programming and interrupts. BASIC programs are generally too slow to make use of line interrupts in a meaningful way.
In addition to the VBLANK interrupt that can be checked by reading $9F27 in the system memory, the VERA can also fire an interrupt when the composer reaches a specified display line.
To specify the line, simply write the line number in little-endian order to $0F0009 and $0F000A (the low byte to $0F0009, the high byte to $0F000A). Upon reaching the specified line, the VERA will trigger an interrupt. You can distinguish a line interrupt from the VERA by checking $9F27 in system memory for $02. Be sure to clear the bit before your interrupt handler exits!