Difference between revisions of "Palettes"
(Removing Sol's lovely corrected image, but we don't need two.) |
|||
(7 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
− | + | == Palettes and Colour Mapping == | |
− | + | To increase the number of available colours on screen, the Spectrum Next supports '''palletized colours'''. The number of displayable colours varies depending on the graphics mode: | |
− | + | * '''ULA''' – 15 colours on screen at once (7 with optional BRIGHT - as bright black is still black), using 1 of 2 ULA palettes | |
+ | * '''ULANext''' – Up to 256 colours via attribute mapping, using 1 of 2 ULA Next palettes | ||
+ | * '''Sprites''' – 16 colours per sprite (4-bit) with palette offsets or 256 colours (8-bit), using 1 of 2 sprite palettes | ||
+ | * '''Layer 2''' – Full 256-colour display, using 8-bit pixel values as palette indices, using 1 of 2 Layer2 palettes | ||
+ | * '''Tilemap''' – One of two tilemap palettes used for final colour lookup. | ||
− | + | Colour IDs in [[Layer 2]], [[Sprites]], and the [[ULA]] are treated as '''palette indices'''. These indices point to colours defined in the palette memory. | |
− | + | == Setting Palette Colours == | |
+ | To define a colour in a palette: | ||
− | [[File:256-palette.png|left|thumb| Default 256 colour 8-bit indexed palette.]] | + | # Use {{NextRegNo|$43}} to select which palette to write to |
+ | # Use {{NextRegNo|$40}} to select the '''palette entry index''' | ||
+ | # Then send the colour using: | ||
+ | ## {{NextRegNo|$41}} to send an '''8-bit colour''' in '''RRRGGGBB''' format | ||
+ | ## ''or'' {{NextRegNo|$44}} to send a '''9-bit colour''', as two bytes: | ||
+ | ### First byte: RRRGGGBB | ||
+ | ### Second byte: | ||
+ | ###* bit 0 = the '''third blue bit''' | ||
+ | ###* For Layer 2: bit 7 = '''priority colour''' — forces the pixel to draw above all layers | ||
+ | |||
+ | {{NextRegNo|$41}} generates the missing third blue bit automatically as an OR of the other two. | ||
+ | Both registers {{NextRegNo|$41}} and {{NextRegNo|$44}} will auto-increment the palette index if auto-increment is enabled in {{NextRegNo|$43}}. | ||
+ | |||
+ | == ULA and ULANext Colour Mapping == | ||
+ | |||
+ | For the ULA, colour mapping behaves differently depending on the mode: | ||
+ | |||
+ | * '''Standard ULA''' | ||
+ | ** INK: palette indices 0–7 | ||
+ | ** BRIGHT INK: indices 8–15 | ||
+ | ** PAPER and BORDER: indices 16–23 and 24–31 | ||
+ | |||
+ | * '''ULANext mode''' (enabled via bit 0 of {{NextRegNo|$43}}): | ||
+ | ** INK: indices 0–127 | ||
+ | ** PAPER/BORDER: indices 128–255 | ||
+ | ** Mask in {{NextRegNo|$42}} defines how attribute byte is split into INK and PAPER | ||
+ | ** If mask is 255 (full ink), PAPER and BORDER colour come from fallback in {{NextRegNo|$4A}} | ||
+ | |||
+ | ULA+ is also avilable by enabling bit 3 of {{NextRegNo|$68}} with further information on how ULA+ works here : https://zxdesign.itch.io/ulaplus | ||
+ | |||
+ | == Palette Registers in Detail == | ||
+ | |||
+ | === {{NextRegNo|$40}} – Palette Index === | ||
+ | * Selects the palette index to modify | ||
+ | * This index will apply to whichever palette is selected via {{NextRegNo|$43}} | ||
+ | * Auto-increments after a write, if enabled | ||
+ | * ULA-specific behaviour: | ||
+ | ** Standard INK = indices 0–7 | ||
+ | ** Bright INK = 8–15 | ||
+ | ** PAPER = 16–23 | ||
+ | ** Bright PAPER = 24–31 | ||
+ | ** In ULANext, INK uses a subset of 0–127 and PAPER uses 128–255 | ||
+ | ** In ULA+ mode, the top 64 entries hold the ULA+ palette. | ||
+ | ** The ULA always takes border colour from paper for standard ULA and ULAnext. | ||
+ | |||
+ | === {{NextRegNo|$41}} – 8-bit Colour Value === | ||
+ | * Writes an 8-bit colour value in '''RRRGGGBB''' format | ||
+ | * The missing third blue bit is generated via OR of the two blue bits | ||
+ | * Writing updates the current palette entry and increments the index (if auto-increment is enabled) | ||
+ | * Reads do not auto-increment and only return the last 8-bit value | ||
+ | |||
+ | === {{NextRegNo|$42}} – ULANext Attribute Format === | ||
+ | * Defines how many bits of the ULA attribute byte represent INK (rest is PAPER) | ||
+ | * Must be one of: 1, 3, 7, 15, 31, 63, 127, 255 | ||
+ | * INK index = base 0 | ||
+ | * PAPER + BORDER index = base 128 | ||
+ | * If not a valid mask, INK = attribute AND mask, and PAPER/BORDER use fallback from {{NextRegNo|$4A}} | ||
+ | * The 255 value enables the full ink colour mode making all the palette entries INK. In this case PAPER and border are both taken from the fallback colour in nextreg 0x4A. | ||
+ | * If the mask is not one of those listed above, the INK is taken as the logical AND of the mask with the attribute byte and the PAPER and border colour are again both taken from the fallback colour in {{NextRegNo|0x4A}}. | ||
+ | |||
+ | === {{NextRegNo|$43}} – Palette Control === | ||
+ | * bit 7 = Disable auto-increment | ||
+ | * bits 6–4 = Select palette to write or read: | ||
+ | ** <code>000</code> = ULA 1st | ||
+ | ** <code>100</code> = ULA 2nd | ||
+ | ** <code>001</code> = Layer 2 1st | ||
+ | ** <code>101</code> = Layer 2 2nd | ||
+ | ** <code>010</code> = Sprites 1st | ||
+ | ** <code>110</code> = Sprites 2nd | ||
+ | ** <code>011</code> = Tilemap 1st | ||
+ | ** <code>111</code> = Tilemap 2nd | ||
+ | * bit 3 = Use Sprites palette (0 = first palette, 1 = second palette) (soft reset = 0) | ||
+ | * bit 2 = Use Layer 2 palette (0 = first palette, 1 = second palette) (soft reset = 0) | ||
+ | * bit 1 = Use ULA palette (0 = first palette, 1 = second palette) (soft reset = 0) | ||
+ | * bit 0 = Enable ULANext mode | ||
+ | |||
+ | === {{NextRegNo|$44}} – 9-bit Colour Value === | ||
+ | * Two consecutive writes are needed: | ||
+ | ** First byte: '''RRRGGGBB''' | ||
+ | ** Second byte: '''P000000B''' | ||
+ | *** bit 0 = third blue bit | ||
+ | *** For Layer 2: bit 7 = '''priority colour''' (Layer 2 pixel is drawn above all other layers). If you need the same colour in both priority and normal modes, you will need to have two different entries with the same colour one with and one without priority. | ||
+ | * Auto-increments index if bit 7 is 0 in {{NextRegNo|$43}} | ||
+ | * Reads only return second byte - does not increment. | ||
+ | * Writing to {{NextRegNo|$40}}, {{NextRegNo|$41}}, or {{NextRegNo|$43}} resets to first write | ||
+ | |||
+ | === {{NextRegNo|$4A}} – Fallback Colour === | ||
+ | * 8-bit colour used if all layers are transparent | ||
+ | * Also used as PAPER/BORDER when ULANext attribute mask is invalid or set to 255 (soft reset = 0xe3) | ||
+ | |||
+ | === {{NextRegNo|$4B}} – Sprite Transparency Index === | ||
+ | * Sets which palette index will be treated as transparent in sprite rendering (soft reset = 0xe3) | ||
+ | * Only bottom 4 bits are used for 4-bit sprites | ||
+ | |||
+ | === {{NextRegNo|$4C}} – Tilemap Transparency Index === | ||
+ | * Lower 4 bits set the tilemap transparent colour index (soft reset = 0xf) | ||
+ | * Upper 4 bits must be zero | ||
+ | |||
+ | == Prepared Palette Files == | ||
+ | |||
+ | The following palettes were created by '''Stefan Bylund''' for various graphics software: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Tool / Format !! 256 Colour Palette !! 512 Colour Palette | ||
+ | |- | ||
+ | | Photoshop || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-256.act .act] || – | ||
+ | |- | ||
+ | | GIMP || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-256.gpl .gpl] || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-512.gpl .gpl] | ||
+ | |- | ||
+ | | JASC-PAL || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-256.pal .pal] || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-512.pal .pal] | ||
+ | |- | ||
+ | | Paint.NET || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-256.txt .txt] || [https://github.com/stefanbylund/zxnext_bmp_tools/tree/master/palettes/zxnext-palette-512.txt .txt] | ||
+ | |} | ||
+ | |||
+ | <small>Only 256 colours can be displayed at once, even when using 512-colour palettes.</small> | ||
+ | |||
+ | More tools and files by Stefan can be found here : | ||
+ | [https://github.com/stefanbylund/zxnext_bmp_tools github.com/stefanbylund/zxnext_bmp_tools] | ||
+ | |||
+ | == Palette Visuals == | ||
+ | |||
+ | [[File:256-palette-2.png|left|thumb|Default 256 colour 8-bit indexed palette.]] | ||
+ | <br clear="all"> | ||
+ | |||
+ | Modern displays use 8-bit RGB values (0–255), but the Spectrum Next uses 3-bit values (0–7). This means converting colours will always involve some form of approximation or scaling. | ||
+ | |||
+ | The following image shows a palette calculated using linear scaling from 3-bit to 8-bit using: | ||
+ | |||
+ | <code>res = src × 255 ÷ 7</code> | ||
+ | |||
+ | |||
+ | <br clear="all"> |
Latest revision as of 15:25, 8 April 2025
Palettes and Colour Mapping
To increase the number of available colours on screen, the Spectrum Next supports palletized colours. The number of displayable colours varies depending on the graphics mode:
- ULA – 15 colours on screen at once (7 with optional BRIGHT - as bright black is still black), using 1 of 2 ULA palettes
- ULANext – Up to 256 colours via attribute mapping, using 1 of 2 ULA Next palettes
- Sprites – 16 colours per sprite (4-bit) with palette offsets or 256 colours (8-bit), using 1 of 2 sprite palettes
- Layer 2 – Full 256-colour display, using 8-bit pixel values as palette indices, using 1 of 2 Layer2 palettes
- Tilemap – One of two tilemap palettes used for final colour lookup.
Colour IDs in Layer 2, Sprites, and the ULA are treated as palette indices. These indices point to colours defined in the palette memory.
Setting Palette Colours
To define a colour in a palette:
- Use Enhanced ULA Control Register ($43) to select which palette to write to
- Use Palette Index Register ($40) to select the palette entry index
- Then send the colour using:
- Palette Value Register ($41) to send an 8-bit colour in RRRGGGBB format
- or Enhanced ULA Palette Extension ($44) to send a 9-bit colour, as two bytes:
- First byte: RRRGGGBB
- Second byte:
- bit 0 = the third blue bit
- For Layer 2: bit 7 = priority colour — forces the pixel to draw above all layers
Palette Value Register ($41) generates the missing third blue bit automatically as an OR of the other two. Both registers Palette Value Register ($41) and Enhanced ULA Palette Extension ($44) will auto-increment the palette index if auto-increment is enabled in Enhanced ULA Control Register ($43).
ULA and ULANext Colour Mapping
For the ULA, colour mapping behaves differently depending on the mode:
- Standard ULA
- INK: palette indices 0–7
- BRIGHT INK: indices 8–15
- PAPER and BORDER: indices 16–23 and 24–31
- ULANext mode (enabled via bit 0 of Enhanced ULA Control Register ($43)):
- INK: indices 0–127
- PAPER/BORDER: indices 128–255
- Mask in Enhanced ULA Ink Color Mask ($42) defines how attribute byte is split into INK and PAPER
- If mask is 255 (full ink), PAPER and BORDER colour come from fallback in Transparency colour fallback Register ($4A)
ULA+ is also avilable by enabling bit 3 of ULA Control Register ($68) with further information on how ULA+ works here : https://zxdesign.itch.io/ulaplus
Palette Registers in Detail
Palette Index Register ($40) – Palette Index
- Selects the palette index to modify
- This index will apply to whichever palette is selected via Enhanced ULA Control Register ($43)
- Auto-increments after a write, if enabled
- ULA-specific behaviour:
- Standard INK = indices 0–7
- Bright INK = 8–15
- PAPER = 16–23
- Bright PAPER = 24–31
- In ULANext, INK uses a subset of 0–127 and PAPER uses 128–255
- In ULA+ mode, the top 64 entries hold the ULA+ palette.
- The ULA always takes border colour from paper for standard ULA and ULAnext.
Palette Value Register ($41) – 8-bit Colour Value
- Writes an 8-bit colour value in RRRGGGBB format
- The missing third blue bit is generated via OR of the two blue bits
- Writing updates the current palette entry and increments the index (if auto-increment is enabled)
- Reads do not auto-increment and only return the last 8-bit value
Enhanced ULA Ink Color Mask ($42) – ULANext Attribute Format
- Defines how many bits of the ULA attribute byte represent INK (rest is PAPER)
- Must be one of: 1, 3, 7, 15, 31, 63, 127, 255
- INK index = base 0
- PAPER + BORDER index = base 128
- If not a valid mask, INK = attribute AND mask, and PAPER/BORDER use fallback from Transparency colour fallback Register ($4A)
- The 255 value enables the full ink colour mode making all the palette entries INK. In this case PAPER and border are both taken from the fallback colour in nextreg 0x4A.
- If the mask is not one of those listed above, the INK is taken as the logical AND of the mask with the attribute byte and the PAPER and border colour are again both taken from the fallback colour in (0x4A).
Enhanced ULA Control Register ($43) – Palette Control
- bit 7 = Disable auto-increment
- bits 6–4 = Select palette to write or read:
000
= ULA 1st100
= ULA 2nd001
= Layer 2 1st101
= Layer 2 2nd010
= Sprites 1st110
= Sprites 2nd011
= Tilemap 1st111
= Tilemap 2nd
- bit 3 = Use Sprites palette (0 = first palette, 1 = second palette) (soft reset = 0)
- bit 2 = Use Layer 2 palette (0 = first palette, 1 = second palette) (soft reset = 0)
- bit 1 = Use ULA palette (0 = first palette, 1 = second palette) (soft reset = 0)
- bit 0 = Enable ULANext mode
Enhanced ULA Palette Extension ($44) – 9-bit Colour Value
- Two consecutive writes are needed:
- First byte: RRRGGGBB
- Second byte: P000000B
- bit 0 = third blue bit
- For Layer 2: bit 7 = priority colour (Layer 2 pixel is drawn above all other layers). If you need the same colour in both priority and normal modes, you will need to have two different entries with the same colour one with and one without priority.
- Auto-increments index if bit 7 is 0 in Enhanced ULA Control Register ($43)
- Reads only return second byte - does not increment.
- Writing to Palette Index Register ($40), Palette Value Register ($41), or Enhanced ULA Control Register ($43) resets to first write
Transparency colour fallback Register ($4A) – Fallback Colour
- 8-bit colour used if all layers are transparent
- Also used as PAPER/BORDER when ULANext attribute mask is invalid or set to 255 (soft reset = 0xe3)
Sprites Transparency Index Register ($4B) – Sprite Transparency Index
- Sets which palette index will be treated as transparent in sprite rendering (soft reset = 0xe3)
- Only bottom 4 bits are used for 4-bit sprites
Tilemap Transparency Index Register ($4C) – Tilemap Transparency Index
- Lower 4 bits set the tilemap transparent colour index (soft reset = 0xf)
- Upper 4 bits must be zero
Prepared Palette Files
The following palettes were created by Stefan Bylund for various graphics software:
Tool / Format | 256 Colour Palette | 512 Colour Palette |
---|---|---|
Photoshop | .act | – |
GIMP | .gpl | .gpl |
JASC-PAL | .pal | .pal |
Paint.NET | .txt | .txt |
Only 256 colours can be displayed at once, even when using 512-colour palettes.
More tools and files by Stefan can be found here : github.com/stefanbylund/zxnext_bmp_tools
Palette Visuals
Modern displays use 8-bit RGB values (0–255), but the Spectrum Next uses 3-bit values (0–7). This means converting colours will always involve some form of approximation or scaling.
The following image shows a palette calculated using linear scaling from 3-bit to 8-bit using:
res = src × 255 ÷ 7