17template<std::
unsigned_
integral Pixel>
19 : vdp(vdp_), vram(vdp.getVRAM())
20 , palette64(palette64_)
24template<std::
unsigned_
integral Pixel>
26 : vdp(vdp_), vram(vdp.getVRAM()), palette64(palette64_)
41 return (128 * ((spriteNo & 0xF0) + spriteY))
42 + ( 8 * (spriteNo & 0x0F));
50 template<std::
unsigned_
integral Pixel>
static void draw1(
51 std::span<const Pixel, 16> palette,
Pixel* __restrict buffer,
52 byte* __restrict ,
size_t p)
59 template<std::
unsigned_
integral Pixel>
static void draw1(
60 std::span<const Pixel, 16> palette,
Pixel* __restrict buffer,
61 byte* __restrict info,
size_t p)
64 if (p) *buffer = palette[p];
79 return (256 * (((spriteNo & 0xE0) >> 1) + spriteY))
80 + ( 8 * (spriteNo & 0x1F));
82 template<std::
unsigned_
integral Pixel>
static void draw1(
83 std::span<const Pixel, 16> palette,
Pixel* __restrict buffer,
84 byte* __restrict info,
size_t p)
96template<
typename Policy,
bool ALIGNED>
97static unsigned getPatternAddress(
98 V9990VRAM& vram,
unsigned nameAddr,
unsigned patternBase,
unsigned x,
unsigned y)
100 assert(!ALIGNED || ((x & 7) == 0));
101 unsigned patternNum = (Policy::readNameTable(vram, nameAddr + 0) +
102 Policy::readNameTable(vram, nameAddr + 1) * 256) & 0x1FFF;
103 constexpr auto PATTERN_PITCH = Policy::PATTERN_CHARS * 8 * (8 / 2);
104 unsigned x2 = (patternNum % Policy::PATTERN_CHARS) * 4 + (ALIGNED ? 0 : ((x & 7) / 2));
105 unsigned y2 = (patternNum / Policy::PATTERN_CHARS) * PATTERN_PITCH + y;
106 return patternBase + y2 + x2;
109template<
typename Policy>
110static constexpr unsigned nextNameAddr(
unsigned addr)
112 constexpr auto MASK = (2 * Policy::NAME_CHARS) - 1;
113 return (addr & ~MASK) | ((addr + 2) & MASK);
116template<
typename Policy,
bool CHECK_WIDTH, std::
unsigned_
integral Pixel>
118 V9990VRAM& vram, std::span<const Pixel, 16> palette,
Pixel* __restrict& buffer,
byte* __restrict& info,
119 unsigned& address,
int& width)
121 byte data = Policy::readPatternTable(vram, address++);
122 Policy::draw1(palette, buffer + 0, info + 0, data >> 4);
123 if (!CHECK_WIDTH || (width != 1)) {
124 Policy::draw1(palette, buffer + 1, info + 1, data & 0x0F);
131template<
typename Policy, std::
unsigned_
integral Pixel>
132static void renderPattern(
134 Pixel bgCol,
unsigned x,
unsigned y,
135 unsigned nameTable,
unsigned patternBase,
136 std::span<const Pixel, 16> palette0, std::span<const Pixel, 16> palette1)
138 assert(x < Policy::IMAGE_WIDTH);
139 auto width = narrow<int>(info_.size());
140 if (width == 0)
return;
141 byte* info = info_.data();
143 std::optional<ScopedAssign<Pixel>> col0, col1;
144 if constexpr (Policy::DRAW_BACKDROP) {
149 col0.emplace(*
const_cast<Pixel*
>(palette0.data()), bgCol);
150 col1.emplace(*
const_cast<Pixel*
>(palette1.data()), bgCol);
153 unsigned nameAddr = nameTable + (((y / 8) * Policy::NAME_CHARS + (x / 8)) * 2);
154 y = (y & 7) * Policy::NAME_CHARS * 2;
157 unsigned address = getPatternAddress<Policy, false>(vram, nameAddr, patternBase, x, y);
159 byte data = Policy::readPatternTable(vram, address);
160 Policy::draw1((address & 1) ? palette1 : palette0, buffer, info, data & 0x0F);
167 while ((x & 7) && (width > 0)) {
168 draw2<Policy, true>(vram, (address & 1) ? palette1 : palette0, buffer, info, address, width);
171 nameAddr = nextNameAddr<Policy>(nameAddr);
173 assert((x & 7) == 0 || (width <= 0));
174 while ((width & ~7) > 0) {
175 unsigned address = getPatternAddress<Policy, true>(vram, nameAddr, patternBase, x, y);
176 draw2<Policy, false>(vram, palette0, buffer, info, address, width);
177 draw2<Policy, false>(vram, palette1, buffer, info, address, width);
178 draw2<Policy, false>(vram, palette0, buffer, info, address, width);
179 draw2<Policy, false>(vram, palette1, buffer, info, address, width);
180 nameAddr = nextNameAddr<Policy>(nameAddr);
184 unsigned address = getPatternAddress<Policy, true>(vram, nameAddr, patternBase, x, y);
186 draw2<Policy, true>(vram, (address & 1) ? palette1 : palette0, buffer, info, address, width);
191template<
typename Policy, std::
unsigned_
integral Pixel>
192static void renderPattern2(
193 V9990VRAM& vram,
Pixel* buffer, std::span<byte, 256> info,
Pixel bgCol,
unsigned width1,
unsigned width2,
194 unsigned displayAX,
unsigned displayAY,
unsigned nameA,
unsigned patternA, std::span<const Pixel, 16> palA,
195 unsigned displayBX,
unsigned displayBY,
unsigned nameB,
unsigned patternB, std::span<const Pixel, 16> palB)
197 renderPattern<Policy>(
198 vram, buffer,
subspan(info, 0, width1), bgCol,
199 displayAX, displayAY, nameA, patternA, palA, palA);
203 displayBX = (displayBX + width1) & 511;
205 renderPattern<Policy>(
206 vram, buffer,
subspan(info, width1, width2), bgCol,
207 displayBX, displayBY, nameB, patternB, palB, palB);
210template<
typename Policy, std::
unsigned_
integral Pixel>
211static void renderSprites(
212 V9990VRAM& vram,
unsigned spritePatternTable, std::span<const Pixel, 64> palette64,
213 Pixel* __restrict buffer, std::span<byte> info,
214 int displayX,
int displayEnd,
unsigned displayY)
216 constexpr unsigned spriteTable = 0x3FE00;
219 std::array<int, 16 + 1> visibleSprites;
222 for (
auto sprite :
xrange(125)) {
223 unsigned spriteInfo = spriteTable + 4 * sprite;
224 byte spriteY = Policy::readSpriteAttr(vram, spriteInfo) + 1;
225 auto posY = narrow_cast<byte>(displayY - spriteY);
227 byte attr = Policy::readSpriteAttr(vram, spriteInfo + 3);
233 visibleSprites[index++] = sprite;
235 if (index == index_max)
break;
238 visibleSprites[index] = -1;
241 for (
unsigned sprite = 0; visibleSprites[sprite] != -1; ++sprite) {
242 unsigned addr = spriteTable + 4 * visibleSprites[sprite];
243 byte spriteAttr = Policy::readSpriteAttr(vram, addr + 3);
244 bool front = (spriteAttr & 0x20) == 0;
245 byte level = front ? 2 : 1;
246 int spriteX = Policy::readSpriteAttr(vram, addr + 2);
247 spriteX += 256 * (spriteAttr & 0x03);
248 if (spriteX > 1008) spriteX -= 1024;
249 byte spriteY = Policy::readSpriteAttr(vram, addr + 0);
250 byte spriteNo = Policy::readSpriteAttr(vram, addr + 1);
251 spriteY = narrow_cast<byte>(displayY - (spriteY + 1));
252 unsigned patAddr = spritePatternTable + Policy::spritePatOfst(spriteNo, spriteY);
253 auto palette16 = subspan<16>(palette64, (spriteAttr >> 2) & 0x30);
254 for (
int x = 0; x < 16; x +=2) {
255 auto draw = [&](
int xPos,
size_t p) {
256 if ((displayX <= xPos) && (xPos < displayEnd)) {
257 size_t xx = xPos - displayX;
259 if (info[xx] < level) {
260 buffer[xx] = palette16[p];
266 byte data = Policy::readPatternTable(vram, patAddr++);
267 draw(spriteX + x + 0, data >> 4);
268 draw(spriteX + x + 1, data & 0x0F);
273template<std::
unsigned_
integral Pixel>
275 std::span<Pixel> buf,
unsigned displayX,
unsigned displayY,
276 unsigned displayYA,
unsigned displayYB,
bool drawSprites)
278 Pixel* __restrict linePtr = buf.data();
279 auto displayWidth = narrow<unsigned>(buf.size());
281 unsigned prioX = vdp.getPriorityControlX();
282 unsigned prioY = vdp.getPriorityControlY();
283 if (displayY >= prioY) prioX = 0;
285 unsigned displayAX = (displayX + vdp.getScrollAX()) & 511;
286 unsigned displayBX = (displayX + vdp.getScrollBX()) & 511;
290 unsigned rollMask = vdp.getRollMask(0x1FF);
291 unsigned scrollAY = vdp.getScrollAY();
292 unsigned scrollBY = vdp.getScrollBY();
293 unsigned scrollAYBase = scrollAY & ~rollMask & 0x1FF;
294 unsigned displayAY = scrollAYBase + ((displayYA + scrollAY) & rollMask);
295 unsigned displayBY = (displayYB + scrollBY) & 0x1FF;
297 unsigned displayEnd = displayX + displayWidth;
298 unsigned end1 =
std::max(0, narrow<int>(
std::min(prioX, displayEnd)) - narrow<int>(displayX));
300 std::array<byte, 256> info;
303 Pixel bgCol = palette64[vdp.getBackDropColor()];
304 byte offset = vdp.getPaletteOffset();
305 auto palA = subspan<16>(palette64, (offset & 0x03) << 4);
306 auto palB = subspan<16>(palette64, (offset & 0x0C) << 2);
307 renderPattern2<P1BackgroundPolicy>(
308 vram, linePtr, info, bgCol, end1, displayWidth,
309 displayBX, displayBY, 0x7E000, 0x40000, palB,
310 displayAX, displayAY, 0x7C000, 0x00000, palA);
313 assert(displayWidth <= 256);
314 renderPattern2<P1ForegroundPolicy>(
315 vram, linePtr, info, bgCol, end1, displayWidth,
316 displayAX, displayAY, 0x7C000, 0x00000, palA,
317 displayBX, displayBY, 0x7E000, 0x40000, palB);
321 unsigned spritePatternTable = vdp.getSpritePatternAddress(
P1);
322 renderSprites<P1Policy>(
323 vram, spritePatternTable, palette64,
324 linePtr, info, displayX, displayEnd, displayY);
328template<std::
unsigned_
integral Pixel>
330 std::span<Pixel> buf,
unsigned displayX,
unsigned displayY,
331 unsigned displayYA,
bool drawSprites)
333 Pixel* __restrict linePtr = buf.data();
334 auto displayWidth = narrow<unsigned>(buf.size());
336 unsigned displayAX = (displayX + vdp.getScrollAX()) & 1023;
338 unsigned scrollY = vdp.getScrollAY();
339 unsigned rollMask = vdp.getRollMask(0x1FF);
340 unsigned scrollYBase = scrollY & ~rollMask & 0x1FF;
341 unsigned displayAY = scrollYBase + ((displayYA + scrollY) & rollMask);
343 unsigned displayEnd = displayX + displayWidth;
346 assert(displayWidth <= 512);
347 std::array<byte, 512> info;
348 Pixel bgCol = palette64[vdp.getBackDropColor()];
349 byte offset = vdp.getPaletteOffset();
350 auto palette0 = subspan<16>(palette64, (offset & 0x03) << 4);
351 auto palette1 = subspan<16>(palette64, (offset & 0x0C) << 2);
352 renderPattern<P2Policy>(
353 vram, linePtr,
subspan(info, 0, displayWidth), bgCol,
354 displayAX, displayAY, 0x7C000, 0x00000, palette0, palette1);
358 unsigned spritePatternTable = vdp.getSpritePatternAddress(
P2);
359 renderSprites<P2Policy>(
360 vram, spritePatternTable, palette64,
361 linePtr, info, displayX, displayEnd, displayY);
370#if HAVE_32BPP || COMPONENT_GL
void convertLine(std::span< Pixel > buf, unsigned displayX, unsigned displayY, unsigned displayYA, unsigned displayYB, bool drawSprites)
V9990P1Converter(V9990 &vdp, std::span< const Pixel, 64 > palette64)
void convertLine(std::span< Pixel > buf, unsigned displayX, unsigned displayY, unsigned displayYA, bool drawSprites)
V9990P2Converter(V9990 &vdp, std::span< const Pixel, 64 > palette64)
byte readVRAMDirect(unsigned address)
byte readVRAMP1(unsigned address)
byte readVRAMBx(unsigned address)
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
This file implemented 3 utility functions:
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
static constexpr bool DRAW_BACKDROP
static void draw1(std::span< const Pixel, 16 > palette, Pixel *buffer, byte *, size_t p)
static constexpr bool DRAW_BACKDROP
static void draw1(std::span< const Pixel, 16 > palette, Pixel *buffer, byte *info, size_t p)
static byte readSpriteAttr(V9990VRAM &vram, unsigned addr)
static constexpr unsigned NAME_CHARS
static byte readNameTable(V9990VRAM &vram, unsigned addr)
static byte readPatternTable(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 readNameTable(V9990VRAM &vram, unsigned addr)
static byte readSpriteAttr(V9990VRAM &vram, unsigned addr)
static byte readPatternTable(V9990VRAM &vram, unsigned addr)
static constexpr unsigned NAME_CHARS
static constexpr unsigned IMAGE_WIDTH
static unsigned spritePatOfst(byte spriteNo, byte spriteY)
static constexpr bool DRAW_BACKDROP
static void draw1(std::span< const Pixel, 16 > palette, Pixel *buffer, byte *info, size_t p)
static constexpr unsigned SCREEN_WIDTH
static constexpr unsigned PATTERN_CHARS
constexpr auto xrange(T e)