Official ULAplus™ Specification
A downloadable project
The ULAplus™ specification describes an enhanced ULA for the Sinclair ZX Spectrum family of computers. It can be implemented as a plug-in replacement for the ULA such as SLAM+, an external interface such as ZXHD or MB03+, in emulators, or in modern clones such as the ZX-Uno. It is designed for maximum compatibility with existing software. This information supersedes all existing versions of the specification, including those with later version numbers.
The latest official version of the specification is version 1.1d.
Revision History
Version 1.1e
Converted specification to markdown and transferred ownership to ZX Design and Media.
Version 1.1d
Restored missing information about palettes in Timex hi-res mode.
Version 1.1c
Revised to clarify that unused modes and groups are reserved for future use.
Version 1.1b
Revised to clarify the values returned when reading from the ports. Added quick palette set example.
Version 1.1a
Revised for the release of Fuse 1.2, incorporating changes from Mark Smith's ULAplus™ implementation. This version deprecates grayscale mode and adds the Timex screen mode control register to the SZX palette block.
Version 1.1
Revised for the release of ZXDS 1.3, incorporating changes from the OpenCores ULAplus™ implementation. This version rationalizes the specification. Software written for the standard mode of the new specification will run unmodified on earlier implementations.
- Includes the Timex video modes (optional).
- Changes the preferred way of multiplexing the 2-bit blue value to obtain a 3-bit blue value.
- Adds 256 grayscale support (optional).
- Deprecated HSL and CMYK support.
Version 1.0
First published version of the specification.
I/O ports
ULAplus™ is controlled by two ports. Port BF3B is the register port (write only). The byte output is interpreted as follows:
- Bits
0-5
: Select the register sub-group - Bits
6-7
: Select the register group.
Two groups are available:
00
- Palette Group. When this group is selected, the sub-group determines the entry in the palette table (0-63
).01
- Mode Group. The sub-group is (optionally) used to mirror the video functionality of Timex port FF and the framebuffer select of port 7FFD as follows:- bits
0-2
: Screen Mode000
: screen 0 (bank 5)001
: screen 1 (bank 5)010
: hi-color (bank 5)110
: hi-res (bank 5)100
: screen 0 (bank 7)101
: screen 1 (bank 7)011
: hi-color (bank 7)111
: hi-res (bank 7)
- bits
3-5
: Screen Color in Hi-Res Mode (foreground/background)000
: 0 / 7001
: 1 / 6010
: 2 / 5011
: 3 / 4100
: 4 / 3101
: 5 / 2110
: 6 / 1111
: 7 / 0
- bits
Port FF3B
is the data port (read/write). When the palette group is selected, the byte written will describe the color. When the mode group is selected, the byte output will be interpreted as follows:
bit 0: ULAplus™ palette on (1) or off (0)
Reading from port FF3B
returns the last data byte written to the currently selected register. This can be used to read back the current palette or determine if palette mode is active.
Implementations that support the Timex video modes use port FF
as the primary means to set the video mode, as per the Timex machines. It is left to the individual implementations to determine if reading the port returns the previous write or the floating bus.
Note: Modes 2
to 255
are reserved for future use. Groups 10
and 11
are reserved for future use.
GRB Palette Entries
For a device using the GRB color space, the palette entry is interpreted as follows:
bits 0-1: Blue Intensity bits 2-4: Red Intensity bits 5-7: Green Intensity
This color space uses a sub-set of 9-bit GRB. The missing lowest blue bit is set to OR of the other two blue bits (Bb
becomes 000
for 00
, and Bb1
for anything else). This gives access to a fixed half the potential 512 color palette. This reduces the jump in intensity in the lower range in the earlier version of the specification. It also means the standard palette can now be represented by the ULAplus™ palette.
Grayscale Palette Entries (deprecated)
In grayscale mode, each palette entry describes an intensity from zero to 255. This can be achieved by simply removing the colour from the output signal.
Limitations
Although in theory 64 colors can be displayed at once, in practice this is usually not possible except when displaying color bars, because the four CLUTs are mutually exclusive; it is not possible to mix colors from two CLUTs in the same cell. However, with software palette cycling it is possible to display all 256 colors on screen at once.
Emulation
The 64 color mode look-up table is organized as four palettes of 16 colors.
Bits 7
and 6
of each attribute byte, normally used for FLASH
and BRIGHT
, are used as an index value (0-3
) to select one of the four color palettes.
Each color palette has 16 entries: eight for INK
, and eight for PAPER
. Bits 0-2
(INK
) and 3-5
(PAPER
) of the attribute byte are used as indexes to retrieve color data from the selected palette.
With the standard Spectrum display, the BORDER
color is the same as the PAPER
color in the first CLUT. For example BORDER 0
would set the border to the same color as PAPER 0
(with the BRIGHT
and FLASH
bits not set).
The complete index can be calculated as:
- ink color = (
FLASH
* 2 +BRIGHT
) * 16 +INK
- paper color = (
FLASH
* 2 +BRIGHT
) * 16 +PAPER
+ 8
When scaling 3-bits of color data to more bits for emulators that operate in high color mode, simply concatenate the bits repeatedly and then truncate to as many bits as needed. For example, for 8-bits the following conversion should be used:
76543210
- hmlhmlhm
where h
is the high bit, m
is the middle bit, and l
is the low bit of the original 3-bit value.
With the Timex hi res display, the BORDER
color is the same as the PAPER
color in the second CLUT. Bits 3-5
of port FF
set the INK
, PAPER
, and BORDER
values to the following ULAplus™ palette registers:
BITS INK PAPER BORDER 000 24 31 31 001 25 30 30 010 26 29 29 011 27 28 28 100 28 27 27 101 29 26 26 110 30 25 25 111 31 24 24
Extension to the ZX-State (SZX) Format
ZXSTPALETTE
The state of the ULA registers found in the 64 color replacement ULA. This block may be present for any machine.
// Palette Block flags #define ZXSTPALETTE_DISABLED 0 #define ZXSTPALETTE_ENABLED 1 // Palette Block (contains the palette register values) typedef struct _tagZXSTPALETTEBLOCK { ZXSTBLOCK blk; BYTE chFlags; BYTE chCurrentRegister; BYTE chPaletteRegs[64]; BYTE chFf; } ZXSTPALETTEBLOCK, *LPZXSTPALETTEBLOCK;
Members
blk
The block header. The block id is ZXSTBID_PALETTE ('P', 'L', 'T', 'T').
chFlags
A flags that indicates if the palette is enabled or if the normal display mode is in use. This can be one of:
- ZXSTPALETTE_DISABLED / Normal palette mode with BRIGHT and FLASH
- ZXSTPALETTE_ENABLED / 64 color palette mode
chCurrentRegister
The currently selected palette register (0-63
).
chPaletteRegs
The current values of the palette registers.
chFf
The current value of port FF
which controls the Timex screen mode, and high resolution colors. Added in v1.1aof the specification.
Extension to the SCR format
A 6912 byte .SCR
file contains a standard Spectrum screen.
A 6976 byte .SCR
file contains a standard Spectrum screen followed by 64 color registers.
A 12288 byte .SCR
file contains a Timex hi-color screen.
A 12352 byte .SCR
file contains a Timex hi-color screen followed by 64 color registers.
A 12289 byte .SCR
file contains a Timex hi-res screen.
A 12353 byte .SCR
file contains a Timex hi-res screen followed by the hi-res color information that was dumped from port 255, followed by 64 color registers.
Palette File Format
The palette format doubles as the BASIC patch loader. This enables you to edit patches produced by other people.
; 64 color palette file format (internal) - version 1.0 ; Copyright (c) 2009 ZX Design and Media ; ; The palette file is stored as a BASIC program with embedded machine code header: defb 0x00; program file defb 0x14, 0x01, "64colour"; file name defw 0x0097; data length defw 0x0000; autostart line defw 0x0097; program length ; 0 RANDOMIZE USR ((PEEK VAL "2 ; 3635"+VAL "256"*PEEK VAL "23636" ; )+VAL "48"): LOAD "": REM basic: defb 0x00, 0x00, 0x93, 0x00, 0xf9, 0xc0, 0x28, 0x28 defb 0xbe, 0xb0, 0x22, 0x32, 0x33, 0x36, 0x33, 0x35 defb 0x22, 0x2b, 0xb0, 0x22, 0x32, 0x35, 0x36, 0x22 defb 0x2a, 0xbe, 0xb0, 0x22, 0x32, 0x33, 0x36, 0x33 defb 0x36, 0x22, 0x29, 0x2b, 0xb0, 0x22, 0x34, 0x38 defb 0x22, 0x29, 0x3a, 0xef, 0x22, 0x22, 0x3a, 0xea start: di; disable interrupts ld hl, 38; HL = length of code add hl, bc; BC = entry point (start) from BASIC ld bc, 0xbf3b; register select ld a, 64; mode group out (c), a; ld a, 1; ld b, 0xff; choose register port out (c), a; turn palette mode on xor a; first register setreg: ld b, 0xbf; choose register port out (c), a; select register ex af, af'; save current register select ld a, (hl); get data ld b, 0xff; choose data port out (c), a; set it ex af, af'; restore current register inc hl; advance pointer inc a; increase register cp 64; are we nearly there yet? jr nz, setreg; repeat until all 64 have been done ei; enable interrupts ret; return ; this is where the actual data is stored.; The following is an example palette. registers: defb 0x00, 0x02, 0x18, 0x1b, 0xc0, 0xc3, 0xd8, 0xdb; INK defb 0x00, 0x02, 0x18, 0x1b, 0xc0, 0xc3, 0xd8, 0xdb; PAPER defb 0x00, 0x03, 0x1c, 0x1f, 0xe0, 0xe3, 0xfc, 0xff; +BRIGHT defb 0x00, 0x03, 0x1c, 0x1f, 0xe0, 0xe3, 0xfc, 0xff; defb 0xdb, 0xd8, 0xc3, 0xc0, 0x1b, 0x18, 0x02, 0x00; +FLASH defb 0xdb, 0xd8, 0xc3, 0xc0, 0x1b, 0x18, 0x02, 0x00; defb 0xff, 0xfc, 0xe3, 0xe0, 0x1f, 0x1c, 0x03, 0x00; +BRIGHT/ defb 0xff, 0xfc, 0xe3, 0xe0, 0x1f, 0x1c, 0x03, 0x00; +FLASH terminating_byte: defb 0x0d;
Quick Palette Set
set_palette: ld c, 0x3b; ULAplus™ port ld de, 0x00bf; d = data, e = register ld hl, pal_end; mode group register ld a, 65; becomes 64 palette_loop: dec a; next register ld b, e; register port out (c), a; select register ld b, d; data port outd; out bc, (hl); dec hl; dec b and a; was that the last register? jr nz, palette_loop; set all 64 entries ret; done palette: incbin "palette.bin"; 64 bytes of G3R3B2 palette register values pal_end: defb 1; write 1 to register 64 to enable ULAplus mode
Standard Spectrum Palette for ULAplus™
; %G..R..B. ;standard black equ %00000000 blue equ %00000010 red equ %00010100 magenta equ %00010110 green equ %10100000 cyan equ %10100010 yellow equ %10110100 white equ %10110110 ;bright b_black equ %00000000 b_blue equ %00000011 b_red equ %00011100 b_magenta equ %00011111 b_green equ %11100000 b_cyan equ %11100011 b_yellow equ %11111100 b_white equ %11111111 ;flash f_black equ %10110110 f_blue equ %10110100 f_red equ %10100010 f_magenta equ %10100000 f_green equ %00010110 f_cyan equ %00010100 f_yellow equ %00000010 f_white equ %00000000 ;flash_bright fb_black equ %11111111 fb_blue equ %11111100 fb_red equ %11100011 fb_magenta equ %11100000 fb_green equ %00011111 fb_cyan equ %00011100 fb_yellow equ %00000011 fb_white equ %00000000 palette: defb black, blue, red, magenta defb green, cyan, yellow, white defb black, blue, red, magenta defb green, cyan, yellow, white defb b_black, b_blue, b_red, b_magenta defb b_green, b_cyan, b_yellow, b_white defb b_black, b_blue, b_red, b_magenta defb b_green, b_cyan, b_yellow, b_white defb f_black, f_blue, f_red, f_magenta defb f_green, f_cyan, f_yellow, f_white defb f_black, f_blue, f_red, f_magenta defb f_green, f_cyan, f_yellow, f_white defb fb_black, fb_blue, fb_red, fb_magenta defb fb_green, fb_cyan, fb_yellow, fb_white defb fb_black, fb_blue, fb_red, fb_magenta defb fb_green, fb_cyan, fb_yellow, fb_white
Legal
ULAplus™ is a trademark of ZX Design and Media. ULAplus™ is a royalty-free open format. The official ULAplus™ specification is released under the Creative Commons Attribution-Share Alike License.
Status | Released |
Category | Other |
Author | ZX Design and Media |
Comments
Log in with itch.io to leave a comment.
Just bought 2x ula + in the zx hdmi am i right in thinking timex hi rez is not supported?
Thanks for the link.
Hello. Nice to see the ULAplus specs here.
There's an issue with the formatting of the listings which makes reading them quite hard. Would it be possible to correct this please?
The md version formatting is correct.
Indeed, thanks.
Is there any was of cleaning up the online version above?
Not without custom CSS, and that requires itch.io to enable CSS support on the account.
With the code listings on this page, it seems like there's something wrong with the CR/Line feeds.
I just did a quick test on a test page here on Itch with code from the ULAplus spec and it shows up normal when creating a code-block.
The only thing that Itch does is remove "empty" lines (ie before the "header:", "basic:" labels, etc.) but that can be easily fixed by putting only a space on those lines.
Nice. I couldn't find a reference to a code style in the documentation, but that looks good.