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