The ULA is the Spectrum video and input chip. The Enhanced ULA adds extra video modes and features to the standard Spectrum.
ULA stands for "Uncommitted Logic Array" and refers to the manufacturing process involved. It does not relate to the video behavior of the chip. The chip itself is a custom chip.
Spectrum Video Mode
The standard Spectrum has only one video mode, with a resolution of 256x192 (not including the border) and 15 colours. This screen is further divided into 8x8 "attribute cells", so the attribute cell resolution is 32x24.
The ULA reads memory from 16k-Bank 5 or 16k-Bank 7 based on the setting at Memory Paging Control ($7FFD / ). If you haven't done any fancy messing about with the memory map, or you're using Spectrum 48k mode, then the screen memory will start at $4000.
There is one bit per pixel, so each byte represents 8 pixels (ie, one line of an attribute cell). Each screen line is stored in straightforward left-to-right order, so the 32 bytes from $4000 onwards represent all the pixels on the first line of the screen. Each 1 bit means the pixel is drawn in the "ink" color; a 0 bit means it is drawn in the "paper" color. Which colors these are is specified in the attribute blocks described below.
Unfortunately, while the columns of the screen are in straightforward order, the lines are not. The 192 line screen is vertically divided into thirds, with each third containing 64 lines (or 8 attribute cells). These three thirds are stored sequentially in memory, but within each third, the order is not what you'd expect. You'd expect it'd store the first line of the first cell, the second line of the first cell.. up to the eighth line of the first cell, then the first line of the second cell. Instead, the sorting is inverted. So it stores the first line of the first cell, then the first line of the second cell, then the first line of the third cell, and so on.. up to the first line of the eighth cell, which is then followed by the second line of the first cell!
This means that getting the address of the start of a given line can be a tricky business. The Spectrum Next provides the PIXELAD opcode to automatically perform the necessary transition. It can be done manually by breaking the line number (0-191) into bits as described below.
Pixel data is stored with 1-bit color in the pixel data memory. However, it is possible for the color to vary across the screen. The snag is that you can set only two colours within a single attribute cell - that is, an 8x8 area. Because you can have only one color value per 8x8 cell, some older games exhibited "color clash" in which an object or part of an object would appear the wrong color because it needed to be drawn inside an 8x8 cell with another object of a different color.
Color data is stored at $5800 onwards. Unlike the pixel data, it's stored in straightforward reading order, with one byte per attribute cell, for a total of 768 bytes. The byte represents:
|0-2||"Ink" color (color of 1 bits). From 0-7: black, blue, red, magenta, green, cyan, yellow, white.|
|3-5||"Paper" color (color of 0 bits).|
|6||Bright flag (toggles brighter version of both colors).|
|7||Flash flag (toggles regular alternation of ink and paper colors)|
If Enhanced ULA is enabled, then this no longer applies; see below.
Calculating line addresses in interleaved video modes
The line number can be broken down into bits as follows:
Each 3rd is 64 lines, so the top two bits of the line number indicate which 3rd we're in, with the highest being %10 for 128 (%11 would be 192, off the bottom of the screen). Then, each cell is 8 lines, so bits 3-5 of the line number give the cell address, and the remaining bits give the line within that cell.
The thirds are stored in sequential order; each third contains 64 lines; and storing a line takes 32 bytes. This means each 3rd takes 2048 bytes, so the "which 3rd" number is multiplied by 2048. Add $4000 for the start of the screen and that'll give the address of the start of the target third. Now we need to get to the block for the correct line number. To store the top line of a single row of cells takes 32 bytes; but since all the top lines for 8 cells are stored together, there's actually 256 bytes (32*8) between lines. So the line number must be multiplied by 256 and added to the address. Then, to select the correct cell within that block, the cell number needs to be multiplied by 32 (because each line takes 32 bytes). So the final 16-bit address is:
The five lowest bits of this value are then used to select between the 32 horizontal cells on that line, which is simply done by dividing the pixel address by 8.
Enhanced video modes
The Next adds a number of additional video modes to the standard ULA.
Timex Sinclair Double Buffering
Writing %001 to the lower 3 bits of Timex Sinclair Video Mode Control ($xxFF / ) will change the accessed base screen address to $6000 instead of $4000 (or otherwise add $2000 to the accessed true address, moving it into the second half of the appropriate 16k-bank or the next 8k-bank). Writing %000 restores the normal base screen address. This allows this area of memory to be used for a "double buffer" for smooth frame transitions. Note however that the Spectrum 128k's bank switching also allows double buffering (using Bank 7, see Memory map), but only first half of bank 7 is mirrored inside the FPGA in the fast BRAM region. The Layer 2 can be used in parallel only when ULA layer data are coming from BRAM memory in the FPGA (the mirror of first half of Bank 7 is new feature of latest cores 3.0.x, in 2.0 cores using the ZX128 shadow screen caused the Layer 2 to switch off).
Timex Sinclair Hi-Res Mode
Writing %110 to the lower 3 bits of Timex Sinclair Video Mode Control ($xxFF / ) will change the screen resolution to 512x192, doubling the X resolution. This still uses the Double Buffer structure of one set of screen data at $4000 and another at $6000, but now columns (one column = one byte = 8 pixels) are alternately read from the two buffers to account for the higher resolution. In addition, attributes are disabled: this mode supports only two colors. Which they are can be set by writing bits 3-5 of Timex Sinclair Video Mode Control ($xxFF / ).
Timex Sinclair Hi-Color Mode
Writing %010 to the lower 3 bits of Timex Sinclair Video Mode Control ($xxFF / ) will disable the standard attribute area. Instead, the attribute area is set to begin at $6000 and to have the same interleaved structure as the pixel data. Each attribute byte is now applied to only one line of each cell, rather than the whole cell. The storage structure matches that of the pixel data (including the weird interleaving). This means that the areas of color clash are now limited to 8x1 blocks instead of 8x8 blocks.
LoRes Layer/Radasjimian Mode
LoRes Layer is a mode similar to the "Radastanian Mode" on the ZXUno, in that it offers reduced resolution in exchange for more colors. LoRes Layer allows any of 256 colors anywhere on the screen, but lowers the resolution in both dimensions to 128x96.
LoRes layer is enabled by setting bit 7 in Sprite Control Register ($15). Each pixel is assigned one byte, in reading order (without interleaving). The first 48 lines are stored between $4000 and $5800, and the second 48 between $6000 and $7800. Each byte is an index into the ULA palette.
Setting bit 0 of Enhanced ULA Control Register ($43) enables the Enhanced ULA mode. In this mode, the breakdown of Ink/Paper/FLASH/BRIGHT bits in the attribute cells given above is ignored. Instead, the bit value written to Enhanced ULA Ink Color Mask ($42) is used as a bitmask to determine which bits are used for the ink color palette index (it must mask off a number of bits on the LSB side, eg %00111111, so not %01010101 or anything silly like that!) and the remaining bits, with 128 added, are used for the paper color palette index. The mask %11111111, to treat all bits as ink, is legal; if used the paper color in every cell will be Transparency colour fallback Register ($4A) (since core 3.0, it was color index 128 in 2.0 core). The mask %00000000 to treat all bits as paper is not legal.