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