openMSX
V9990BitmapConverter.cc
Go to the documentation of this file.
2#include "V9990VRAM.hh"
3#include "V9990.hh"
4#include "unreachable.hh"
5#include "build-info.hh"
6#include "components.hh"
7#include "narrow.hh"
8#include <array>
9#include <cassert>
10#include <cstdint>
11
12namespace openmsx {
13
14template<std::unsigned_integral Pixel>
16 V9990& vdp_,
17 std::span<const Pixel, 64> palette64_, std::span<const int16_t, 64> palette64_32768_,
18 std::span<const Pixel, 256> palette256_, std::span<const int16_t, 256> palette256_32768_,
19 std::span<const Pixel, 32768> palette32768_)
20 : vdp(vdp_), vram(vdp.getVRAM())
21 , palette64 (palette64_ ), palette64_32768 (palette64_32768_ )
22 , palette256(palette256_), palette256_32768(palette256_32768_)
23 , palette32768(palette32768_)
24{
25 setColorMode(PP, B0); // initialize with dummy values
26}
27
28template<bool YJK, bool PAL, bool SKIP, std::unsigned_integral Pixel, typename ColorLookup>
29static inline void draw_YJK_YUV_PAL(
30 ColorLookup color, V9990VRAM& vram,
31 Pixel* __restrict& out, unsigned& address, int firstX = 0)
32{
33 std::array<byte, 4> data;
34 for (auto& d : data) {
35 d = vram.readVRAMBx(address++);
36 }
37
38 int u = (data[2] & 7) + ((data[3] & 3) << 3) - ((data[3] & 4) << 3);
39 int v = (data[0] & 7) + ((data[1] & 3) << 3) - ((data[1] & 4) << 3);
40
41 for (auto i : xrange(SKIP ? firstX : 0, 4)) {
42 if (PAL && (data[i] & 0x08)) {
43 *out++ = color.lookup64(data[i] >> 4);
44 } else {
45 int y = (data[i] & 0xF8) >> 3;
46 int r = std::clamp(y + u, 0, 31);
47 int g = std::clamp((5 * y - 2 * u - v) / 4, 0, 31);
48 int b = std::clamp(y + v, 0, 31);
49 // The only difference between YUV and YJK is that
50 // green and blue are swapped.
51 if constexpr (YJK) std::swap(g, b);
52 *out++ = color.lookup32768((g << 10) + (r << 5) + b);
53 }
54 }
55}
56
57template<std::unsigned_integral Pixel, typename ColorLookup>
58static void rasterBYUV(
59 ColorLookup color, V9990& vdp, V9990VRAM& vram,
60 std::span<Pixel> buf, unsigned x, unsigned y)
61{
62 Pixel* __restrict out = buf.data();
63 int nrPixels = narrow<int>(buf.size());
64 unsigned address = (x & ~3) + y * vdp.getImageWidth();
65 if (x & 3) {
66 draw_YJK_YUV_PAL<false, false, true>(
67 color, vram, out, address, x & 3);
68 nrPixels -= narrow<int>(4 - (x & 3));
69 }
70 for (; nrPixels > 0; nrPixels -= 4) {
71 draw_YJK_YUV_PAL<false, false, false>(
72 color, vram, out, address);
73 }
74 // Note: this can draw up to 3 pixels too many, but that's ok.
75}
76
77template<std::unsigned_integral Pixel, typename ColorLookup>
78static void rasterBYUVP(
79 ColorLookup color, V9990& vdp, V9990VRAM& vram,
80 std::span<Pixel> buf, unsigned x, unsigned y)
81{
82 // TODO this mode cannot be shown in B4 and higher resolution modes
83 // (So the dual palette for B4 modes is not an issue here.)
84 Pixel* __restrict out = buf.data();
85 int nrPixels = narrow<int>(buf.size());
86 unsigned address = (x & ~3) + y * vdp.getImageWidth();
87 if (x & 3) {
88 draw_YJK_YUV_PAL<false, true, true>(
89 color, vram, out, address, x & 3);
90 nrPixels -= narrow<int>(4 - (x & 3));
91 }
92 for (; nrPixels > 0; nrPixels -= 4) {
93 draw_YJK_YUV_PAL<false, true, false>(
94 color, vram, out, address);
95 }
96 // Note: this can draw up to 3 pixels too many, but that's ok.
97}
98
99template<std::unsigned_integral Pixel, typename ColorLookup>
100static void rasterBYJK(
101 ColorLookup color, V9990& vdp, V9990VRAM& vram,
102 std::span<Pixel> buf, unsigned x, unsigned y)
103{
104 Pixel* __restrict out = buf.data();
105 int nrPixels = narrow<int>(buf.size());
106 unsigned address = (x & ~3) + y * vdp.getImageWidth();
107 if (x & 3) {
108 draw_YJK_YUV_PAL<true, false, true>(
109 color, vram, out, address, x & 3);
110 nrPixels -= narrow<int>(4 - (x & 3));
111 }
112 for (; nrPixels > 0; nrPixels -= 4) {
113 draw_YJK_YUV_PAL<true, false, false>(
114 color, vram, out, address);
115 }
116 // Note: this can draw up to 3 pixels too many, but that's ok.
117}
118
119template<std::unsigned_integral Pixel, typename ColorLookup>
120static void rasterBYJKP(
121 ColorLookup color, V9990& vdp, V9990VRAM& vram,
122 std::span<Pixel> buf, unsigned x, unsigned y)
123{
124 // TODO this mode cannot be shown in B4 and higher resolution modes
125 // (So the dual palette for B4 modes is not an issue here.)
126 Pixel* __restrict out = buf.data();
127 int nrPixels = narrow<int>(buf.size());
128 unsigned address = (x & ~3) + y * vdp.getImageWidth();
129 if (x & 3) {
130 draw_YJK_YUV_PAL<true, true, true>(
131 color, vram, out, address, x & 3);
132 nrPixels -= narrow<int>(4 - (x & 3));
133 }
134 for (; nrPixels > 0; nrPixels -= 4) {
135 draw_YJK_YUV_PAL<true, true, false>(
136 color, vram, out, address);
137 }
138 // Note: this can draw up to 3 pixels too many, but that's ok.
139}
140
141template<std::unsigned_integral Pixel, typename ColorLookup>
142static void rasterBD16(
143 ColorLookup color, V9990& vdp, V9990VRAM& vram,
144 std::span<Pixel> buf, unsigned x, unsigned y)
145{
146 Pixel* __restrict out = buf.data();
147 int nrPixels = narrow<int>(buf.size());
148 unsigned address = 2 * (x + y * vdp.getImageWidth());
149 if (vdp.isSuperimposing()) {
150 auto transparent = color.lookup256(0);
151 for (; nrPixels > 0; --nrPixels) {
152 byte high = vram.readVRAMBx(address + 1);
153 if (high & 0x80) {
154 *out = transparent;
155 } else {
156 byte low = vram.readVRAMBx(address + 0);
157 *out = color.lookup32768(low + 256 * high);
158 }
159 address += 2;
160 out += 1;
161 }
162 } else {
163 for (; nrPixels > 0; --nrPixels) {
164 byte low = vram.readVRAMBx(address++);
165 byte high = vram.readVRAMBx(address++);
166 *out++ = color.lookup32768((low + 256 * high) & 0x7FFF);
167 }
168 }
169}
170
171template<std::unsigned_integral Pixel, typename ColorLookup>
172static void rasterBD8(
173 ColorLookup color, V9990& vdp, V9990VRAM& vram,
174 std::span<Pixel> buf, unsigned x, unsigned y)
175{
176 Pixel* __restrict out = buf.data();
177 int nrPixels = narrow<int>(buf.size());
178 unsigned address = x + y * vdp.getImageWidth();
179 for (; nrPixels > 0; --nrPixels) {
180 *out++ = color.lookup256(vram.readVRAMBx(address++));
181 }
182}
183
184template<std::unsigned_integral Pixel, typename ColorLookup>
185static void rasterBP6(
186 ColorLookup color, V9990& vdp, V9990VRAM& vram,
187 std::span<Pixel> buf, unsigned x, unsigned y)
188{
189 Pixel* __restrict out = buf.data();
190 int nrPixels = narrow<int>(buf.size());
191 unsigned address = x + y * vdp.getImageWidth();
192 for (; nrPixels > 0; --nrPixels) {
193 *out++ = color.lookup64(vram.readVRAMBx(address++) & 0x3F);
194 }
195}
196
197template<std::unsigned_integral Pixel, typename ColorLookup>
198static void rasterBP4(
199 ColorLookup color, V9990& vdp, V9990VRAM& vram,
200 std::span<Pixel> buf, unsigned x, unsigned y)
201{
202 Pixel* __restrict out = buf.data();
203 int nrPixels = narrow<int>(buf.size());
204 assert(nrPixels > 0);
205 unsigned address = (x + y * vdp.getImageWidth()) / 2;
206 color.set64Offset((vdp.getPaletteOffset() & 0xC) << 2);
207 if (x & 1) {
208 byte data = vram.readVRAMBx(address++);
209 *out++ = color.lookup64(data & 0x0F);
210 --nrPixels;
211 }
212 for (; nrPixels > 0; nrPixels -= 2) {
213 byte data = vram.readVRAMBx(address++);
214 *out++ = color.lookup64(data >> 4);
215 *out++ = color.lookup64(data & 0x0F);
216 }
217 // Note: this possibly draws 1 pixel too many, but that's ok.
218}
219template<std::unsigned_integral Pixel, typename ColorLookup>
220static void rasterBP4HiRes(
221 ColorLookup color, V9990& vdp, V9990VRAM& vram,
222 std::span<Pixel> buf, unsigned x, unsigned y)
223{
224 // Verified on real HW:
225 // Bit PLT05 in palette offset is ignored, instead for even pixels
226 // bit 'PLT05' is '0', for odd pixels it's '1'.
227 Pixel* __restrict out = buf.data();
228 int nrPixels = narrow<int>(buf.size());
229 unsigned address = (x + y * vdp.getImageWidth()) / 2;
230 color.set64Offset((vdp.getPaletteOffset() & 0x4) << 2);
231 if (x & 1) {
232 byte data = vram.readVRAMBx(address++);
233 *out++ = color.lookup64(32 | (data & 0x0F));
234 --nrPixels;
235 }
236 for (; nrPixels > 0; nrPixels -= 2) {
237 byte data = vram.readVRAMBx(address++);
238 *out++ = color.lookup64( 0 | (data >> 4 ));
239 *out++ = color.lookup64(32 | (data & 0x0F));
240 }
241 // Note: this possibly draws 1 pixel too many, but that's ok.
242}
243
244template<std::unsigned_integral Pixel, typename ColorLookup>
245static void rasterBP2(
246 ColorLookup color, V9990& vdp, V9990VRAM& vram,
247 std::span<Pixel> buf, unsigned x, unsigned y)
248{
249 Pixel* __restrict out = buf.data();
250 int nrPixels = narrow<int>(buf.size());
251 assert(nrPixels > 0);
252 unsigned address = (x + y * vdp.getImageWidth()) / 4;
253 color.set64Offset(vdp.getPaletteOffset() << 2);
254 if (x & 3) {
255 byte data = vram.readVRAMBx(address++);
256 if ((x & 3) <= 1) *out++ = color.lookup64((data & 0x30) >> 4);
257 if ((x & 3) <= 2) *out++ = color.lookup64((data & 0x0C) >> 2);
258 if (true) *out++ = color.lookup64((data & 0x03) >> 0);
259 nrPixels -= narrow<int>(4 - (x & 3));
260 }
261 for (; nrPixels > 0; nrPixels -= 4) {
262 byte data = vram.readVRAMBx(address++);
263 *out++ = color.lookup64((data & 0xC0) >> 6);
264 *out++ = color.lookup64((data & 0x30) >> 4);
265 *out++ = color.lookup64((data & 0x0C) >> 2);
266 *out++ = color.lookup64((data & 0x03) >> 0);
267 }
268 // Note: this can draw up to 3 pixels too many, but that's ok.
269}
270template<std::unsigned_integral Pixel, typename ColorLookup>
271static void rasterBP2HiRes(
272 ColorLookup color, V9990& vdp, V9990VRAM& vram,
273 std::span<Pixel> buf, unsigned x, unsigned y)
274{
275 // Verified on real HW:
276 // Bit PLT05 in palette offset is ignored, instead for even pixels
277 // bit 'PLT05' is '0', for odd pixels it's '1'.
278 Pixel* __restrict out = buf.data();
279 int nrPixels = narrow<int>(buf.size());
280 assert(nrPixels > 0);
281 unsigned address = (x + y * vdp.getImageWidth()) / 4;
282 color.set64Offset((vdp.getPaletteOffset() & 0x7) << 2);
283 if (x & 3) {
284 byte data = vram.readVRAMBx(address++);
285 if ((x & 3) <= 1) *out++ = color.lookup64(32 | ((data & 0x30) >> 4));
286 if ((x & 3) <= 2) *out++ = color.lookup64( 0 | ((data & 0x0C) >> 2));
287 if (true) *out++ = color.lookup64(32 | ((data & 0x03) >> 0));
288 nrPixels -= narrow<int>(4 - (x & 3));
289 }
290 for (; nrPixels > 0; nrPixels -= 4) {
291 byte data = vram.readVRAMBx(address++);
292 *out++ = color.lookup64( 0 | ((data & 0xC0) >> 6));
293 *out++ = color.lookup64(32 | ((data & 0x30) >> 4));
294 *out++ = color.lookup64( 0 | ((data & 0x0C) >> 2));
295 *out++ = color.lookup64(32 | ((data & 0x03) >> 0));
296 }
297 // Note: this can draw up to 3 pixels too many, but that's ok.
298}
299
300// Helper class to translate V9990 palette indices into host Pixel values.
301template<std::unsigned_integral Pixel>
303{
304public:
305 PaletteLookup(std::span<const Pixel, 64> palette64_,
306 std::span<const Pixel, 256> palette256_,
307 std::span<const Pixel, 32768> palette32768_)
308 : palette64Base(palette64_)
309 , palette64(palette64_)
310 , palette256(palette256_)
311 , palette32768(palette32768_)
312 {
313 }
314
315 void set64Offset(size_t offset) { palette64 = palette64Base.subspan(offset); }
316 [[nodiscard]] Pixel lookup64 (size_t idx) const { return palette64 [idx]; }
317 [[nodiscard]] Pixel lookup256 (size_t idx) const { return palette256 [idx]; }
318 [[nodiscard]] Pixel lookup32768(size_t idx) const { return palette32768[idx]; }
319
320private:
321 std::span<const Pixel, 64> palette64Base;
322 std::span<const Pixel> palette64;
323 std::span<const Pixel, 256> palette256;
324 std::span<const Pixel, 32768> palette32768;
325};
326
327// Helper class to translate V9990 palette indices (64-entry, 256-entry and
328// 32768-entry palettes) into V9990 32768-entry palette indices.
330{
331public:
332 IndexLookup(std::span<const int16_t, 64> palette64_, std::span<const int16_t, 256> palette256_)
333 : palette64_32768Base(palette64_)
334 , palette64_32768(palette64_)
335 , palette256_32768(palette256_)
336 {
337 }
338
339 void set64Offset(size_t offset) { palette64_32768 = palette64_32768Base.subspan(offset); }
340 [[nodiscard]] int16_t lookup64 (size_t idx) const { return palette64_32768 [idx]; }
341 [[nodiscard]] int16_t lookup256 (size_t idx) const { return palette256_32768[idx]; }
342 [[nodiscard]] int16_t lookup32768(size_t idx) const { return int16_t(idx); }
343
344private:
345 std::span<const int16_t, 64> palette64_32768Base;
346 std::span<const int16_t> palette64_32768;
347 std::span<const int16_t, 256> palette256_32768;
348};
349
350template<std::unsigned_integral Pixel, typename ColorLookup>
351static void raster(V9990ColorMode colorMode, bool highRes,
352 ColorLookup color, V9990& vdp, V9990VRAM& vram,
353 std::span<Pixel> out, unsigned x, unsigned y)
354{
355 switch (colorMode) {
356 case BYUV: return rasterBYUV <Pixel>(color, vdp, vram, out, x, y);
357 case BYUVP: return rasterBYUVP<Pixel>(color, vdp, vram, out, x, y);
358 case BYJK: return rasterBYJK <Pixel>(color, vdp, vram, out, x, y);
359 case BYJKP: return rasterBYJKP<Pixel>(color, vdp, vram, out, x, y);
360 case BD16: return rasterBD16 <Pixel>(color, vdp, vram, out, x, y);
361 case BD8: return rasterBD8 <Pixel>(color, vdp, vram, out, x, y);
362 case BP6: return rasterBP6 <Pixel>(color, vdp, vram, out, x, y);
363 case BP4: return highRes ? rasterBP4HiRes<Pixel>(color, vdp, vram, out, x, y)
364 : rasterBP4 <Pixel>(color, vdp, vram, out, x, y);
365 case BP2: return highRes ? rasterBP2HiRes<Pixel>(color, vdp, vram, out, x, y)
366 : rasterBP2 <Pixel>(color, vdp, vram, out, x, y);
367 default: UNREACHABLE;
368 }
369}
370
371// Missing details in the V9990 application manual (reverse engineered from
372// tests on a real gfx9000):
373// * Cursor 0 is drawn on top of cursor 1 (IOW cursor 0 has higher priority).
374// This remains the case when both cursors use the 'EOR' feature (it's not
375// so that EOR is applied twice).
376// * The CC1,CC0,EOR bits in the cursor attribute table work like this:
377// (CC1,CC0):
378// when (0,0): pick the resulting color from bitmap rendering (this is
379// a 15 bit RGB value)
380// when (x,y): pick the color from palette with index R#28:x:y (this is
381// also a 15 bit RGB color)
382// (EOR):
383// when 0: use the above color unchanged
384// when 1: flip all the bits in the above color (IOW XOR with 0x7fff)
385// From this follows:
386// (CC1,CC0,EOR)==(0,0,0):
387// Results in an invisible cursor: each pixel is colored the same as the
388// corresponding background pixel.
389// (CC1,CC0,EOR)==(0,0,1):
390// This is the only combination where the cursor is drawn using multiple
391// colors, each pixel is the complement of the corresponding background
392// pixel.
393// (CC1,CC0,EOR)==(x,y,0):
394// This is the 'usual' configuration, cursor is drawn with a specific
395// color from the palette (also when bitmap rendering is not using the
396// palette, e.g. YJK or BD8 mode).
397// (CC1,CC0,EOR)==(x,y,1):
398// This undocumented mode draws the cursor with a single color which is
399// the complement of a specific palette color.
401{
402public:
403 CursorInfo(V9990& vdp, V9990VRAM& vram, std::span<const int16_t, 64> palette64_32768,
404 unsigned attrAddr, unsigned patAddr,
405 int displayY, bool drawCursor)
406 {
407 if (!drawCursor) return;
408
409 unsigned attrY = vram.readVRAMBx(attrAddr + 0) +
410 (vram.readVRAMBx(attrAddr + 2) & 1) * 256;
411 attrY += vdp.isInterlaced() ? 2 : 1; // 1 or 2 lines later
412 unsigned cursorLine = (displayY - attrY) & 511;
413 if (cursorLine >= 32) return;
414
415 byte attr = vram.readVRAMBx(attrAddr + 6);
416 if ((attr & 0x10) || ((attr & 0xe0) == 0x00)) {
417 // don't display
418 return;
419 }
420
421 pattern = (vram.readVRAMBx(patAddr + 4 * cursorLine + 0) << 24)
422 + (vram.readVRAMBx(patAddr + 4 * cursorLine + 1) << 16)
423 + (vram.readVRAMBx(patAddr + 4 * cursorLine + 2) << 8)
424 + (vram.readVRAMBx(patAddr + 4 * cursorLine + 3) << 0);
425 if (pattern == 0) {
426 // optimization, completely transparent line
427 return;
428 }
429
430 // mark cursor visible
431 x = vram.readVRAMBx(attrAddr + 4) + (attr & 3) * 256;
432
433 doXor = (attr & 0xe0) == 0x20;
434
435 auto colorIdx = vdp.getSpritePaletteOffset() + (attr >> 6);
436 color = palette64_32768[colorIdx];
437 if (attr & 0x20) color ^= 0x7fff;
438 }
439
440 [[nodiscard]] bool isVisible() const {
441 return x != unsigned(-1);
442 }
443 [[nodiscard]] bool dot() const {
444 return (x == 0) && (pattern & 0x80000000);
445 }
446 void shift() {
447 if (x) {
448 --x;
449 } else {
450 pattern <<= 1;
451 }
452 }
453
454public:
455 unsigned x{unsigned(-1)}; // not visible
456 uint32_t pattern{0};
457 int16_t color{0};
458 bool doXor{false};
459};
460
461template<std::unsigned_integral Pixel>
463 std::span<Pixel> dst, unsigned x, unsigned y,
464 int cursorY, bool drawCursors)
465{
466 assert(dst.size() <= 1024);
467
468 CursorInfo cursor0(vdp, vram, palette64_32768, 0x7fe00, 0x7ff00, cursorY, drawCursors);
469 CursorInfo cursor1(vdp, vram, palette64_32768, 0x7fe08, 0x7ff80, cursorY, drawCursors);
470
471 if (cursor0.isVisible() || cursor1.isVisible()) {
472 // raster background into a temporary buffer
473 std::array<uint16_t, 1024> buf;
474 raster(colorMode, highRes,
475 IndexLookup(palette64_32768, palette256_32768),
476 vdp, vram,
477 subspan(buf, dst.size()), x, y);
478
479 // draw sprites in this buffer
480 // TODO can be optimized
481 // TODO probably goes wrong when startX != 0
482 // TODO investigate dual palette in B4 and higher modes
483 // TODO check X-roll behavior
484 for (auto i : xrange(dst.size())) {
485 if (cursor0.dot()) {
486 if (cursor0.doXor) {
487 buf[i] ^= 0x7fff;
488 } else {
489 buf[i] = cursor0.color;
490 }
491 } else if (cursor1.dot()) {
492 if (cursor1.doXor) {
493 buf[i] ^= 0x7fff;
494 } else {
495 buf[i] = cursor1.color;
496 }
497 }
498 cursor0.shift();
499 cursor1.shift();
500 if ((cursor0.pattern == 0) && (cursor1.pattern == 0)) break;
501 }
502
503 // copy buffer to destination, translate from V9990 to host colors
504 for (auto i : xrange(dst.size())) {
505 dst[i] = palette32768[buf[i]];
506 }
507 } else {
508 // Optimization: no cursor(s) visible on this line, directly draw to destination
509 raster(colorMode, highRes,
510 PaletteLookup<Pixel>(palette64, palette256, palette32768),
511 vdp, vram, dst, x, y);
512 }
513}
514
515// Force template instantiation
516#if HAVE_16BPP
517template class V9990BitmapConverter<uint16_t>;
518#endif
519#if HAVE_32BPP || COMPONENT_GL
520template class V9990BitmapConverter<uint32_t>;
521#endif
522
523} // namespace openmsx
int g
CursorInfo(V9990 &vdp, V9990VRAM &vram, std::span< const int16_t, 64 > palette64_32768, unsigned attrAddr, unsigned patAddr, int displayY, bool drawCursor)
int16_t lookup64(size_t idx) const
IndexLookup(std::span< const int16_t, 64 > palette64_, std::span< const int16_t, 256 > palette256_)
void set64Offset(size_t offset)
int16_t lookup256(size_t idx) const
int16_t lookup32768(size_t idx) const
Pixel lookup256(size_t idx) const
Pixel lookup64(size_t idx) const
void set64Offset(size_t offset)
Pixel lookup32768(size_t idx) const
PaletteLookup(std::span< const Pixel, 64 > palette64_, std::span< const Pixel, 256 > palette256_, std::span< const Pixel, 32768 > palette32768_)
Utility class to convert VRAM content to host pixels.
V9990BitmapConverter(V9990 &vdp, std::span< const Pixel, 64 > palette64, std::span< const int16_t, 64 > palette64_32768, std::span< const Pixel, 256 > palette256, std::span< const int16_t, 256 > palette256_32768, std::span< const Pixel, 32768 > palette32768)
void convertLine(std::span< Pixel > dst, unsigned x, unsigned y, int cursorY, bool drawCursors)
Convert a line of VRAM into host pixels.
void setColorMode(V9990ColorMode colorMode_, V9990DisplayMode display)
Set a different rendering mode.
Video RAM for the V9990.
Definition: V9990VRAM.hh:16
byte readVRAMBx(unsigned address)
Definition: V9990VRAM.hh:54
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:34
bool isInterlaced() const
Get interlace status.
Definition: V9990.hh:61
byte getSpritePaletteOffset() const
return sprite palette offset
Definition: V9990.hh:307
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
Definition: gl_vec.hh:293
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
Definition: MemBuffer.hh:202
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition: ranges.hh:446
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:133