-
Notifications
You must be signed in to change notification settings - Fork 10
/
chapter-next-layer2.tex
759 lines (602 loc) · 31.8 KB
/
chapter-next-layer2.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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
\section{Layer 2}
\label{zx_next_layer2}
% ───────────────────────────────
% ─██████─────────██████████████─
% ─██░░██─────────██░░░░░░░░░░██─
% ─██░░██─────────██████████░░██─
% ─██░░██─────────────────██░░██─
% ─██░░██─────────██████████░░██─
% ─██░░██─────────██░░░░░░░░░░██─
% ─██░░██─────────██░░██████████─
% ─██░░██─────────██░░██─────────
% ─██░░██████████─██░░██████████─
% ─██░░░░░░░░░░██─██░░░░░░░░░░██─
% ─██████████████─██████████████─
% ───────────────────────────────
As we saw in the previous section, drawing with ULA graphics is much simplified on Next. But it can't eliminate the colour clash. Well, not with ULA mode at least. However, Next brings a couple of brand new graphic modes to the table, hidden behind a somewhat casual name ``Layer 2''. But don't let its name deceive you; Layer 2 raises Next graphics capabilities to a whole new level!
Layer 2 may appear behind or above the ULA layer. It supports different resolutions with every pixel coloured independently and memory organized sequentially, line by line, pixel by pixel. Consequently, Layer 2 requires more memory compared to ULA; each mode needs multiple 16K banks. But of course, Next has far more memory than the original Speccy ever did!
\begin{tabularx}{\textwidth}{cccX}
\BitHead{Resolution} & \BitHead{Colours} & \BitHead{BPP} & \BitHead{Memory Organization} \\
256$\times$192 & 256 & 8 & 48K, 3 horizontal banks of 64 lines \\
320$\times$256 & 256 & 8 & 80K, 5 vertical banks of 64 columns\footnote{Core 3.0.6+ only} \\
640$\times$256 & 16 & 4 & 80K, 5 vertical banks of 128 columns\footnotemark[\value{footnote}] \\
\end{tabularx}
\subsection{Initialization}
Drawing on Layer 2 is much simpler than ULA. But in contrast with ULA, which is always ``on'', Layer 2 needs to be explicitly enabled. This is done by setting bit {\tt 1} of \PortTextXRef{123B}.
By default, Layer 2 will use 256$\times$192 with 256 colours, supported across all Next core versions. You can select another resolution with \PortTextXRef{70}. In this case you will also have to set up clip window correctly with \PortTextXRef{18}.
\subsection{Paging}
After Layer 2 is enabled, we can start writing into memory banks. As mentioned, Layer 2 requires 3-5 contiguous 16K banks. Upon boot, Next assigns it 16K banks 8-10. However, that gets modified by NextZXOS to 9-11 soon afterwards. You can use this configuration, but it's a good idea to set it up manually to future proof our programs.
There are two pieces to the ``puzzle'' of Layer 2 paging: first, we need to tell the hardware which banks are used. Since banks are always contiguous we only need to write the starting 16K bank number into \PortTextXRef{12}. Then we need to swap the banks into one or more slots to write or read the data. Any supported mode can be used for paging, as described in section \XRef{zx_next_memorypaging}. But the recommended and simplest is MMU mode. It's recommended to only use 16K banks 9 or greater for Layer 2.
16K slot 3 (\MemAddr{C000}-\MemAddr{FFFF}, MMU7 and 8) is typically used for Layer 2 banks. But any other slot will work. You can use MMU registers to swap banks. Alternatively \PortTextXRef{123B} also allows setting up paging for Layer 2. Either way, make sure paging is reset before passing control back from Layer 2 handling code.
Similar to ULA, Layer 2 can also be set up to use a double-buffering scheme. \PortTextXRef{13} defines starting 16K bank number for ``shadow screen'' (back buffer) in this case. This is mainly used when paging is set up through \PortTextXRef{123B}; bit 3 is used to switch configuration between normal and shadow banks. Or we can use MMU registers instead. If we track shadow banks manually, we don't have to use register \MemAddr{13} at all. We still need to assign starting shadow screen bank to register \PortTextXRef{12} in order to make it visible on screen.
\subsection{Drawing}
In general, drawing pixels requires the programmer to:
\begin{itemize}[topsep=1pt,itemsep=1pt]
\item Determine and select bank to write to
\item Calculate address of the pixel within the bank
\item Write byte with colour data
\end{itemize}
All Layer 2 modes use the same approach when drawing pixels. Each pixel uses one byte (except 640$\times$320 where each byte contains data for 2 pixels). The value is simply an index into the palette entries list. Similar to other layers, Layer 2 also has two palettes, of which only one can be active at any given time. \PortTextXRef{43} is used to select active palette. See Palette chapter \XRef{zx_next_palette} for details on how to program palettes.
See specific modes in the following pages for examples of writing pixel data.
\subsection{Effects}
\PortTextXRef{15} can be used to change Layer 2 priority, effectively moving Layer 2 above or below other layers - see Tilemap chapter, section \XRef{zx_next_tilemap_registers} for details.
We can even be more specific and only prioritize specific colours, so only pixels using those colours will appear on top while other pixels below other layers. This way we can achieve a simple depth effect. Per-pixel priority is available when writing a custom palette with \PortTextXRef{44} (9-bit colours). See description under Palette chapter, section \XRef{zx_next_palette} for details on how to program palette.
We can also use both Layer 2 palettes to achieve simple effects. For example, certain colours can be marked with the priority flag on one palette but not on the other. When swapping palettes, pixels drawn with these colours would appear on top or below other layers. Another simple effect using both palettes could be colour animation, though it can't be very smooth with only two states.
\PortTextXRef{14} register can be used to alter the transparent colour of Layer 2. This same register also affects ULA, LoRes and 1-bit (``text mode'') tilemap.
Scrolling effects can be achieved by writing pixel offsets to registers \PortTextXRef{71}, \PortTextXRef{16} and \PortTextXRef{17}.
\pagebreak
\subsection{256$\times$192 256 Colour Mode}
\begin{multicols}{2}
3 horizontal banks:
\begin{tabularx}{0.95\linewidth}{c|YY|YY|}
\multicolumn{1}{l}{} &
\multicolumn{1}{l}{0} &
\multicolumn{2}{c}{\dots} &
\multicolumn{1}{r}{255} \\
\cline{2-5}
0 &
\multicolumn{2}{l|}{16K BANK 0} &
\multicolumn{2}{l|}{8K BANK 0} \\
\multirow{2}{*}{\vdots} & & &
\multicolumn{2}{l|}{0\dots 31} \\
\cline{4-5}
& & & \multicolumn{2}{l|}{8K BANK 1} \\
63 & & & \multicolumn{2}{l|}{32 \dots 63} \\
\cline{2-5}
64 &
\multicolumn{2}{l|}{16K BANK 1} &
\multicolumn{2}{l|}{8K BANK 2} \\
\multirow{2}{*}{\vdots} & & &
\multicolumn{2}{l|}{64 \dots 95} \\
\cline{4-5}
& & & \multicolumn{2}{l|}{8K BANK 3} \\
127 & & & \multicolumn{2}{l|}{96 \dots 127} \\
\cline{2-5}
128 &
\multicolumn{2}{l|}{16K BANK 2} &
\multicolumn{2}{l|}{8K BANK 4} \\
\multirow{2}{*}{\vdots} & & &
\multicolumn{2}{l|}{128 \dots 159} \\
\cline{4-5}
& & & \multicolumn{2}{l|}{8K BANK 5} \\
191 & & & \multicolumn{2}{l|}{160 \dots 191} \\
\cline{2-5}
\end{tabularx}
\columnbreak
8BPP:\\
\begin{BitTableByte}
\BitSmall{$I_7$} &
\BitSmall{$I_6$} &
\BitSmall{$I_5$} &
\BitSmall{$I_4$} &
\BitSmall{$I_3$} &
\BitSmall{$I_2$} &
\BitSmall{$I_1$} &
\BitSmall{$I_0$} \\
\hline
\BitStartMulti{8}{Colour index} \\
\end{BitTableByte}
Banking Setup:
\begin{ElegantTableX}{|c|c|c|c|Y|}
\ElegantHeader{
\BitHead{15} &
\BitHead{14} &
\BitHead{13} &
\BitHead{12-8} &
\BitHead{7-0}
}
\BitStartMulti{4}{$Y$} &
\BitMulti{1}{$X$} \\
\hline
\BitStartMulti{2}{16K} &
\BitMulti{2}{$Y_{5-0}$} &
\BitMulti{1}{$X$} \\
\hline
\BitStartMulti{3}{8K} &
\BitMulti{1}{$Y_{4-0}$} &
\BitMulti{1}{$X$} \\
\end{ElegantTableX}
\end{multicols}
This mode is the closest to ULA, resolution wise, so is perhaps the simplest to grasp. It's also supported across all Next core versions. Pixels are laid out from left to right and top to bottom. Each pixel uses one byte that represents an 8-bit index into the palette. 3 16K banks are needed to cover the whole screen, each holding data for 64 lines. Or, if using 8K, 6 banks, 32 lines each. Combined, colour data requires 48K of memory.
Each (x,y) coordinate pair requires 16-bits. If the upper byte is used for Y and lower for the X coordinate, together they will form exact memory location offset from the top of the first bank. But to account for bank swapping; for 16K banks, the most significant 2 bits of Y correspond to bank number and for 8K banks, top 3 bits. The rest of Y + X is memory location within the bank.
Example of filling the screen with a vertical rainbow:
\begin{tcblisting}{}
START_16K_BANK = 9
START_8K_BANK = START_16K_BANK*2
; Enable Layer 2
LD BC, &123B
LD A, 2
OUT (C), A
; Setup starting Layer2 16K bank
NEXTREG &12, START_16K_BANK
LD D, 0 ; D=Y, start at top of the screen
nextY:
; Calculate bank number and swap it in
LD A, D ; Copy current Y to A
AND %11100000 ; 32100000 (3 MSBs = bank number)
RLCA ; 21000003
RLCA ; 10000032
RLCA ; 00000321
ADD A, START_8K_BANK ; A=bank number to swap in
NEXTREG &56, A ; Swap bank to slot 6 (&C000-&DFFF)
; Convert DE (yx) to screen memory location starting at &C000
PUSH DE ; (DE) will be changed to bank offset
LD A, D ; Copy current Y to A
AND %00011111 ; Discard bank number
OR &C0 ; Screen starts at &C000
LD D, A ; D=high byte for &C000 screen memory
; Loop X through 0..255; we don't have to deal with bank swapping
; here because it only occurs when changing Y
LD E, 0
nextX:
LD A, E ; A=current X
LD (DE), A ; Use X as colour index
INC E ; Increment to next X
JR NZ, nextX ; Repeat until E rolls over
; Continue with next line or exit
POP DE ; Restore DE to coordinates
INC D ; Increment to next Y
LD A, D ; A=current Y
CP 192 ; Did we just complete last line?
JP C, nextY ; No, continue with next linee
\end{tcblisting}
Worth noting: MMU page 6 (next register \MemAddr{56}) covers memory \MemAddr{C000} - \MemAddr{DFFF}. As we swap different 8K banks there, we're effectively changing 8K banks that are readable and writable at those memory addresses. That's why we {\tt OR \$C0} in line 24; we need to convert zero based address to \MemAddr{C000} based.
We don't have to handle bank swapping on every iteration; once per 32 rows would do for this example. But the code is more versatile this way and could be easily converted into a reusable pixel setting routine.
You can find fully working example in companion code on GitHub in folder {\tt layer2-256x192}.
\pagebreak
\subsection{320$\times$256 256 Colour Mode}
\begin{multicols}{2}
5 vertical banks:
\begin{tabularx}{0.95\linewidth}{l|X|X|X|X|X|X|X|X|X|X|}
\multicolumn{1}{l}{} &
\multicolumn{1}{l}{0} &
\multicolumn{7}{X}{} &
\multicolumn{2}{r}{319} \\
\cline{2-11}
\rotatebox[origin=c]{90}{~~~~~~~~~~~~~~0} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{~16K BANK 0~}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 1}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 2}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 3}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 4}} \\
\cline{2-11}
\rotatebox[origin=c]{90}{255~~~~~~~~~~~} &
\rotatebox[origin=c]{90}{~8K BANK 0~} &
\rotatebox[origin=c]{90}{8K BANK 1} &
\rotatebox[origin=c]{90}{8K BANK 2} &
\rotatebox[origin=c]{90}{8K BANK 3} &
\rotatebox[origin=c]{90}{8K BANK 4} &
\rotatebox[origin=c]{90}{8K BANK 5} &
\rotatebox[origin=c]{90}{8K BANK 6} &
\rotatebox[origin=c]{90}{8K BANK 7} &
\rotatebox[origin=c]{90}{8K BANK 8} &
\rotatebox[origin=c]{90}{8K BANK 9} \\
\cline{2-11}
\multicolumn{1}{c}{} & \multicolumn{10}{c}{} \\[-5pt]
\multicolumn{1}{c}{} &
\multicolumn{10}{l}{16K bank contains 64 columns} \\
\multicolumn{1}{c}{} &
\multicolumn{10}{l}{8K bank contains 32 columns} \\
\end{tabularx}
\columnbreak
8BPP:\\
\begin{BitTableByte}
\BitSmall{$I_7$} &
\BitSmall{$I_6$} &
\BitSmall{$I_5$} &
\BitSmall{$I_4$} &
\BitSmall{$I_3$} &
\BitSmall{$I_2$} &
\BitSmall{$I_1$} &
\BitSmall{$I_0$} \\
\hline
\BitStartMulti{8}{Colour index} \\
\end{BitTableByte}
Banking Setup:
\begin{ElegantTableX}{|c|C{1em}|C{1em}|C{1em}|C{2.5em}|Y|}
\ElegantHeader{
\BitHead{16} &
\BitMulti{4}{\BitHead{15-8}} &
\BitHead{7-0}
}
\ElegantHeader{
\BitHead{16} &
\BitHead{15} &
\BitHead{14} &
\BitHead{13} &
\BitHead{12-8} &
\BitHead{7-0}
}
\BitStartMulti{1}{$X_{8}$} &
\BitMulti{4}{$X_{7-0}$} &
\BitMulti{1}{$Y$} \\
\hline
\BitStartMulti{3}{16K} &
\BitMulti{2}{$X_{5-0}$} &
\BitMulti{1}{$Y$} \\
\hline
\BitStartMulti{4}{8K} &
\BitMulti{1}{$X_{4-0}$} &
\BitMulti{1}{$Y$} \\
\end{ElegantTableX}
\end{multicols}
320$\times$256 mode is only available on Next core 3.0.6 or later. Pixels are laid out from top to bottom and left to right. Each pixel uses one byte that represents an 8-bit index into the palette. To cover the whole screen, 5 16K banks of 64 columns or 10 8K banks of 32 columns are needed. Together colour data requires 80K of memory.
In contrast with 256$\times$192, this mode allows drawing to the whole screen, including border. In fact, you can think of it as the regular 256$\times$192 mode with additional 32 pixel border around (32 + 256 + 32 = 320 and 32 + 192 + 32 = 256).
Addressing is more complicated though. As we need 9 bits for X and 8 for Y, we can't address all screen pixels with single 16-bit register pair. But we can use 16-bit register pair to address all pixels within each bank. From this perspective, the setup is similar to 256$\times$192 mode, except that X and Y are reversed: if the upper byte is used for X and lower for Y, then most significant 2 bits of 16-bit register pair represent lower 2 bits of 16K bank number. And for 8K banks, the most significant 3 bits correspond to the lower 3 bits of 8K bank number. In either case, the most significant bit of the bank number arrives from the 9th bit of the X coordinate ($X_8$ in the table above). The rest of the X + Y is memory location within the bank.
To use this mode, we must explicitly select it with \PortTextXRef{70}. We must also not forget to set clip window correctly with \PortTextXRef{18} and \PortTextXRef{1C}, as demonstrated in example below:
\begin{tcblisting}{}
START_16K_BANK = 9
START_8K_BANK = START_16K_BANK*2
RESOLUTION_X = 320
RESOLUTION_Y = 256
BANK_8K_SIZE = 8192
NUM_BANKS = RESOLUTION_X * RESOLUTION_Y / BANK_8K_SIZE
BANK_X = BANK_8K_SIZE / RESOLUTION_Y
; Enable Layer 2
LD BC, &123B
LD A, 2
OUT (C), A
; Setup starting Layer2 16K bank
NEXTREG &12, START_16K_BANK
NEXTREG &70, %00010000 ; 320x256 256 colour mode
; Setup window clip for 320x256 resolution
NEXTREG &1C, 1 ; Reset Layer 2 clip window reg index
NEXTREG &18, 0 ; X1; X2 next line
NEXTREG &18, RESOLUTION_X / 2 - 1
NEXTREG &18, 0 ; Y1; Y2 next line
NEXTREG &18, RESOLUTION_Y - 1
LD B, START_8K_BANK ; Bank number
LD H, 0 ; Colour index
nextBank:
; Swap to next bank, exit once all 5 are done
LD A, B ; Copy current bank number to A
NEXTREG &56, A ; Swap bank to slot 6 (&C000-&DFFF)
; Fill in current bank
LD DE, &C000 ; Prepare starting address
nextY:
; Fill in 256 pixels of current line
LD A, H ; Copy colour index to A
LD (DE), A ; Write colour index into memory
INC E ; Increment Y
JR NZ, nextY ; Continue with next Y until we wrap to next X
; Prepare for next line until bank is full
INC H ; Increment colour
INC D ; Increment X
LD A, D ; Copy X to A
AND %00111111 ; Clear &C0 to get pure X coordinate
CP BANK_X ; Did we reach next bank?
JP NZ, nextY ; No, continue with next Y
; Prepare for next bank
INC B ; Increment to next bank
LD A, B ; Copy bank to A
CP START_8K_BANK+NUM_BANKS; Did we fill last bank?
JP NZ, nextBank ; No, proceed with next bank
\end{tcblisting}
You can find fully working example in companion code on GitHub in folder {\tt layer2-320x256}.
\pagebreak
\subsection{640$\times$256 16 Colour Mode}
\begin{multicols}{2}
5 vertical banks:
\begin{tabularx}{0.95\linewidth}{l|X|X|X|X|X|X|X|X|X|X|}
\multicolumn{1}{l}{} &
\multicolumn{1}{l}{0} &
\multicolumn{7}{X}{} &
\multicolumn{2}{r}{639} \\
\cline{2-11}
\rotatebox[origin=c]{90}{~~~~~~~~~~~~~~0} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{~16K BANK 0~}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 1}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 2}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 3}} &
\multicolumn{2}{X|}{\rotatebox[origin=c]{90}{16K BANK 4}} \\
\cline{2-11}
\rotatebox[origin=c]{90}{255~~~~~~~~~~~} &
\rotatebox[origin=c]{90}{~8K BANK 0~} &
\rotatebox[origin=c]{90}{8K BANK 1} &
\rotatebox[origin=c]{90}{8K BANK 2} &
\rotatebox[origin=c]{90}{8K BANK 3} &
\rotatebox[origin=c]{90}{8K BANK 4} &
\rotatebox[origin=c]{90}{8K BANK 5} &
\rotatebox[origin=c]{90}{8K BANK 6} &
\rotatebox[origin=c]{90}{8K BANK 7} &
\rotatebox[origin=c]{90}{8K BANK 8} &
\rotatebox[origin=c]{90}{8K BANK 9} \\
\cline{2-11}
\multicolumn{1}{c}{} & \multicolumn{10}{c}{} \\[-5pt]
\multicolumn{1}{c}{} &
\multicolumn{10}{l}{16K bank contains 128 columns} \\
\multicolumn{1}{c}{} &
\multicolumn{10}{l}{8K bank contains 64 columns} \\
\end{tabularx}
\columnbreak
4BPP:\\
\begin{BitTableByte}
\BitSmall{$I_3$} &
\BitSmall{$I_2$} &
\BitSmall{$I_1$} &
\BitSmall{$I_0$} &
\BitSmall{$I_3$} &
\BitSmall{$I_2$} &
\BitSmall{$I_1$} &
\BitSmall{$I_0$} \\
\hline
\BitStartMulti{4}{Colour 1} &
\BitMulti{4}{Colour 2} \\
\end{BitTableByte}
Banking Setup:
\begin{ElegantTableX}{|c|C{1em}|C{1em}|C{1em}|c|Y|}
\ElegantHeader{
\BitHead{16} &
\BitMulti{4}{\BitHead{15-8}} &
\BitHead{7-0}
}
\ElegantHeader{
\BitHead{16} &
\BitHead{15} &
\BitHead{14} &
\BitHead{13} &
\BitHead{12-8} &
\BitHead{7-0}
}
\BitStartMulti{1}{$X_{8}$} &
\BitMulti{4}{$X_{7-0}$} &
\BitMulti{1}{$Y$} \\
\hline
\BitStartMulti{3}{16K} &
\BitMulti{2}{$X_{5-0}$} &
\BitMulti{1}{$Y$} \\
\hline
\BitStartMulti{4}{8K} &
\BitMulti{1}{$X_{4-0}$} &
\BitMulti{1}{$Y$} \\
\end{ElegantTableX}
\end{multicols}
640$\times$256 mode is very similar to 320$\times$256, except that each byte represents 2 colours instead of 1. It's also available on Next core 3.0.6 or later only. Pixels are laid out from top to bottom and left to right. Each pixel takes 4 bits, so each byte contains data for 2 pixels. Therefore division by 2 should be used to convert screen coordinate to $X$ for address, multiplication by 2 for the other way around.
To cover the whole screen, 5 16K banks of 128 columns or 10 8K banks of 64 columns are needed. Together colour data requires 80K of memory. Similar to 320$\times$256, this mode also covers the whole screen, including the border.
Addressing wise, this mode is the same as 320$\times$256. Using 16-bit register pair we can't address all pixels on the screen, but we can address all pixels within each bank. Again, assuming upper byte of 16-bit register pair is used for X and lower for Y and using 9th bit of X coordinate (bit $X_8$ in the table above) as the most significant bit of bank number, then most significant 2 bits of 16-bit register pair represent lower 2 bits of 16K bank number. And for 8K banks, the most significant 3 bits correspond to the lower 3 bits of 8K bank number. The rest of the X + Y is memory location within the bank. Don't forget: each colour byte represents 2 screen pixels, so the memory X coordinate (as described above) needs to be multiplied by 2 to convert to screen X coordinate.
To use this mode, we must explicitly select it with \PortTextXRef{70}. We must also not forget to set clip window correctly with \PortTextXRef{18} and \PortTextXRef{1C}, as demonstrated in example below:
\begin{tcblisting}{}
START_16K_BANK = 9
START_8K_BANK = START_16K_BANK*2
RESOLUTION_X = 640
RESOLUTION_Y = 256
BANK_8K_SIZE = 8192
NUM_BANKS = RESOLUTION_X * RESOLUTION_Y / BANK_8K_SIZE / 2
BANK_X = BANK_8K_SIZE / RESOLUTION_Y
; Enable Layer 2
LD BC, &123B
LD A, 2
OUT (C), A
; Setup starting Layer2 16K bank
NEXTREG &12, START_16K_BANK
NEXTREG &70, %00100000 ; 640x256 16 colour mode
NEXTREG &1C, 1 ; Reset Layer 2 clip window reg index
NEXTREG &18, 0
NEXTREG &18, RESOLUTION_X / 4 - 1
NEXTREG &18, 0
NEXTREG &18, RESOLUTION_Y - 1
LD B, START_8K_BANK ; Bank number
LD H, 0 ; Colour index for 2 pixels
nextBank:
; Swap to next bank, exit once all 5 are done
LD A, B ; Copy current bank number to A
NEXTREG &56, A ; Swap bank to slot 6 (&C000-&DFFF)
; Fill in current bank
LD DE, &C000 ; Prepare starting address
nextY:
; Fill in 256 pixels of current line
LD A, H ; Copy colour indexes for 2 pixels to A
LD (DE), A ; Write colour indexes into memory
INC E ; Increment Y
JR NZ, nextY ; Continue with next Y until we wrap to next X
; Prepare for next line until bank is full
INC H ; Increment colour index for both colours
INC D ; Increment X
LD A, D ; Copy X to A
AND %00111111 ; Clear &C0 to get pure X coordinate
CP BANK_X ; Did we reach next bank?
JP NZ, nextY ; No, continue with next Y
; Prepare for next bank
INC B ; Increment to next bank
LD A, B ; Copy bank to A
CP START_8K_BANK+NUM_BANKS; Did we fill last bank?
JP NZ, nextBank ; No, proceed with next bank
\end{tcblisting}
You can find fully working example in companion code on GitHub in folder {\tt layer2-640x256}.
\subsection{Layer 2 Registers}
\label{zx_next_layer2_registers}
\subsubsection{\PortDeclaration{123B}}
% note: most port xrefs exclude page numbers since those ports/registers are declared on the same page spread
\begin{NextPort}
\PortBits{7-6}
\PortDesc{Video RAM bank select}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{00}{First 16K of layer 2 in the bottom 16K slot}
\PortBitLine{01}{Second 16K of layer 2 in the bottom 16K slot}
\PortBitLine{10}{Third 16K of layer 2 in the bottom 16K slot}
\PortBitLine{11}{First 48K of layer 2 in the bottom 48K - 16K slots 0-2 (core 3.0+)}
\end{PortBitConfig}
}
\PortBits{5}
\PortDesc{Reserved, use {\tt 0}}
\PortBits{4}
\PortDesc{{\tt 0} (see below)}
\PortBits{3}
\PortDesc{Use Shadow Layer 2 for paging}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{0}{Map \PortTextXRef[]{12}}
\PortBitLine{1}{Map \PortTextXRef[]{13}}
\end{PortBitConfig}
}
\PortBits{2}
\PortDesc{Enable Layer 2 read-only paging on 16K slot 0 (core 3.0+)}
\PortBits{1}
\PortDesc{Layer 2 visible, see \PortTextXRef[]{12}}
\PortDescOnly{Since core 3.0 this bit has mirror in \PortTextXRef{69}}
\PortBits{0}
\PortDesc{Enable Layer 2 write-only paging on 16K slot 0}
\end{NextPort}
Note: bits 0 and 2 can be combined to get both read and write access to selected bank(s). If bit 3 is set, then paging uses shadow screen banks instead.
Since core 3.0.7, write with bit {\tt 4} set was also added:
\begin{NextPort}
\PortBits{7-5}
\PortDesc{Reserved, use {\tt 0}}
\PortBits{4}
\PortDesc{{\tt 1}}
\PortBits{3}
\PortDesc{Reserved, use {\tt 0}}
\PortBits{2-0}
\PortDesc{16K bank relative offset (+0..+7) applied to Layer 2 memory mapping}
\end{NextPort}
With this, all 5 banks needed for 320$\times$256 and 640$\times$256 modes can be selected for reading or writing to 16K slot 0 (or first three slots). To use this mode, port \MemAddr{123B} is typically written to twice, first without bit 4 to switch on read/write access, then with bit 4 set to select bank offset.
Note: read and write access to Layer 2 banks (or any other bank for that matter) can also be achieved using Next MMU registers which is arguably simpler to use. Regardless, don't forget to reset banks back to the original after you're done handling Layer 2!
\subsubsection{\PortDeclaration{12}}
\begin{NextPort}
\PortBits{7}
\PortDesc{Reserved, must be {\tt 0}}
\PortBits{6-0}
\PortDesc{Starting 16K bank of Layer 2}
\end{NextPort}
Default 256$\times$192 mode requires 3 16K banks while new, 320$\times$256 and 640$\times$256 modes require 5 16K banks. Banks need to be contiguous in memory, so here we only specify the first one. Valid bank numbers are therefore {\tt 0} - {\tt 45} ({\tt 109} for 2MB RAM models) for standard mode and {\tt 0} - {\tt 43} ({\tt 107} for 2MB RAM models) for new modes.
Changes to this registers are immediately visible on screen.
Note: this register uses 16K bank numbers. If you're using 8K banks, you have to multiply this value by 2. For example, 16K bank 9 corresponds to 8K banks 18 and 19.
\subsubsection{\PortDeclaration{13}}
% note: most port xrefs exclude page numbers since those ports/registers are declared on the same page spread
\begin{NextPort}
\PortBits{7}
\PortDesc{Reserved, must be {\tt 0}}
\PortBits{6-0}
\PortDesc{Starting 16K bank of Layer 2 shadow screen}
\end{NextPort}
Similar to \PortTextXRef[]{12} except this register sets up starting 16K bank for Layer 2 shadow screen. The other difference is that changes to this registers are not immediately visible on screen.
Note: this register doesn't affect the shadow screen in any way besides when bit 3 is set in \PortTextXRef[]{123B}. We can circumvent it completely if a manual paging scheme is used to swap banks for reading and writing.
\subsubsection{\PortDeclaration{14}}
\begin{NextPort}
\PortBits{7-0}\PortDesc{Sets index of transparent colour for Layer 2, ULA and LoRes pixel data (\MemAddr{E3} after reset).}
\end{NextPort}
\subsubsection{\PortDeclaration{16}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Writes or reads X pixel offset used for drawing Layer 2 graphics on the screen.}
\end{NextPort}
This can be used for creating scrolling effects. For 320$\times$256 and 640$\times$256 modes, 9 bits are required; use \PortTextXRef{71} to set it up.
\pagebreak
\subsubsection{\PortDeclaration{17}}
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Writes or reads Y pixel offset used for drawing Layer 2 graphics on the screen.}
\end{NextPort}
Valid range is:
\begin{itemize}[topsep=1pt,itemsep=1pt]
\item 256$\times$192: {\tt 191}
\item 320$\times$256: {\tt 255}
\item 640$\times$256: {\tt 255}
\end{itemize}
\subsubsection{\PortDeclaration{18}}
% note: most port xrefs exclude page numbers since those ports/registers are declared on the same page spread
\begin{NextPort}
\PortBits{7-0}
\PortDesc{Reads and writes clip-window coordinates for Layer 2}
\end{NextPort}
4 coordinates need to be set: X1, X2, Y1 and Y2. Which coordinate gets set, is determined by index. As each write to this register will also increment index, the usual flow is to reset the index to {\tt 0} in \PortTextXRef[]{1C}, then write all 4 coordinates in succession. Positions are inclusive. Furthermore, X positions are doubled for 320$\times$256 mode, quadrupled for 640$\times$256. Therefore, to view the whole of Layer 2, the values are:
\begin{tabular}{cllll}
& &
\BitHead{256$\times$192} &
\BitHead{320$\times$256} &
\BitHead{640$\times$256} \\
0 & X1 position & \BitMono{0} & \BitMono{0} & \BitMono{0} \\
1 & X2 position & \BitMono{255} & \BitMono{159} & \BitMono{159} \\
2 & Y1 position & \BitMono{0} & \BitMono{0} & \BitMono{0} \\
3 & Y2 position & \BitMono{191} & \BitMono{255} & \BitMono{255} \\
\end{tabular}
\subsubsection{\PortDeclaration{1C}}
Write:
\begin{NextPort}
\PortBits{7-4}
\PortDesc{Reserved, must be {\tt 0}}
\PortBits{3}
\PortDesc{{\tt 1} to reset Tilemap clip-window register index}
\PortBits{2}
\PortDesc{{\tt 1} to reset ULA/LoRes clip-window register index}
\PortBits{1}
\PortDesc{{\tt 1} to reset Sprite clip-window register index}
\PortBits{0}
\PortDesc{{\tt 1} to reset Layer 2 clip-window register index}
\end{NextPort}
Read:
\begin{NextPort}
\PortBits{7-6}
\PortDesc{Current Tilemap clip-window register index}
\PortBits{5-4}
\PortDesc{Current ULA/LoRes clip-window register index}
\PortBits{3-2}
\PortDesc{Current Sprite clip-window register index}
\PortBits{1-0}
\PortDesc{Current Layer 2 clip-window register index}
\end{NextPort}
\subsubsection{\PortTextXRef[]{40}}
\vspace*{-2ex}
\subsubsection{\PortTextXRef[]{41}}
\vspace*{-2ex}
\subsubsection{\PortTextXRef[]{43}}
\vspace*{-2ex}
\subsubsection{\PortTextXRef[]{44}}
\PortDeclarationXRefMultiple{Palette}{40}{44}
\subsubsection{\PortDeclaration{69}}
% note: we use comma for referencing port declaration page to avoid double closing parenthesis, more readable this way
\begin{NextPort}
\PortBits{7}
\PortDesc{{\tt 1} to enable Layer 2 (alias for bit 1 in \PortTextXRef[,]{123B})}
\PortBits{6}
\PortDesc{{\tt 1} to enable ULA shadow display (alias for bit 3 in \PortTextXRef[,]{7FFD})}
\PortBits{5-0}
\PortDesc{Alias for bits 5-0 in Timex Sinclair Video Mode Control \MemAddr{xxFF}}
\end{NextPort}
ULA shadow screen from Bank 7 has higher priority than Timex modes.
\subsubsection{\PortDeclaration{70}}
\begin{NextPort}
\PortBits{7-6}
\PortDesc{Reserved, must be {\tt 0}}
\PortBits{5-4}
\PortDesc{Layer 2 resolution ({\tt 0} after soft reset)}
\PortDescOnly{
\begin{PortBitConfig}
\PortBitLine{00}{256$\times$192, 8BPP}
\PortBitLine{01}{320$\times$256, 8BPP}
\PortBitLine{10}{640$\times$256, 4BPP}
\end{PortBitConfig}
}
\PortBits{3-0}
\PortDesc{Palette offset ({\tt 0} after soft reset)}
\end{NextPort}
\subsubsection{\PortDeclaration{71}}
\begin{NextPort}
\PortBits{7-1}
\PortDesc{Reserved, must be {\tt 0}}
\PortBits{0}
\PortDesc{MSB for X pixel offset}
\end{NextPort}
This is only used for 320$\times$256 and 640$\times$256 modes. Together with \PortTextXRef{16} full 319 pixels offsets are available. For 640$\times$256 only 2 pixel offsets are possible.
\pagebreak