openMSX
CharacterConverter.cc
Go to the documentation of this file.
1 /*
2 TODO:
3 - Clean up renderGraphics2, it is currently very hard to understand
4  with all the masks and quarters etc.
5 - Try using a generic inlined pattern-to-pixels converter.
6 - Correctly implement vertical scroll in text modes.
7  Can be implemented by reordering blitting, but uses a smaller
8  wrap than GFX modes: 8 lines instead of 256 lines.
9 */
10 
11 #include "CharacterConverter.hh"
12 #include "VDP.hh"
13 #include "VDPVRAM.hh"
14 #include "unreachable.hh"
15 #include "build-info.hh"
16 #include "components.hh"
17 #include <cstdint>
18 
19 namespace openmsx {
20 
21 template <class Pixel>
23  VDP& vdp_, const Pixel* palFg_, const Pixel* palBg_)
24  : vdp(vdp_), vram(vdp.getVRAM()), palFg(palFg_), palBg(palBg_)
25 {
26  modeBase = 0; // not strictly needed, but avoids Coverity warning
27 }
28 
29 template <class Pixel>
31 {
32  modeBase = mode.getBase();
33  assert(modeBase < 0x0C);
34 }
35 
36 template <class Pixel>
38 {
39  switch (modeBase) {
40  // M5 M4 = 0 0 (MSX1 modes)
41  case 0: renderGraphic1(linePtr, line); break;
42  case 1: renderText1 (linePtr, line); break;
43  case 2: renderMulti (linePtr, line); break;
44  case 3: renderBogus (linePtr); break;
45  case 4: renderGraphic2(linePtr, line); break;
46  case 5: renderText1Q (linePtr, line); break;
47  case 6: renderMultiQ (linePtr, line); break;
48  case 7: renderBogus (linePtr); break;
49  // M5 M4 = 0 1
50  case 8: renderGraphic2(linePtr, line); break; // graphic 3, actually
51  case 9: renderText2 (linePtr, line); break;
52  case 10: renderBogus (linePtr); break;
53  case 11: renderBogus (linePtr); break;
54  default: UNREACHABLE;
55  }
56 }
57 
58 template <class Pixel>
60  Pixel* __restrict pixelPtr, int line)
61 {
62  Pixel fg = palFg[vdp.getForegroundColor()];
63  Pixel bg = palFg[vdp.getBackgroundColor()];
64 
65  // 8 * 256 is small enough to always be contiguous
66  const byte* patternArea = vram.patternTable.getReadArea(0, 256 * 8);
67  patternArea += (line + vdp.getVerticalScroll()) & 7;
68 
69  // Note: Because line width is not a power of two, reading an entire line
70  // from a VRAM pointer returned by readArea will not wrap the index
71  // correctly. Therefore we read one character at a time.
72  unsigned nameStart = (line / 8) * 40;
73  unsigned nameEnd = nameStart + 40;
74  for (unsigned name = nameStart; name < nameEnd; ++name) {
75  unsigned charcode = vram.nameTable.readNP((name + 0xC00) | (~0u << 12));
76  unsigned pattern = patternArea[charcode * 8];
77  pixelPtr[0] = (pattern & 0x80) ? fg : bg;
78  pixelPtr[1] = (pattern & 0x40) ? fg : bg;
79  pixelPtr[2] = (pattern & 0x20) ? fg : bg;
80  pixelPtr[3] = (pattern & 0x10) ? fg : bg;
81  pixelPtr[4] = (pattern & 0x08) ? fg : bg;
82  pixelPtr[5] = (pattern & 0x04) ? fg : bg;
83  pixelPtr += 6;
84  }
85 }
86 
87 template <class Pixel>
88 void CharacterConverter<Pixel>::renderText1Q(
89  Pixel* __restrict pixelPtr, int line)
90 {
91  Pixel fg = palFg[vdp.getForegroundColor()];
92  Pixel bg = palFg[vdp.getBackgroundColor()];
93 
94  unsigned patternBaseLine = (~0u << 13) | ((line + vdp.getVerticalScroll()) & 7);
95 
96  // Note: Because line width is not a power of two, reading an entire line
97  // from a VRAM pointer returned by readArea will not wrap the index
98  // correctly. Therefore we read one character at a time.
99  unsigned nameStart = (line / 8) * 40;
100  unsigned nameEnd = nameStart + 40;
101  unsigned patternQuarter = (line & 0xC0) << 2;
102  for (unsigned name = nameStart; name < nameEnd; ++name) {
103  unsigned charcode = vram.nameTable.readNP((name + 0xC00) | (~0u << 12));
104  unsigned patternNr = patternQuarter | charcode;
105  unsigned pattern = vram.patternTable.readNP(
106  patternBaseLine | (patternNr * 8));
107  pixelPtr[0] = (pattern & 0x80) ? fg : bg;
108  pixelPtr[1] = (pattern & 0x40) ? fg : bg;
109  pixelPtr[2] = (pattern & 0x20) ? fg : bg;
110  pixelPtr[3] = (pattern & 0x10) ? fg : bg;
111  pixelPtr[4] = (pattern & 0x08) ? fg : bg;
112  pixelPtr[5] = (pattern & 0x04) ? fg : bg;
113  pixelPtr += 6;
114  }
115 }
116 
117 template <class Pixel>
118 void CharacterConverter<Pixel>::renderText2(
119  Pixel* __restrict pixelPtr, int line)
120 {
121  Pixel plainFg = palFg[vdp.getForegroundColor()];
122  Pixel plainBg = palFg[vdp.getBackgroundColor()];
123  Pixel blinkFg, blinkBg;
124  if (vdp.getBlinkState()) {
125  int fg = vdp.getBlinkForegroundColor();
126  blinkFg = palBg[fg ? fg : vdp.getBlinkBackgroundColor()];
127  blinkBg = palBg[vdp.getBlinkBackgroundColor()];
128  } else {
129  blinkFg = plainFg;
130  blinkBg = plainBg;
131  }
132 
133  // 8 * 256 is small enough to always be contiguous
134  const byte* patternArea = vram.patternTable.getReadArea(0, 256 * 8);
135  patternArea += (line + vdp.getVerticalScroll()) & 7;
136 
137  unsigned colorStart = (line / 8) * (80 / 8);
138  unsigned nameStart = (line / 8) * 80;
139  for (unsigned i = 0; i < (80 / 8); ++i) {
140  unsigned colorPattern = vram.colorTable.readNP(
141  (colorStart + i) | (~0u << 9));
142  const byte* nameArea = vram.nameTable.getReadArea(
143  (nameStart + 8 * i) | (~0u << 12), 8);
144 
145  Pixel fg0 = (colorPattern & 0x80) ? blinkFg : plainFg;
146  Pixel bg0 = (colorPattern & 0x80) ? blinkBg : plainBg;
147  unsigned pattern0 = patternArea[nameArea[0] * 8];
148  pixelPtr[ 0] = (pattern0 & 0x80) ? fg0 : bg0;
149  pixelPtr[ 1] = (pattern0 & 0x40) ? fg0 : bg0;
150  pixelPtr[ 2] = (pattern0 & 0x20) ? fg0 : bg0;
151  pixelPtr[ 3] = (pattern0 & 0x10) ? fg0 : bg0;
152  pixelPtr[ 4] = (pattern0 & 0x08) ? fg0 : bg0;
153  pixelPtr[ 5] = (pattern0 & 0x04) ? fg0 : bg0;
154 
155  Pixel fg1 = (colorPattern & 0x40) ? blinkFg : plainFg;
156  Pixel bg1 = (colorPattern & 0x40) ? blinkBg : plainBg;
157  unsigned pattern1 = patternArea[nameArea[1] * 8];
158  pixelPtr[ 6] = (pattern1 & 0x80) ? fg1 : bg1;
159  pixelPtr[ 7] = (pattern1 & 0x40) ? fg1 : bg1;
160  pixelPtr[ 8] = (pattern1 & 0x20) ? fg1 : bg1;
161  pixelPtr[ 9] = (pattern1 & 0x10) ? fg1 : bg1;
162  pixelPtr[10] = (pattern1 & 0x08) ? fg1 : bg1;
163  pixelPtr[11] = (pattern1 & 0x04) ? fg1 : bg1;
164 
165  Pixel fg2 = (colorPattern & 0x20) ? blinkFg : plainFg;
166  Pixel bg2 = (colorPattern & 0x20) ? blinkBg : plainBg;
167  unsigned pattern2 = patternArea[nameArea[2] * 8];
168  pixelPtr[12] = (pattern2 & 0x80) ? fg2 : bg2;
169  pixelPtr[13] = (pattern2 & 0x40) ? fg2 : bg2;
170  pixelPtr[14] = (pattern2 & 0x20) ? fg2 : bg2;
171  pixelPtr[15] = (pattern2 & 0x10) ? fg2 : bg2;
172  pixelPtr[16] = (pattern2 & 0x08) ? fg2 : bg2;
173  pixelPtr[17] = (pattern2 & 0x04) ? fg2 : bg2;
174 
175  Pixel fg3 = (colorPattern & 0x10) ? blinkFg : plainFg;
176  Pixel bg3 = (colorPattern & 0x10) ? blinkBg : plainBg;
177  unsigned pattern3 = patternArea[nameArea[3] * 8];
178  pixelPtr[18] = (pattern3 & 0x80) ? fg3 : bg3;
179  pixelPtr[19] = (pattern3 & 0x40) ? fg3 : bg3;
180  pixelPtr[20] = (pattern3 & 0x20) ? fg3 : bg3;
181  pixelPtr[21] = (pattern3 & 0x10) ? fg3 : bg3;
182  pixelPtr[22] = (pattern3 & 0x08) ? fg3 : bg3;
183  pixelPtr[23] = (pattern3 & 0x04) ? fg3 : bg3;
184 
185  Pixel fg4 = (colorPattern & 0x08) ? blinkFg : plainFg;
186  Pixel bg4 = (colorPattern & 0x08) ? blinkBg : plainBg;
187  unsigned pattern4 = patternArea[nameArea[4] * 8];
188  pixelPtr[24] = (pattern4 & 0x80) ? fg4 : bg4;
189  pixelPtr[25] = (pattern4 & 0x40) ? fg4 : bg4;
190  pixelPtr[26] = (pattern4 & 0x20) ? fg4 : bg4;
191  pixelPtr[27] = (pattern4 & 0x10) ? fg4 : bg4;
192  pixelPtr[28] = (pattern4 & 0x08) ? fg4 : bg4;
193  pixelPtr[29] = (pattern4 & 0x04) ? fg4 : bg4;
194 
195  Pixel fg5 = (colorPattern & 0x04) ? blinkFg : plainFg;
196  Pixel bg5 = (colorPattern & 0x04) ? blinkBg : plainBg;
197  unsigned pattern5 = patternArea[nameArea[5] * 8];
198  pixelPtr[30] = (pattern5 & 0x80) ? fg5 : bg5;
199  pixelPtr[31] = (pattern5 & 0x40) ? fg5 : bg5;
200  pixelPtr[32] = (pattern5 & 0x20) ? fg5 : bg5;
201  pixelPtr[33] = (pattern5 & 0x10) ? fg5 : bg5;
202  pixelPtr[34] = (pattern5 & 0x08) ? fg5 : bg5;
203  pixelPtr[35] = (pattern5 & 0x04) ? fg5 : bg5;
204 
205  Pixel fg6 = (colorPattern & 0x02) ? blinkFg : plainFg;
206  Pixel bg6 = (colorPattern & 0x02) ? blinkBg : plainBg;
207  unsigned pattern6 = patternArea[nameArea[6] * 8];
208  pixelPtr[36] = (pattern6 & 0x80) ? fg6 : bg6;
209  pixelPtr[37] = (pattern6 & 0x40) ? fg6 : bg6;
210  pixelPtr[38] = (pattern6 & 0x20) ? fg6 : bg6;
211  pixelPtr[39] = (pattern6 & 0x10) ? fg6 : bg6;
212  pixelPtr[40] = (pattern6 & 0x08) ? fg6 : bg6;
213  pixelPtr[41] = (pattern6 & 0x04) ? fg6 : bg6;
214 
215  Pixel fg7 = (colorPattern & 0x01) ? blinkFg : plainFg;
216  Pixel bg7 = (colorPattern & 0x01) ? blinkBg : plainBg;
217  unsigned pattern7 = patternArea[nameArea[7] * 8];
218  pixelPtr[42] = (pattern7 & 0x80) ? fg7 : bg7;
219  pixelPtr[43] = (pattern7 & 0x40) ? fg7 : bg7;
220  pixelPtr[44] = (pattern7 & 0x20) ? fg7 : bg7;
221  pixelPtr[45] = (pattern7 & 0x10) ? fg7 : bg7;
222  pixelPtr[46] = (pattern7 & 0x08) ? fg7 : bg7;
223  pixelPtr[47] = (pattern7 & 0x04) ? fg7 : bg7;
224 
225  pixelPtr += 48;
226  }
227 }
228 
229 template <class Pixel>
230 const byte* CharacterConverter<Pixel>::getNamePtr(int line, int scroll)
231 {
232  // no need to test whether multi-page scrolling is enabled,
233  // indexMask in the nameTable already takes care of it
234  return vram.nameTable.getReadArea(
235  ((line / 8) * 32) | ((scroll & 0x20) ? 0x8000 : 0), 32);
236 }
237 template <class Pixel>
238 void CharacterConverter<Pixel>::renderGraphic1(
239  Pixel* __restrict pixelPtr, int line)
240 {
241  const byte* patternArea = vram.patternTable.getReadArea(0, 256 * 8);
242  patternArea += line & 7;
243  const byte* colorArea = vram.colorTable.getReadArea(0, 256 / 8);
244 
245  int scroll = vdp.getHorizontalScrollHigh();
246  const byte* namePtr = getNamePtr(line, scroll);
247  for (unsigned n = 0; n < 32; ++n) {
248  unsigned charcode = namePtr[scroll & 0x1F];
249  unsigned color = colorArea[charcode / 8];
250  Pixel fg = palFg[color >> 4];
251  Pixel bg = palFg[color & 0x0F];
252 
253  unsigned pattern = patternArea[charcode * 8];
254  pixelPtr[0] = (pattern & 0x80) ? fg : bg;
255  pixelPtr[1] = (pattern & 0x40) ? fg : bg;
256  pixelPtr[2] = (pattern & 0x20) ? fg : bg;
257  pixelPtr[3] = (pattern & 0x10) ? fg : bg;
258  pixelPtr[4] = (pattern & 0x08) ? fg : bg;
259  pixelPtr[5] = (pattern & 0x04) ? fg : bg;
260  pixelPtr[6] = (pattern & 0x02) ? fg : bg;
261  pixelPtr[7] = (pattern & 0x01) ? fg : bg;
262  pixelPtr += 8;
263  if (!(++scroll & 0x1F)) namePtr = getNamePtr(line, scroll);
264  }
265 }
266 
267 template <class Pixel>
268 void CharacterConverter<Pixel>::renderGraphic2(
269  Pixel* __restrict pixelPtr, int line)
270 {
271 #ifdef __arm__
272  bool misAligned =
273  sizeof(Pixel) == 2 && (reinterpret_cast<uintptr_t>(pixelPtr) & 3);
274  if (misAligned) pixelPtr--;
275  unsigned partial = *pixelPtr;
276 #endif
277  int quarter = ((line / 8) * 32) & ~0xFF;
278  int baseLine = (~0u << 13) | (quarter * 8) | (line & 7);
279 
280  // pattern area is contiguous, color area not
281  const byte* patternArea = vram.patternTable.getReadArea(quarter * 8, 8 * 256);
282  patternArea += line & 7;
283 
284  int scroll = vdp.getHorizontalScrollHigh();
285  const byte* namePtr = getNamePtr(line, scroll);
286 
287  for (unsigned n = 0; n < 32; ++n) {
288  unsigned charCode8 = namePtr[scroll & 0x1F] * 8;
289  unsigned pattern = patternArea[charCode8];
290  unsigned index = charCode8 | baseLine;
291  unsigned color = vram.colorTable.readNP(index);
292  Pixel fg = palFg[color >> 4];
293  Pixel bg = palFg[color & 0x0F];
294 #ifdef __arm__
295  if (sizeof(Pixel) == 2) {
296  if (misAligned) {
297  asm volatile (
298  "mov r0,%[PART]\n\t"
299  "tst %[PAT],#128\n\t"
300  "ite eq\n\t"
301  "orreq r0,r0,%[BG], lsl #16\n\t"
302  "orrne r0,r0,%[FG], lsl #16\n\t"
303  "tst %[PAT],#64\n\t"
304  "ite eq\n\t"
305  "moveq r1,%[BG]\n\t"
306  "movne r1,%[FG]\n\t"
307  "tst %[PAT],#32\n\t"
308  "ite eq\n\t"
309  "orreq r1,r1,%[BG], lsl #16\n\t"
310  "orrne r1,r1,%[FG], lsl #16\n\t"
311  "tst %[PAT],#16\n\t"
312  "ite eq\n\t"
313  "moveq r2,%[BG]\n\t"
314  "movne r2,%[FG]\n\t"
315  "tst %[PAT],#8\n\t"
316  "ite eq\n\t"
317  "orreq r2,r2,%[BG], lsl #16\n\t"
318  "orrne r2,r2,%[FG], lsl #16\n\t"
319  "tst %[PAT],#4\n\t"
320  "ite eq\n\t"
321  "moveq r3,%[BG]\n\t"
322  "movne r3,%[FG]\n\t"
323  "tst %[PAT],#2\n\t"
324  "ite eq\n\t"
325  "orreq r3,r3,%[BG], lsl #16\n\t"
326  "orrne r3,r3,%[FG], lsl #16\n\t"
327  "tst %[PAT],#1\n\t"
328  "ite eq\n\t"
329  "moveq %[PART],%[BG]\n\t"
330  "movne %[PART],%[FG]\n\t"
331  "stmia %[OUT]!,{r0-r3}\n\t"
332  : [OUT] "=r" (pixelPtr)
333  , [PART] "=r" (partial)
334  : "[OUT]" (pixelPtr)
335  , "[PART]" (partial)
336  , [PAT] "r" (pattern)
337  , [FG] "r" (unsigned(fg))
338  , [BG] "r" (unsigned(bg))
339  : "r0","r1","r2","r3","memory"
340  );
341  } else {
342  asm volatile (
343  "tst %[PAT],#128\n\t"
344  "ite eq\n\t"
345  "moveq r0,%[BG]\n\t"
346  "movne r0,%[FG]\n\t"
347  "tst %[PAT],#64\n\t"
348  "ite eq\n\t"
349  "orreq r0,r0,%[BG], lsl #16\n\t"
350  "orrne r0,r0,%[FG], lsl #16\n\t"
351  "tst %[PAT],#32\n\t"
352  "ite eq\n\t"
353  "moveq r1,%[BG]\n\t"
354  "movne r1,%[FG]\n\t"
355  "tst %[PAT],#16\n\t"
356  "ite eq\n\t"
357  "orreq r1,r1,%[BG], lsl #16\n\t"
358  "orrne r1,r1,%[FG], lsl #16\n\t"
359  "tst %[PAT],#8\n\t"
360  "ite eq\n\t"
361  "moveq r2,%[BG]\n\t"
362  "movne r2,%[FG]\n\t"
363  "tst %[PAT],#4\n\t"
364  "ite eq\n\t"
365  "orreq r2,r2,%[BG], lsl #16\n\t"
366  "orrne r2,r2,%[FG], lsl #16\n\t"
367  "tst %[PAT],#2\n\t"
368  "ite eq\n\t"
369  "moveq r3,%[BG]\n\t"
370  "movne r3,%[FG]\n\t"
371  "tst %[PAT],#1\n\t"
372  "ite eq\n\t"
373  "orreq r3,r3,%[BG], lsl #16\n\t"
374  "orrne r3,r3,%[FG], lsl #16\n\t"
375  "stmia %[OUT]!,{r0-r3}\n\t"
376 
377  : [OUT] "=r" (pixelPtr)
378  : "[OUT]" (pixelPtr)
379  , [PAT] "r" (pattern)
380  , [FG] "r" (unsigned(fg))
381  , [BG] "r" (unsigned(bg))
382  : "r0","r1","r2","r3","memory"
383  );
384  }
385  } else {
386 #endif
387  pixelPtr[0] = (pattern & 0x80) ? fg : bg;
388  pixelPtr[1] = (pattern & 0x40) ? fg : bg;
389  pixelPtr[2] = (pattern & 0x20) ? fg : bg;
390  pixelPtr[3] = (pattern & 0x10) ? fg : bg;
391  pixelPtr[4] = (pattern & 0x08) ? fg : bg;
392  pixelPtr[5] = (pattern & 0x04) ? fg : bg;
393  pixelPtr[6] = (pattern & 0x02) ? fg : bg;
394  pixelPtr[7] = (pattern & 0x01) ? fg : bg;
395  pixelPtr += 8;
396 #ifdef __arm__
397  }
398 #endif
399  if (!(++scroll & 0x1F)) namePtr = getNamePtr(line, scroll);
400  }
401 #ifdef __arm__
402  if (misAligned) {
403  *pixelPtr = static_cast<Pixel>(partial);
404  }
405 #endif
406 }
407 
408 template <class Pixel>
409 void CharacterConverter<Pixel>::renderMultiHelper(
410  Pixel* __restrict pixelPtr, int line,
411  int mask, int patternQuarter)
412 {
413  unsigned baseLine = mask | ((line / 4) & 7);
414  unsigned scroll = vdp.getHorizontalScrollHigh();
415  const byte* namePtr = getNamePtr(line, scroll);
416  for (unsigned n = 0; n < 32; ++n) {
417  unsigned patternNr = patternQuarter | namePtr[scroll & 0x1F];
418  unsigned color = vram.patternTable.readNP((patternNr * 8) | baseLine);
419  Pixel cl = palFg[color >> 4];
420  Pixel cr = palFg[color & 0x0F];
421  pixelPtr[0] = cl; pixelPtr[1] = cl;
422  pixelPtr[2] = cl; pixelPtr[3] = cl;
423  pixelPtr[4] = cr; pixelPtr[5] = cr;
424  pixelPtr[6] = cr; pixelPtr[7] = cr;
425  pixelPtr += 8;
426  if (!(++scroll & 0x1F)) namePtr = getNamePtr(line, scroll);
427  }
428 }
429 template <class Pixel>
430 void CharacterConverter<Pixel>::renderMulti(
431  Pixel* __restrict pixelPtr, int line)
432 {
433  int mask = (~0u << 11);
434  renderMultiHelper(pixelPtr, line, mask, 0);
435 }
436 
437 template <class Pixel>
438 void CharacterConverter<Pixel>::renderMultiQ(
439  Pixel* __restrict pixelPtr, int line)
440 {
441  int mask = (~0u << 13);
442  int patternQuarter = (line * 4) & ~0xFF; // (line / 8) * 32
443  renderMultiHelper(pixelPtr, line, mask, patternQuarter);
444 }
445 
446 template <class Pixel>
447 void CharacterConverter<Pixel>::renderBogus(
448  Pixel* __restrict pixelPtr)
449 {
450  Pixel fg = palFg[vdp.getForegroundColor()];
451  Pixel bg = palFg[vdp.getBackgroundColor()];
452  for (int n = 8; n--; ) *pixelPtr++ = bg;
453  for (int c = 20; c--; ) {
454  for (int n = 4; n--; ) *pixelPtr++ = fg;
455  for (int n = 2; n--; ) *pixelPtr++ = bg;
456  }
457  for (int n = 8; n--; ) *pixelPtr++ = bg;
458 }
459 
460 // Force template instantiation.
461 #if HAVE_16BPP
462 template class CharacterConverter<uint16_t>;
463 #endif
464 #if HAVE_32BPP || COMPONENT_GL
465 template class CharacterConverter<uint32_t>;
466 #endif
467 
468 } // namespace openmsx
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
Represents a VDP display mode.
Definition: DisplayMode.hh:14
unsigned Pixel
CharacterConverter(VDP &vdp, const Pixel *palFg, const Pixel *palBg)
Create a new bitmap scanline converter.
void convertLine(Pixel *linePtr, int line)
Convert a line of V9938 VRAM to 512 host pixels.
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:66
byte getBase() const
Get the base dispay mode as an integer: M5..M1 combined.
Definition: DisplayMode.hh:123
Utility class for converting VRAM contents to host pixels.
void setDisplayMode(DisplayMode mode)
Select the display mode to use for scanline conversion.
#define UNREACHABLE
Definition: unreachable.hh:56