openMSX
V9990PxConverter.cc
Go to the documentation of this file.
1 #include "V9990PxConverter.hh"
2 #include "V9990.hh"
3 #include "V9990VRAM.hh"
4 #include "ScopedAssign.hh"
5 #include "build-info.hh"
6 #include "components.hh"
7 #include <cassert>
8 #include <algorithm>
9 #include <cstdint>
10 #include <optional>
11 
12 namespace openmsx {
13 
14 template<typename Pixel>
16  : vdp(vdp_), vram(vdp.getVRAM())
17  , palette64(palette64_)
18 {
19 }
20 
21 template<typename Pixel>
23  : vdp(vdp_), vram(vdp.getVRAM()), palette64(palette64_)
24 {
25 }
26 
27 struct P1Policy {
28  static byte readNameTable(V9990VRAM& vram, unsigned addr) {
29  return vram.readVRAMP1(addr);
30  }
31  static byte readPatternTable(V9990VRAM& vram, unsigned addr) {
32  return vram.readVRAMP1(addr);
33  }
34  static byte readSpriteAttr(V9990VRAM& vram, unsigned addr) {
35  return vram.readVRAMP1(addr);
36  }
37  static unsigned spritePatOfst(byte spriteNo, byte spriteY) {
38  return (128 * ((spriteNo & 0xF0) + spriteY))
39  + ( 8 * (spriteNo & 0x0F));
40  }
41  static constexpr unsigned SCREEN_WIDTH = 256;
42  static constexpr unsigned IMAGE_WIDTH = 2 * SCREEN_WIDTH;
43  static constexpr unsigned NAME_CHARS = IMAGE_WIDTH / 8;
44  static constexpr unsigned PATTERN_CHARS = SCREEN_WIDTH / 8;
45 };
47  template<typename Pixel> static void draw1(
48  const Pixel* palette, Pixel* __restrict buffer,
49  byte* __restrict /*info*/, size_t p)
50  {
51  *buffer = palette[p];
52  }
53  static constexpr bool DRAW_BACKDROP = true;
54 };
56  template<typename Pixel> static void draw1(
57  const Pixel* palette, Pixel* __restrict buffer,
58  byte* __restrict info, size_t p)
59  {
60  *info = bool(p);
61  if (p) *buffer = palette[p];
62  }
63  static constexpr bool DRAW_BACKDROP = false;
64 };
65 struct P2Policy {
66  static byte readNameTable(V9990VRAM& vram, unsigned addr) {
67  return vram.readVRAMDirect(addr);
68  }
69  static byte readPatternTable(V9990VRAM& vram, unsigned addr) {
70  return vram.readVRAMBx(addr);
71  }
72  static byte readSpriteAttr(V9990VRAM& vram, unsigned addr) {
73  return vram.readVRAMDirect(addr);
74  }
75  static unsigned spritePatOfst(byte spriteNo, byte spriteY) {
76  return (256 * (((spriteNo & 0xE0) >> 1) + spriteY))
77  + ( 8 * (spriteNo & 0x1F));
78  }
79  template<typename Pixel> static void draw1(
80  const Pixel* palette, Pixel* __restrict buffer,
81  byte* __restrict info, size_t p)
82  {
83  *info = bool(p);
84  *buffer = palette[p];
85  }
86  static constexpr bool DRAW_BACKDROP = true;
87  static constexpr unsigned SCREEN_WIDTH = 512;
88  static constexpr unsigned IMAGE_WIDTH = 2 * SCREEN_WIDTH;
89  static constexpr unsigned NAME_CHARS = IMAGE_WIDTH / 8;
90  static constexpr unsigned PATTERN_CHARS = SCREEN_WIDTH / 8;
91 };
92 
93 template<typename Policy, bool ALIGNED>
94 static unsigned getPatternAddress(
95  V9990VRAM& vram, unsigned nameAddr, unsigned patternBase, unsigned x, unsigned y)
96 {
97  assert(!ALIGNED || ((x & 7) == 0));
98  unsigned patternNum = (Policy::readNameTable(vram, nameAddr + 0) +
99  Policy::readNameTable(vram, nameAddr + 1) * 256) & 0x1FFF;
100  constexpr auto PATTERN_PITCH = Policy::PATTERN_CHARS * 8 * (8 / 2);
101  unsigned x2 = (patternNum % Policy::PATTERN_CHARS) * 4 + (ALIGNED ? 0 : ((x & 7) / 2));
102  unsigned y2 = (patternNum / Policy::PATTERN_CHARS) * PATTERN_PITCH + y;
103  return patternBase + y2 + x2;
104 }
105 
106 template<typename Policy>
107 static unsigned nextNameAddr(unsigned addr)
108 {
109  constexpr auto MASK = (2 * Policy::NAME_CHARS) - 1;
110  return (addr & ~MASK) | ((addr + 2) & MASK);
111 }
112 
113 template<typename Policy, bool CHECK_WIDTH, typename Pixel>
114 static void draw2(
115  V9990VRAM& vram, const Pixel* palette, Pixel* __restrict& buffer, byte* __restrict& info,
116  unsigned& address, int& width)
117 {
118  byte data = Policy::readPatternTable(vram, address++);
119  Policy::draw1(palette, buffer + 0, info + 0, data >> 4);
120  if (!CHECK_WIDTH || (width != 1)) {
121  Policy::draw1(palette, buffer + 1, info + 1, data & 0x0F);
122  }
123  width -= 2;
124  buffer += 2;
125  info += 2;
126 }
127 
128 template<typename Policy, typename Pixel>
129 static void renderPattern(
130  V9990VRAM& vram, Pixel* __restrict buffer, byte* __restrict info,
131  Pixel bgcol, int width, unsigned x, unsigned y,
132  unsigned nameTable, unsigned patternBase, const Pixel* palette0, const Pixel* palette1)
133 {
134  assert(x < Policy::IMAGE_WIDTH);
135  if (width == 0) return;
136 
137  std::optional<ScopedAssign<Pixel>> col0, col1; // optimized away when not used
138  if (Policy::DRAW_BACKDROP) {
139  // Speedup drawing by temporarily replacing palette index 0.
140  // OK because palette0 and palette1 never partially overlap, IOW either:
141  // - palette0 == palette1 (fully overlap)
142  // - abs(palette0 - palette1) >= 16 (no overlap at all)
143  col0.emplace(const_cast<Pixel*>(palette0)[0], bgcol);
144  col1.emplace(const_cast<Pixel*>(palette1)[0], bgcol);
145  }
146 
147  unsigned nameAddr = nameTable + (((y / 8) * Policy::NAME_CHARS + (x / 8)) * 2);
148  y = (y & 7) * Policy::NAME_CHARS * 2;
149 
150  if (x & 7) {
151  unsigned address = getPatternAddress<Policy, false>(vram, nameAddr, patternBase, x, y);
152  if (x & 1) {
153  byte data = Policy::readPatternTable(vram, address);
154  Policy::draw1((address & 1) ? palette1 : palette0, buffer, info, data & 0x0F);
155  ++address;
156  ++x;
157  ++buffer;
158  ++info;
159  --width;
160  }
161  while ((x & 7) && (width > 0)) {
162  draw2<Policy, true>(vram, (address & 1) ? palette1 : palette0, buffer, info, address, width);
163  x += 2;
164  }
165  nameAddr = nextNameAddr<Policy>(nameAddr);
166  }
167  assert((x & 7) == 0 || (width <= 0));
168  while ((width & ~7) > 0) {
169  unsigned address = getPatternAddress<Policy, true>(vram, nameAddr, patternBase, x, y);
170  draw2<Policy, false>(vram, palette0, buffer, info, address, width);
171  draw2<Policy, false>(vram, palette1, buffer, info, address, width);
172  draw2<Policy, false>(vram, palette0, buffer, info, address, width);
173  draw2<Policy, false>(vram, palette1, buffer, info, address, width);
174  nameAddr = nextNameAddr<Policy>(nameAddr);
175  }
176  assert(width < 8);
177  if (width > 0) {
178  unsigned address = getPatternAddress<Policy, true>(vram, nameAddr, patternBase, x, y);
179  do {
180  draw2<Policy, true>(vram, (address & 1) ? palette1 : palette0, buffer, info, address, width);
181  } while (width > 0);
182  }
183 }
184 
185 template<typename Policy, typename Pixel> // only used for P1
186 static void renderPattern2(
187  V9990VRAM& vram, Pixel* buffer, byte* info, Pixel bgcol, unsigned width1, unsigned width2,
188  unsigned displayAX, unsigned displayAY, unsigned nameA, unsigned patternA, const Pixel* palA,
189  unsigned displayBX, unsigned displayBY, unsigned nameB, unsigned patternB, const Pixel* palB)
190 {
191  renderPattern<Policy>(
192  vram, buffer, info, bgcol, width1,
193  displayAX, displayAY, nameA, patternA, palA, palA);
194 
195  buffer += width1;
196  info += width1;
197  width2 -= width1;
198  displayBX = (displayBX + width1) & 511;
199 
200  renderPattern<Policy>(
201  vram, buffer, info, bgcol, width2,
202  displayBX, displayBY, nameB, patternB, palB, palB);
203 }
204 
205 template<typename Policy, typename Pixel>
206 static void renderSprites(
207  V9990VRAM& vram, unsigned spritePatternTable, const Pixel* palette64,
208  Pixel* __restrict buffer, byte* __restrict info,
209  int displayX, int displayEnd, unsigned displayY)
210 {
211  constexpr unsigned spriteTable = 0x3FE00;
212 
213  // determine visible sprites
214  int visibleSprites[16 + 1];
215  int index = 0;
216  int index_max = 16;
217  for (unsigned sprite = 0; sprite < 125; ++sprite) {
218  unsigned spriteInfo = spriteTable + 4 * sprite;
219  byte spriteY = Policy::readSpriteAttr(vram, spriteInfo) + 1;
220  byte posY = displayY - spriteY;
221  if (posY < 16) {
222  byte attr = Policy::readSpriteAttr(vram, spriteInfo + 3);
223  if (attr & 0x10) {
224  // Invisible sprites do contribute towards the
225  // 16-sprites-per-line limit.
226  index_max--;
227  } else {
228  visibleSprites[index++] = sprite;
229  }
230  if (index == index_max) break;
231  }
232  }
233  visibleSprites[index] = -1;
234 
235  // actually draw sprites
236  for (unsigned sprite = 0; visibleSprites[sprite] != -1; ++sprite) {
237  unsigned addr = spriteTable + 4 * visibleSprites[sprite];
238  byte spriteAttr = Policy::readSpriteAttr(vram, addr + 3);
239  bool front = (spriteAttr & 0x20) == 0;
240  byte level = front ? 2 : 1;
241  int spriteX = Policy::readSpriteAttr(vram, addr + 2);
242  spriteX += 256 * (spriteAttr & 0x03);
243  if (spriteX > 1008) spriteX -= 1024; // hack X coord into -16..1008
244  byte spriteY = Policy::readSpriteAttr(vram, addr + 0);
245  byte spriteNo = Policy::readSpriteAttr(vram, addr + 1);
246  spriteY = displayY - (spriteY + 1);
247  unsigned patAddr = spritePatternTable + Policy::spritePatOfst(spriteNo, spriteY);
248  const Pixel* palette = palette64 + ((spriteAttr >> 2) & 0x30);
249  for (int x = 0; x < 16; x +=2) {
250  auto draw = [&](int xPos, size_t p) {
251  if ((displayX <= xPos) && (xPos < displayEnd)) {
252  size_t xx = xPos - displayX;
253  if (p) {
254  if (info[xx] < level) {
255  buffer[xx] = palette[p];
256  }
257  info[xx] = 2; // also if back-sprite is behind foreground
258  }
259  }
260  };
261  byte data = Policy::readPatternTable(vram, patAddr++);
262  draw(spriteX + x + 0, data >> 4);
263  draw(spriteX + x + 1, data & 0x0F);
264  }
265  }
266 }
267 
268 template<typename Pixel>
270  Pixel* linePtr, unsigned displayX, unsigned displayWidth,
271  unsigned displayY, unsigned displayYA, unsigned displayYB,
272  bool drawSprites)
273 {
274  unsigned prioX = vdp.getPriorityControlX();
275  unsigned prioY = vdp.getPriorityControlY();
276  if (displayY >= prioY) prioX = 0;
277 
278  unsigned displayAX = (displayX + vdp.getScrollAX()) & 511;
279  unsigned displayBX = (displayX + vdp.getScrollBX()) & 511;
280 
281  // Configurable 'roll' only applies to layer A.
282  // Layer B always rolls at 512 lines.
283  unsigned rollMask = vdp.getRollMask(0x1FF);
284  unsigned scrollAY = vdp.getScrollAY();
285  unsigned scrollBY = vdp.getScrollBY();
286  unsigned scrollAYBase = scrollAY & ~rollMask & 0x1FF;
287  unsigned displayAY = scrollAYBase + ((displayYA + scrollAY) & rollMask);
288  unsigned displayBY = (displayYB + scrollBY) & 0x1FF;
289 
290  unsigned displayEnd = displayX + displayWidth;
291  unsigned end1 = std::max<int>(0, std::min<int>(prioX, displayEnd) - displayX);
292 
293  // background + backdrop color
294  Pixel bgcol = palette64[vdp.getBackDropColor()];
295  byte offset = vdp.getPaletteOffset();
296  const Pixel* palA = palette64 + ((offset & 0x03) << 4);
297  const Pixel* palB = palette64 + ((offset & 0x0C) << 2);
298  renderPattern2<P1BackgroundPolicy>(
299  vram, linePtr, nullptr, bgcol, end1, displayWidth,
300  displayBX, displayBY, 0x7E000, 0x40000, palB,
301  displayAX, displayAY, 0x7C000, 0x00000, palA);
302 
303  // foreground + fill-in 'info'
304  assert(displayWidth <= 256);
305  byte info[256]; // left uninitialized
306  // 0->background, 1->foreground, 2->sprite (front or back)
307  renderPattern2<P1ForegroundPolicy>(
308  vram, linePtr, info, bgcol, end1, displayWidth,
309  displayAX, displayAY, 0x7C000, 0x00000, palA,
310  displayBX, displayBY, 0x7E000, 0x40000, palB);
311 
312  // combined back+front sprite plane
313  if (drawSprites) {
314  unsigned spritePatternTable = vdp.getSpritePatternAddress(P1);
315  renderSprites<P1Policy>(
316  vram, spritePatternTable, palette64,
317  linePtr, info, displayX, displayEnd, displayY);
318  }
319 }
320 
321 template<typename Pixel>
323  Pixel* linePtr, unsigned displayX, unsigned displayWidth,
324  unsigned displayY, unsigned displayYA, bool drawSprites)
325 {
326  unsigned displayAX = (displayX + vdp.getScrollAX()) & 1023;
327 
328  unsigned scrollY = vdp.getScrollAY();
329  unsigned rollMask = vdp.getRollMask(0x1FF);
330  unsigned scrollYBase = scrollY & ~rollMask & 0x1FF;
331  unsigned displayAY = scrollYBase + ((displayYA + scrollY) & rollMask);
332 
333  unsigned displayEnd = displayX + displayWidth;
334 
335  // image plane + backdrop color + fill-in 'info'
336  assert(displayWidth <= 512);
337  byte info[512]; // left uninitialized
338  // 0->background, 1->foreground, 2->sprite (front or back)
339  Pixel bgcol = palette64[vdp.getBackDropColor()];
340  byte offset = vdp.getPaletteOffset();
341  const Pixel* palette0 = palette64 + ((offset & 0x03) << 4);
342  const Pixel* palette1 = palette64 + ((offset & 0x0C) << 2);
343  renderPattern<P2Policy>(
344  vram, linePtr, info, bgcol, displayWidth,
345  displayAX, displayAY, 0x7C000, 0x00000, palette0, palette1);
346 
347  // combined back+front sprite plane
348  if (drawSprites) {
349  unsigned spritePatternTable = vdp.getSpritePatternAddress(P2);
350  renderSprites<P2Policy>(
351  vram, spritePatternTable, palette64,
352  linePtr, info, displayX, displayEnd, displayY);
353  }
354 }
355 
356 // Force template instantiation
357 #if HAVE_16BPP
358 template class V9990P1Converter<uint16_t>;
359 template class V9990P2Converter<uint16_t>;
360 #endif
361 #if HAVE_32BPP || COMPONENT_GL
362 template class V9990P1Converter<uint32_t>;
363 template class V9990P2Converter<uint32_t>;
364 #endif
365 
366 } // namespace openmsx
openmsx::P1Policy::PATTERN_CHARS
static constexpr unsigned PATTERN_CHARS
Definition: V9990PxConverter.cc:44
openmsx::V9990VRAM::readVRAMDirect
byte readVRAMDirect(unsigned address)
Definition: V9990VRAM.hh:74
openmsx::V9990VRAM
Video RAM for the V9990.
Definition: V9990VRAM.hh:16
openmsx::V9990P1Converter::convertLine
void convertLine(Pixel *linePtr, unsigned displayX, unsigned displayWidth, unsigned displayY, unsigned displayYA, unsigned displayYB, bool drawSprites)
Definition: V9990PxConverter.cc:269
openmsx::P2Policy::SCREEN_WIDTH
static constexpr unsigned SCREEN_WIDTH
Definition: V9990PxConverter.cc:87
openmsx::P2Policy::draw1
static void draw1(const Pixel *palette, Pixel *buffer, byte *info, size_t p)
Definition: V9990PxConverter.cc:79
openmsx::P2Policy::NAME_CHARS
static constexpr unsigned NAME_CHARS
Definition: V9990PxConverter.cc:89
openmsx::P1ForegroundPolicy
Definition: V9990PxConverter.cc:55
openmsx::P1BackgroundPolicy::DRAW_BACKDROP
static constexpr bool DRAW_BACKDROP
Definition: V9990PxConverter.cc:53
openmsx::V9990P1Converter::V9990P1Converter
V9990P1Converter(V9990 &vdp, const Pixel *palette64)
Definition: V9990PxConverter.cc:15
openmsx::P2
@ P2
Definition: V9990ModeEnum.hh:8
openmsx::P1ForegroundPolicy::DRAW_BACKDROP
static constexpr bool DRAW_BACKDROP
Definition: V9990PxConverter.cc:63
openmsx::P2Policy
Definition: V9990PxConverter.cc:65
openmsx::V9990VRAM::readVRAMBx
byte readVRAMBx(unsigned address)
Definition: V9990VRAM.hh:54
openmsx::P2Policy::DRAW_BACKDROP
static constexpr bool DRAW_BACKDROP
Definition: V9990PxConverter.cc:86
openmsx::P2Policy::PATTERN_CHARS
static constexpr unsigned PATTERN_CHARS
Definition: V9990PxConverter.cc:90
ScopedAssign.hh
openmsx::P2Policy::spritePatOfst
static unsigned spritePatOfst(byte spriteNo, byte spriteY)
Definition: V9990PxConverter.cc:75
V9990VRAM.hh
openmsx::P2Policy::readPatternTable
static byte readPatternTable(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:69
openmsx::P1Policy
Definition: V9990PxConverter.cc:27
openmsx::P1Policy::readNameTable
static byte readNameTable(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:28
openmsx::P1Policy::SCREEN_WIDTH
static constexpr unsigned SCREEN_WIDTH
Definition: V9990PxConverter.cc:41
openmsx::P2Policy::readNameTable
static byte readNameTable(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:66
openmsx::P1Policy::IMAGE_WIDTH
static constexpr unsigned IMAGE_WIDTH
Definition: V9990PxConverter.cc:42
openmsx::Pixel
uint32_t Pixel
Definition: GLHQLiteScaler.cc:98
openmsx::P1Policy::readPatternTable
static byte readPatternTable(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:31
V9990.hh
openmsx::P2Policy::readSpriteAttr
static byte readSpriteAttr(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:72
V9990PxConverter.hh
openmsx::P2Policy::IMAGE_WIDTH
static constexpr unsigned IMAGE_WIDTH
Definition: V9990PxConverter.cc:88
build-info.hh
openmsx::V9990VRAM::readVRAMP1
byte readVRAMP1(unsigned address)
Definition: V9990VRAM.hh:57
openmsx::P1
@ P1
Definition: V9990ModeEnum.hh:8
openmsx::V9990P2Converter::convertLine
void convertLine(Pixel *linePtr, unsigned displayX, unsigned displayWidth, unsigned displayY, unsigned displayYA, bool drawSprites)
Definition: V9990PxConverter.cc:322
components.hh
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1416
openmsx::P1BackgroundPolicy
Definition: V9990PxConverter.cc:46
openmsx::P1Policy::spritePatOfst
static unsigned spritePatOfst(byte spriteNo, byte spriteY)
Definition: V9990PxConverter.cc:37
openmsx::P1Policy::readSpriteAttr
static byte readSpriteAttr(V9990VRAM &vram, unsigned addr)
Definition: V9990PxConverter.cc:34
openmsx::P1Policy::NAME_CHARS
static constexpr unsigned NAME_CHARS
Definition: V9990PxConverter.cc:43
openmsx::P1BackgroundPolicy::draw1
static void draw1(const Pixel *palette, Pixel *buffer, byte *, size_t p)
Definition: V9990PxConverter.cc:47
openmsx::V9990P1Converter
Definition: V9990PxConverter.hh:11
openmsx::V9990P2Converter
Definition: V9990PxConverter.hh:28
openmsx::P1ForegroundPolicy::draw1
static void draw1(const Pixel *palette, Pixel *buffer, byte *info, size_t p)
Definition: V9990PxConverter.cc:56
openmsx::V9990VRAM
V9990VRAM
Definition: V9990VRAM.cc:59
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::V9990P2Converter::V9990P2Converter
V9990P2Converter(V9990 &vdp, const Pixel *palette64)
Definition: V9990PxConverter.cc:22
openmsx::V9990
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:31