-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathchapter-next-palette.tex
297 lines (214 loc) · 12.8 KB
/
chapter-next-palette.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
\section{Palette}
\label{zx_next_palette}
Next greatly enhances ZX Spectrum video capabilities by offering several new ways to draw graphics on a screen. We'll see how to program each in later chapters, but let's check common behaviour first - colour management.
To draw a pixel on a screen, we need to set its colour as data in memory. Next shares implementation to other contemporary 8-bit computers - all possible colours are stored together in a palette, as an array of RGB values, and each pixel is simply an index into this array. This approach requires less memory and allows creating efficient effects such as fade to/from black, transitions from day to night, water animations etc.
\subsection{Palette Selection}
Each graphics mode or layer has not one but two palettes, each of which can be changed independently. Of course, only one of two can be active at any given time for each mode. The other can be initialized with alternate colours and can be quickly activated to achieve colour animation effects.
Active palette is set with \PortTextXRef{43} for ULA, Layer 2 and Sprites and \PortTextXRef{6B} for Tilemap.
\subsection{Palette Editing}
Next palettes have 256 colours. All are initialized with default values, so they are usable out of the box. But it's also possible to change every colour. Regardless of the palette, the procedure to read or write colours is:
\begin{enumerate}[topsep=1pt,itemsep=1pt]
\item \PortTextXRef{43} selects palette which colours you want to edit
\item \PortTextXRef{40} selects colour index that will be read or written
\item \PortTextXRef{41} or \PortTextXRef{44} reads or writes data for selected colour
\end{enumerate}
% note: since all mentioned registers already include page numbers in enumerate above, they are omitted in the description below, makes it more readable
When writing colours, we can chose to automatically increment colour indexes after each write. Bit 7 of \PortTextXRef[]{43} is used for that purpose. This works the same for both write registers (\MemAddr{41} and \MemAddr{44}). Colour RGB values can either be 8-bit {\tt RRRGGGBB}, or 9-bit {\tt RRRGGGBBB} values. Use \PortTextXRef[]{41} for 8-bit and \PortTextXRef[]{44} for 9-bit.
Note: \PortTextXRef[]{43} has two roles when working with palettes - it selects the active palette for display (out of two available - only for ULA, Layer 2 and Sprites) and selects palette for editing (for all layers, including Tilemap). Therefore care needs to be taken when updating colour entries to avoid accidentally changing the active palette for display at the same time. Depending on our program, we may first need to read the value and then only change bits affecting the palette for editing to ensure the rest of the data remains unaffected.
\subsection{8 Bit Colours}
8-bit colours are stored as {\tt RRRGGGBB} values with 3 bits per red and green and 2 bits per blue component. Each colour is therefore stored as a single byte. \PortTextXRef{41} is used to read or write the value.
Here's a reusable subroutine for copying {\tt B} number of colours stored as a contiguous block in memory addressed by {\tt HL} register, starting at the currently selected colour index:
\begin{tcblisting}{}
Copy8BitPalette:
LD A, (HL) ; Load RRRGGGBB into A
INC HL ; Increment to next colour entry
NEXTREG &41, A ; Send colour data to Next HW
DJNZ Copy8BitPalette ; Repeat until B=0
\end{tcblisting}
And we'd call the subroutine with something like:
\begin{tcblisting}{}
NEXTREG &43, %00010000 ; Auto increment, Layer 2 first palette for read/write
NEXTREG &40, 0 ; Start copying into index 0
LD HL, palette ; Address to copy RRRGGGBB values from
LD B, 255 ; Copy 255 colours
CALL Copy8BitPalette
\end{tcblisting}
Note: we could also use DMA to transfer all the bytes. I won't show it here, but feel free to implement it as an excercise - see section \ref{zx_next_dma} for details on programming the DMA.
\subsection{9 Bit Colours}
With 9 bits per colour, each RGB component uses full 3 bits, thus greatly increasing the available colour gamut. However, each colour needs 2 bytes in memory instead of 1. To read or write we use \PortTextXRef{44} register instead of \PortAddr{41}. It works similarly to \PortAddr{41}~except that each colour requires two writes: first one stores {\tt RRRGGGBB} part and second least significant bit of blue component. Subroutine for copying 9-bit colours:
\begin{tcblisting}{}
Copy9BitPalette:
LD A, (HL) ; Load RRRGGGBB into A
INC HL ; Increment to next byte
NEXTREG &44, A ; Send colour data to Next HW
LD A, (HL) ; Load remaining byte with LSB of B component into A
INC HL ; Increment to next colour entry
NEXTREG &44, A ; Send colour data to Next HW and increment index
DJNZ Copy9BitPalette ; Repeat until B=0
\end{tcblisting}
Note: subroutine requires that colours are stored in 2 bytes with first containing {\tt RRRGGGBB} part and second least significant bit of blue. Which is how typically drawing programs store a 9-bit palette anyways. The code for calling this subroutine is exactly the same as for the 8-bit colours above.
\subsection{Palette Registers}
\label{zx_next_palette_registers}
\subsubsection{\PortDeclaration{40}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Reads or writes palette colour index to be manipulated}
\end{NextPort}
% note: register $41 is declared on next page (same spread), so we omit page number, but others are declared on the following spreads, so we leave them
Writing an index {\tt 0-255} associates it with colour set through \PortTextXRef[]{41} or \PortTextXRef{44} of currently selected pallette in \PortTextXRef{43}. Write also resets value of \PortTextXRef{44} so next write will occur for first colour of the palette
While Tilemap, Layer 2 and Sprites palettes use all 256 distinct colours (with some caveats, as described in specific chapters), ULA modes work like this:
\begin{quote}
\small
\NewDocumentEnvironment{PalIndexTable}{ +b }{
\begin{tabularx}{\linewidth}{@{}p{1.5cm}X}
Index & Colours \\
#1
\end{tabularx}
\vspace*{-1ex}
}{}
\NewDocumentCommand{\PalIndexEntry}{ m m }{{\tt #1} & #2 \\}
\textbf{Classic ULA}
\begin{PalIndexTable}
\PalIndexEntry{~0-7}{Ink}
\PalIndexEntry{~8-15}{Bright ink}
\PalIndexEntry{16-23}{Paper}
\PalIndexEntry{24-31}{Bright paper}
\end{PalIndexTable}
Border is taken from paper colours.
\vspace*{1em}
\textbf{ULA+}
\begin{PalIndexTable}
\PalIndexEntry{0-64}{Ink}
\end{PalIndexTable}
Paper and border are taken from \PortTextXRef{4A}.
\vspace*{1em}
\textbf{ULANext normal mode}
\begin{PalIndexTable}
\PalIndexEntry{~~0-127}{Ink (only a subset)}
\PalIndexEntry{128-255}{Paper (only a subset)}
\end{PalIndexTable}
% note: register $42 is declared on next page (same spread), so we omit page number to keep text more readable
Border is taken from paper colours. The number of active indices depends on the number of attribute bits assigned to ink and paper out of the attribute byte by \PortTextXRef[]{42}.
\vspace*{1em}
\textbf{ULANext full-ink mode}
\begin{PalIndexTable}
\PalIndexEntry{0-255}{Ink}
\end{PalIndexTable}
Paper and border are taken from \PortTextXRef{4A}.
\end{quote}
\pagebreak
\subsubsection{\PortDeclaration{41}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Reads or writes 8-bit colour data}
\end{NextPort}
Format is:
\begin{BitTableByte}
\BitSmall{$R_2$} &
\BitSmall{$R_1$} &
\BitSmall{$R_0$} &
\BitSmall{$G_2$} &
\BitSmall{$G_1$} &
\BitSmall{$G_0$} &
\BitSmall{$B_2$} &
\BitSmall{$B_1$} \\
\hline
\BitStartMulti{3}{Red} &
\BitMulti{3}{Green} &
\BitMulti{2}{Blue} \\
\end{BitTableByte}
Least significant bit of blue is set to {\tt OR} between $B_2$ and $B_1$.
% note: register $40 is declared on previous page (same spread), so we omit page number to keep text more readable
Writing the value will automatically increment index in \PortTextXRef[]{40}, if auto-increment is enabled in \PortTextXRef{43}. Read doesn't auto-increment index.
\subsubsection{\PortDeclaration{42}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{The number for last ink colour entry in the palette. Only used when ULANext mode is enabled (see \PortTextXRef{43}). Only the following values are allowed, harware behavior is unpredictable for other values:}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{1}{Ink and paper only use 1 colour each on indices {\tt 0} and {\tt 128} respectively}
\PortBitLine{3}{Ink and paper use 4 colours each, on indices {\tt 0}-{\tt 3} and {\tt 128}-{\tt 131}}
\PortBitLine{7}{Ink and paper use 8 colours each, on indices {\tt 0}-{\tt 7} and {\tt 128}-{\tt 135}}
\PortBitLine{15}{Ink and paper use 16 colours each, on indices {\tt 0}-{\tt 15} and {\tt 128}-{\tt 143}}
\PortBitLine{31}{Ink and paper use 32 colours each, on indices {\tt 0}-{\tt 31} and {\tt 128}-{\tt 159}}
\PortBitLine{63}{Ink and paper use 64 colours each, on indices {\tt 0}-{\tt 63} and {\tt 128}-{\tt 191}}
\PortBitLine{127}{Ink and paper use 128 colours each, on indices {\tt 0}-{\tt 127} and {\tt 128}-{\tt 255}}
\PortBitLine{255}{Enables full-ink colour mode where all indices are ink. In this mode paper and border are taken from \PortTextXRef{4A}}
\end{PortBitConfig}
}
\PortDescOnly{Default value is {\tt 7} for core 3.0 and later, {\tt 15} for older cores.}
\end{NextPort}
\subsubsection{\PortDeclaration{43}}
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 1} to disable palette index auto-increment, {\tt 0} to enable}
\PortBits{6-4}
\PortDesc{Selects palette for read or write}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{000}{ULA first palette}
\PortBitLine{100}{ULA second palette}
\PortBitLine{001}{Layer 2 first palette}
\PortBitLine{101}{Layer 2 second palette}
\PortBitLine{010}{Sprites first palette}
\PortBitLine{110}{Sprites second palette}
\PortBitLine{011}{Tilemap first palette}
\PortBitLine{111}{Tilemap second palette}
\end{PortBitConfig}
}
\PortBits{3}
\PortDesc{Selects active Sprites palette ({\tt 0} = first palette, {\tt 1} = second palette)}
\PortBits{2}
\PortDesc{Selects active Layer 2 palette ({\tt 0} = first palette, {\tt 1} = second palette)}
\PortBits{1}
\PortDesc{Selects active ULA palette ({\tt 0} = first palette, {\tt 1} = second palette)}
\PortBits{0}
\PortDesc{Enables ULANext mode if {\tt 1} ({\tt 0} after reset)}
\end{NextPort}
% note: register $44 is declared on the same page, so we omit page number to keep text more readable
Write will also reset the index of \PortTextXRef[]{44} so next write there will be considered as first byte of first colour.
\subsubsection{\PortDeclaration{44}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Reads or writes 9-bit colour definition}
\end{NextPort}
Two consequtive writes are needed:
\begin{multicols}{2}
First write:
\begin{BitTableByte}[C{1em}]
\BitSmall{$R_2$} &
\BitSmall{$R_1$} &
\BitSmall{$R_0$} &
\BitSmall{$G_2$} &
\BitSmall{$G_1$} &
\BitSmall{$G_0$} &
\BitSmall{$B_2$} &
\BitSmall{$B_1$} \\
\hline
\BitStartMulti{3}{Red} &
\BitMulti{3}{Green} &
\BitMulti{2}{Blue} \\
\end{BitTableByte}
\columnbreak
Second write:
\begin{BitTableByte}[C{1em}]
\BitSmall{$P_r$} &
\BitMulti{6}{-} &
\BitSmall{$B_0$} \\
\hline
\BitSmall{$L2$} &
\BitMulti{6}{Reserved, set to {\tt 0}} &
\BitSmall{B} \\
\end{BitTableByte}
\end{multicols}
Bit 7 of the second write must be {\tt 0} except for Layer 2 palettes where it specifies colour priority. If set to {\tt 1}, then the colour will always be on top, above all other layers, regardless of priority set with \PortTextXRef{15}. So if you need exactly the same colour with priority and non-priority, you will need to set the same data twice, to different indexes, once with priority bit {\tt 1} and then with {\tt 0}.
% note: register $43 is declared on the same page, therefore we omit page number to keep text more readable
After the second write palette colour index in \PortTextXRef{40} is automatically increment, if auto-increment is enabled in \PortTextXRef[]{43}.
Note: reading will always return the second byte of the colour (least significant bit of blue) and will not auto-increment index. You can read {\tt RRRGGGBB} part with \PortTextXRef{41}.
\subsubsection{\PortDeclaration{4A}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{8-bit colour to be used when all layers contain transparent pixel. Format is {\tt RRRGGGBB}}
\end{NextPort}
This colour is also used for paper and border when ULANext full-ink mode is enabled - see \PortTextXRef{42}.
\pagebreak