yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
ppu.cc
Go to the documentation of this file.
1#include "app/emu/video/ppu.h"
2
3#include <cstdint>
4#include <iostream>
5#include <vector>
6
8
9namespace yaze {
10namespace emu {
11
12// array for layer definitions per mode:
13// 0-7: mode 0-7; 8: mode 1 + l3prio; 9: mode 7 + extbg
14
15// 0-3; layers 1-4; 4: sprites; 5: nonexistent
16static const int kLayersPerMode[10][12] = {
17 {4, 0, 1, 4, 0, 1, 4, 2, 3, 4, 2, 3}, {4, 0, 1, 4, 0, 1, 4, 2, 4, 2, 5, 5},
18 {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5}, {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
19 {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5}, {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
20 {4, 0, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5}, {4, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5, 5},
21 {2, 4, 0, 1, 4, 0, 1, 4, 4, 2, 5, 5}, {4, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5, 5}};
22
23static const int kPrioritysPerMode[10][12] = {
24 {3, 1, 1, 2, 0, 0, 1, 1, 1, 0, 0, 0}, {3, 1, 1, 2, 0, 0, 1, 1, 0, 0, 5, 5},
25 {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5}, {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
26 {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5}, {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
27 {3, 1, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5}, {3, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5},
28 {1, 3, 1, 1, 2, 0, 0, 1, 0, 0, 5, 5}, {3, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5, 5}};
29
30static const int kLayerCountPerMode[10] = {12, 10, 8, 8, 8, 8, 6, 5, 10, 7};
31
32static const int kBitDepthsPerMode[10][4] = {
33 {2, 2, 2, 2}, {4, 4, 2, 5}, {4, 4, 5, 5}, {8, 4, 5, 5}, {8, 2, 5, 5},
34 {4, 2, 5, 5}, {4, 5, 5, 5}, {8, 5, 5, 5}, {4, 4, 2, 5}, {8, 7, 5, 5}};
35
36static const int kSpriteSizes[8][2] = {{8, 16}, {8, 32}, {8, 64}, {16, 32},
37 {16, 64}, {32, 64}, {16, 32}, {16, 32}};
38
39void Ppu::Reset() {
40 memset(vram, 0, sizeof(vram));
41 vram_pointer = 0;
46 memset(cgram, 0, sizeof(cgram));
48 cgram_second_write_ = false;
49 cgram_buffer_ = 0;
50 memset(oam, 0, sizeof(oam));
51 memset(high_oam_, 0, sizeof(high_oam_));
52 oam_adr_ = 0;
54 oam_in_high_ = false;
56 oam_second_write_ = false;
57 oam_buffer_ = 0;
58 obj_priority_ = false;
61 obj_size_ = 0;
62 obj_pixel_buffer_.fill(0);
64 time_over_ = false;
65 range_over_ = false;
66 obj_interlace_ = false;
67 for (int i = 0; i < 4; i++) {
68 bg_layer_[i].hScroll = 0;
69 bg_layer_[i].vScroll = 0;
70 bg_layer_[i].tilemapWider = false;
71 bg_layer_[i].tilemapHigher = false;
72 bg_layer_[i].tilemapAdr = 0;
73 bg_layer_[i].tileAdr = 0;
74 bg_layer_[i].bigTiles = false;
75 bg_layer_[i].mosaicEnabled = false;
76 }
77 scroll_prev_ = 0;
78 scroll_prev2_ = 0;
79 mosaic_size_ = 1;
81 for (int i = 0; i < 5; i++) {
82 layer_[i].mainScreenEnabled = false;
83 layer_[i].subScreenEnabled = false;
84 layer_[i].mainScreenWindowed = false;
85 layer_[i].subScreenWindowed = false;
86 }
87 memset(m7matrix, 0, sizeof(m7matrix));
88 m7prev = 0;
89 m7largeField = false;
90 m7charFill = false;
91 m7xFlip = false;
92 m7yFlip = false;
93 m7extBg = false;
94 m7startX = 0;
95 m7startY = 0;
96 for (int i = 0; i < 6; i++) {
97 windowLayer[i].window1enabled = false;
98 windowLayer[i].window2enabled = false;
100 windowLayer[i].window2inversed = false;
101 windowLayer[i].maskLogic = 0;
102 }
103 window1left = 0;
104 window1right = 0;
105 window2left = 0;
106 window2right = 0;
107 clip_mode_ = 0;
109 add_subscreen_ = false;
110 subtract_color_ = false;
111 half_color_ = false;
112 memset(math_enabled_array_, 0, sizeof(math_enabled_array_));
113 fixed_color_r_ = 0;
114 fixed_color_g_ = 0;
115 fixed_color_b_ = 0;
116 forced_blank_ = true;
117 brightness = 15; // SNES hardware default is maximum brightness
118 mode = 0;
119 bg3priority = false;
120 even_frame = false;
121 pseudo_hires_ = false;
122 overscan_ = false;
123 frame_overscan_ = false;
124 interlace = false;
125 frame_interlace = false;
126 direct_color_ = false;
127 h_count_ = 0;
128 v_count_ = 0;
129 h_count_second_ = false;
130 v_count_second_ = false;
131 counters_latched_ = false;
132 ppu1_open_bus_ = 0;
133 ppu2_open_bus_ = 0;
134 memset(pixelBuffer, 0, sizeof(pixelBuffer));
135}
136
138 // called at (0, 0)
140 range_over_ = false;
141 time_over_ = false;
143}
144
145void Ppu::RunLine(int line) {
146 // called for lines 1-224/239
147 // evaluate sprites
148 obj_pixel_buffer_.fill(0);
149 if (!forced_blank_) EvaluateSprites(line - 1);
150 // actual line
151 if (mode == 7) CalculateMode7Starts(line);
152 for (int x = 0; x < 256; x++) {
153 HandlePixel(x, line);
154 }
155}
156
157void Ppu::HandlePixel(int x, int y) {
158 int r = 0, r2 = 0;
159 int g = 0, g2 = 0;
160 int b = 0, b2 = 0;
161 if (!forced_blank_) {
162 int mainLayer = GetPixel(x, y, false, &r, &g, &b);
163 bool colorWindowState = GetWindowState(5, x);
164 if (clip_mode_ == 3 || (clip_mode_ == 2 && colorWindowState) ||
165 (clip_mode_ == 1 && !colorWindowState)) {
166 r = 0;
167 g = 0;
168 b = 0;
169 }
170 int secondLayer = 5; // backdrop
171 bool mathEnabled = mainLayer < 6 && math_enabled_array_[mainLayer] &&
172 !(prevent_math_mode_ == 3 ||
173 (prevent_math_mode_ == 2 && colorWindowState) ||
174 (prevent_math_mode_ == 1 && !colorWindowState));
175 if ((mathEnabled && add_subscreen_) || pseudo_hires_ || mode == 5 ||
176 mode == 6) {
177 secondLayer = GetPixel(x, y, true, &r2, &g2, &b2);
178 }
179 // TODO: subscreen pixels can be clipped to black as well
180 // TODO: math for subscreen pixels (add/sub sub to main)
181 if (mathEnabled) {
182 if (subtract_color_) {
183 r -= (add_subscreen_ && secondLayer != 5) ? r2 : fixed_color_r_;
184 g -= (add_subscreen_ && secondLayer != 5) ? g2 : fixed_color_g_;
185 b -= (add_subscreen_ && secondLayer != 5) ? b2 : fixed_color_b_;
186 } else {
187 r += (add_subscreen_ && secondLayer != 5) ? r2 : fixed_color_r_;
188 g += (add_subscreen_ && secondLayer != 5) ? g2 : fixed_color_g_;
189 b += (add_subscreen_ && secondLayer != 5) ? b2 : fixed_color_b_;
190 }
191 if (half_color_ && (secondLayer != 5 || !add_subscreen_)) {
192 r >>= 1;
193 g >>= 1;
194 b >>= 1;
195 }
196 if (r > 31) r = 31;
197 if (g > 31) g = 31;
198 if (b > 31) b = 31;
199 if (r < 0) r = 0;
200 if (g < 0) g = 0;
201 if (b < 0) b = 0;
202 }
203 if (!(pseudo_hires_ || mode == 5 || mode == 6)) {
204 r2 = r;
205 g2 = g;
206 b2 = b;
207 }
208 }
209 int row = (y - 1) + (even_frame ? 0 : 239);
210
211 // SDL_PIXELFORMAT_ARGB8888 with pixelOutputFormat=0 (BGRX)
212 // Memory layout: [B][G][R][A] at offsets 0,1,2,3 respectively
213 // Convert 5-bit SNES color (0-31) to 8-bit (0-255) via (val << 3) | (val >> 2)
214 // Two pixels per X position for hi-res support:
215 // pixel1 at x*8 + 0..3, pixel2 at x*8 + 4..7
216
217 // Apply brightness
218 r = r * brightness / 15;
219 g = g * brightness / 15;
220 b = b * brightness / 15;
221 r2 = r2 * brightness / 15;
222 g2 = g2 * brightness / 15;
223 b2 = b2 * brightness / 15;
224
225 // First pixel (hi-res/main screen)
226 pixelBuffer[row * 2048 + x * 8 + 0 + pixelOutputFormat] =
227 ((b2 << 3) | (b2 >> 2)); // Blue channel
228 pixelBuffer[row * 2048 + x * 8 + 1 + pixelOutputFormat] =
229 ((g2 << 3) | (g2 >> 2)); // Green channel
230 pixelBuffer[row * 2048 + x * 8 + 2 + pixelOutputFormat] =
231 ((r2 << 3) | (r2 >> 2)); // Red channel
232 pixelBuffer[row * 2048 + x * 8 + 3 + pixelOutputFormat] = 0xFF; // Alpha (opaque)
233
234 // Second pixel (lo-res/subscreen)
235 pixelBuffer[row * 2048 + x * 8 + 4 + pixelOutputFormat] =
236 ((b << 3) | (b >> 2)); // Blue channel
237 pixelBuffer[row * 2048 + x * 8 + 5 + pixelOutputFormat] =
238 ((g << 3) | (g >> 2)); // Green channel
239 pixelBuffer[row * 2048 + x * 8 + 6 + pixelOutputFormat] =
240 ((r << 3) | (r >> 2)); // Red channel
241 pixelBuffer[row * 2048 + x * 8 + 7 + pixelOutputFormat] = 0xFF; // Alpha (opaque)
242}
243
244int Ppu::GetPixel(int x, int y, bool subscreen, int* r, int* g, int* b) {
245 // figure out which color is on this location on main- or subscreen, sets it
246 // in r, g, b
247 // returns which layer it is: 0-3 for bg layer, 4 or 6 for sprites (depending
248 // on palette), 5 for backdrop
249 int actMode = mode == 1 && bg3priority ? 8 : mode;
250 actMode = mode == 7 && m7extBg ? 9 : actMode;
251 int layer = 5;
252 int pixel = 0;
253 for (int i = 0; i < kLayerCountPerMode[actMode]; i++) {
254 int curLayer = kLayersPerMode[actMode][i];
255 int curPriority = kPrioritysPerMode[actMode][i];
256 bool layerActive = false;
257 if (!subscreen) {
258 layerActive = layer_[curLayer].mainScreenEnabled &&
259 (!layer_[curLayer].mainScreenWindowed ||
260 !GetWindowState(curLayer, x));
261 } else {
262 layerActive =
263 layer_[curLayer].subScreenEnabled &&
264 (!layer_[curLayer].subScreenWindowed || !GetWindowState(curLayer, x));
265 }
266 if (layerActive) {
267 if (curLayer < 4) {
268 // bg layer
269 int lx = x;
270 int ly = y;
271 if (bg_layer_[curLayer].mosaicEnabled && mosaic_size_ > 1) {
272 lx -= lx % mosaic_size_;
273 ly -= (ly - mosaic_startline_) % mosaic_size_;
274 }
275 if (mode == 7) {
276 pixel = GetPixelForMode7(lx, curLayer, curPriority);
277 } else {
278 lx += bg_layer_[curLayer].hScroll;
279 if (mode == 5 || mode == 6) {
280 lx *= 2;
281 lx += (subscreen || bg_layer_[curLayer].mosaicEnabled) ? 0 : 1;
282 if (interlace) {
283 ly *= 2;
284 ly += (even_frame || bg_layer_[curLayer].mosaicEnabled) ? 0 : 1;
285 }
286 }
287 ly += bg_layer_[curLayer].vScroll;
288 if (mode == 2 || mode == 4 || mode == 6) {
289 HandleOPT(curLayer, &lx, &ly);
290 }
291 pixel =
292 GetPixelForBgLayer(lx & 0x3ff, ly & 0x3ff, curLayer, curPriority);
293 }
294 } else {
295 // get a pixel from the sprite buffer
296 pixel = 0;
297 if (obj_priority_buffer_[x] == curPriority)
298 pixel = obj_pixel_buffer_[x];
299 }
300 }
301 if (pixel > 0) {
302 layer = curLayer;
303 break;
304 }
305 }
306 if (direct_color_ && layer < 4 && kBitDepthsPerMode[actMode][layer] == 8) {
307 *r = ((pixel & 0x7) << 2) | ((pixel & 0x100) >> 7);
308 *g = ((pixel & 0x38) >> 1) | ((pixel & 0x200) >> 8);
309 *b = ((pixel & 0xc0) >> 3) | ((pixel & 0x400) >> 8);
310 } else {
311 uint16_t color = cgram[pixel & 0xff];
312 *r = color & 0x1f;
313 *g = (color >> 5) & 0x1f;
314 *b = (color >> 10) & 0x1f;
315 }
316 if (layer == 4 && pixel < 0xc0)
317 layer = 6; // sprites with palette color < 0xc0
318 return layer;
319}
320
321int Ppu::GetPixelForMode7(int x, int layer, bool priority) {
322 uint8_t rx = m7xFlip ? 255 - x : x;
323 int xPos = (m7startX + m7matrix[0] * rx) >> 8;
324 int yPos = (m7startY + m7matrix[2] * rx) >> 8;
325 bool outsideMap = xPos < 0 || xPos >= 1024 || yPos < 0 || yPos >= 1024;
326 xPos &= 0x3ff;
327 yPos &= 0x3ff;
328 if (!m7largeField) outsideMap = false;
329 uint8_t tile = outsideMap ? 0 : vram[(yPos >> 3) * 128 + (xPos >> 3)] & 0xff;
330 uint8_t pixel = outsideMap && !m7charFill
331 ? 0
332 : vram[tile * 64 + (yPos & 7) * 8 + (xPos & 7)] >> 8;
333 if (layer == 1) {
334 if (((bool)(pixel & 0x80)) != priority) return 0;
335 return pixel & 0x7f;
336 }
337 return pixel;
338}
339
340bool Ppu::GetWindowState(int layer, int x) {
341 if (!windowLayer[layer].window1enabled &&
342 !windowLayer[layer].window2enabled) {
343 return false;
344 }
345 if (windowLayer[layer].window1enabled && !windowLayer[layer].window2enabled) {
346 bool test = x >= window1left && x <= window1right;
347 return windowLayer[layer].window1inversed ? !test : test;
348 }
349 if (!windowLayer[layer].window1enabled && windowLayer[layer].window2enabled) {
350 bool test = x >= window2left && x <= window2right;
351 return windowLayer[layer].window2inversed ? !test : test;
352 }
353 bool test1 = x >= window1left && x <= window1right;
354 bool test2 = x >= window2left && x <= window2right;
355 if (windowLayer[layer].window1inversed) test1 = !test1;
356 if (windowLayer[layer].window2inversed) test2 = !test2;
357 switch (windowLayer[layer].maskLogic) {
358 case 0:
359 return test1 || test2;
360 case 1:
361 return test1 && test2;
362 case 2:
363 return test1 != test2;
364 case 3:
365 return test1 == test2;
366 }
367 return false;
368}
369
370void Ppu::HandleOPT(int layer, int* lx, int* ly) {
371 int x = *lx;
372 int y = *ly;
373 int column = 0;
374 if (mode == 6) {
375 column = ((x - (x & 0xf)) - ((bg_layer_[layer].hScroll * 2) & 0xfff0)) >> 4;
376 } else {
377 column = ((x - (x & 0x7)) - (bg_layer_[layer].hScroll & 0xfff8)) >> 3;
378 }
379 if (column > 0) {
380 // fetch offset values from layer 3 tilemap
381 int valid = layer == 0 ? 0x2000 : 0x4000;
382 uint16_t hOffset = GetOffsetValue(column - 1, 0);
383 uint16_t vOffset = 0;
384 if (mode == 4) {
385 if (hOffset & 0x8000) {
386 vOffset = hOffset;
387 hOffset = 0;
388 }
389 } else {
390 vOffset = GetOffsetValue(column - 1, 1);
391 }
392 if (mode == 6) {
393 // TODO: not sure if correct
394 if (hOffset & valid)
395 *lx = (((hOffset & 0x3f8) + (column * 8)) * 2) | (x & 0xf);
396 } else {
397 if (hOffset & valid) *lx = ((hOffset & 0x3f8) + (column * 8)) | (x & 0x7);
398 }
399 // TODO: not sure if correct for interlace
400 if (vOffset & valid)
401 *ly = (vOffset & 0x3ff) + (y - bg_layer_[layer].vScroll);
402 }
403}
404
405uint16_t Ppu::GetOffsetValue(int col, int row) {
406 int x = col * 8 + bg_layer_[2].hScroll;
407 int y = row * 8 + bg_layer_[2].vScroll;
408 int tileBits = bg_layer_[2].bigTiles ? 4 : 3;
409 int tileHighBit = bg_layer_[2].bigTiles ? 0x200 : 0x100;
410 uint16_t tilemapAdr =
412 (((y >> tileBits) & 0x1f) << 5 | ((x >> tileBits) & 0x1f));
413 if ((x & tileHighBit) && bg_layer_[2].tilemapWider) tilemapAdr += 0x400;
414 if ((y & tileHighBit) && bg_layer_[2].tilemapHigher)
415 tilemapAdr += bg_layer_[2].tilemapWider ? 0x800 : 0x400;
416 return vram[tilemapAdr & 0x7fff];
417}
418
419int Ppu::GetPixelForBgLayer(int x, int y, int layer, bool priority) {
420 // figure out address of tilemap word and read it
421 bool wideTiles = bg_layer_[layer].bigTiles || mode == 5 || mode == 6;
422 int tileBitsX = wideTiles ? 4 : 3;
423 int tileHighBitX = wideTiles ? 0x200 : 0x100;
424 int tileBitsY = bg_layer_[layer].bigTiles ? 4 : 3;
425 int tileHighBitY = bg_layer_[layer].bigTiles ? 0x200 : 0x100;
426 uint16_t tilemapAdr =
427 bg_layer_[layer].tilemapAdr +
428 (((y >> tileBitsY) & 0x1f) << 5 | ((x >> tileBitsX) & 0x1f));
429 if ((x & tileHighBitX) && bg_layer_[layer].tilemapWider) tilemapAdr += 0x400;
430 if ((y & tileHighBitY) && bg_layer_[layer].tilemapHigher)
431 tilemapAdr += bg_layer_[layer].tilemapWider ? 0x800 : 0x400;
432 uint16_t tile = vram[tilemapAdr & 0x7fff];
433 // check priority, get palette
434 if (((bool)(tile & 0x2000)) != priority) return 0; // wrong priority
435 int paletteNum = (tile & 0x1c00) >> 10;
436 // figure out position within tile
437 int row = (tile & 0x8000) ? 7 - (y & 0x7) : (y & 0x7);
438 int col = (tile & 0x4000) ? (x & 0x7) : 7 - (x & 0x7);
439 int tileNum = tile & 0x3ff;
440 if (wideTiles) {
441 // if unflipped right half of tile, or flipped left half of tile
442 if (((bool)(x & 8)) ^ ((bool)(tile & 0x4000))) tileNum += 1;
443 }
444 if (bg_layer_[layer].bigTiles) {
445 // if unflipped bottom half of tile, or flipped upper half of tile
446 if (((bool)(y & 8)) ^ ((bool)(tile & 0x8000))) tileNum += 0x10;
447 }
448 // read tiledata, ajust palette for mode 0
449 int bitDepth = kBitDepthsPerMode[mode][layer];
450 if (mode == 0) paletteNum += 8 * layer;
451 // plane 1 (always)
452 int paletteSize = 4;
453 uint16_t plane1 = vram[(bg_layer_[layer].tileAdr +
454 ((tileNum & 0x3ff) * 4 * bitDepth) + row) &
455 0x7fff];
456 int pixel = (plane1 >> col) & 1;
457 pixel |= ((plane1 >> (8 + col)) & 1) << 1;
458 // plane 2 (for 4bpp, 8bpp)
459 if (bitDepth > 2) {
460 paletteSize = 16;
461 uint16_t plane2 = vram[(bg_layer_[layer].tileAdr +
462 ((tileNum & 0x3ff) * 4 * bitDepth) + 8 + row) &
463 0x7fff];
464 pixel |= ((plane2 >> col) & 1) << 2;
465 pixel |= ((plane2 >> (8 + col)) & 1) << 3;
466 }
467 // plane 3 & 4 (for 8bpp)
468 if (bitDepth > 4) {
469 paletteSize = 256;
470 uint16_t plane3 = vram[(bg_layer_[layer].tileAdr +
471 ((tileNum & 0x3ff) * 4 * bitDepth) + 16 + row) &
472 0x7fff];
473 pixel |= ((plane3 >> col) & 1) << 4;
474 pixel |= ((plane3 >> (8 + col)) & 1) << 5;
475 uint16_t plane4 = vram[(bg_layer_[layer].tileAdr +
476 ((tileNum & 0x3ff) * 4 * bitDepth) + 24 + row) &
477 0x7fff];
478 pixel |= ((plane4 >> col) & 1) << 6;
479 pixel |= ((plane4 >> (8 + col)) & 1) << 7;
480 }
481 // return cgram index, or 0 if transparent, palette number in bits 10-8 for
482 // 8-color layers
483 return pixel == 0 ? 0 : paletteSize * paletteNum + pixel;
484}
485
486void Ppu::EvaluateSprites(int line) {
487 // TODO: rectangular sprites, wierdness with sprites at -256
488 uint8_t index = obj_priority_ ? (oam_adr_ & 0xfe) : 0;
489 int spritesFound = 0;
490 int tilesFound = 0;
491 uint8_t foundSprites[32] = {};
492 // iterate over oam to find sprites in range
493 for (int i = 0; i < 128; i++) {
494 uint8_t y = oam[index] >> 8;
495 // check if the sprite is on this line and get the sprite size
496 uint8_t row = line - y;
497 int spriteSize =
498 kSpriteSizes[obj_size_]
499 [(high_oam_[index >> 3] >> ((index & 7) + 1)) & 1];
500 int spriteHeight = obj_interlace_ ? spriteSize / 2 : spriteSize;
501 if (row < spriteHeight) {
502 // in y-range, get the x location, using the high bit as well
503 int x = oam[index] & 0xff;
504 x |= ((high_oam_[index >> 3] >> (index & 7)) & 1) << 8;
505 if (x > 255) x -= 512;
506 // if in x-range, record
507 if (x > -spriteSize) {
508 // break if we found 32 sprites already
509 spritesFound++;
510 if (spritesFound > 32) {
511 range_over_ = true;
512 spritesFound = 32;
513 break;
514 }
515 foundSprites[spritesFound - 1] = index;
516 }
517 }
518 index += 2;
519 }
520 // iterate over found sprites backwards to fetch max 34 tile slivers
521 for (int i = spritesFound; i > 0; i--) {
522 index = foundSprites[i - 1];
523 uint8_t y = oam[index] >> 8;
524 uint8_t row = line - y;
525 int spriteSize =
526 kSpriteSizes[obj_size_]
527 [(high_oam_[index >> 3] >> ((index & 7) + 1)) & 1];
528 int x = oam[index] & 0xff;
529 x |= ((high_oam_[index >> 3] >> (index & 7)) & 1) << 8;
530 if (x > 255) x -= 512;
531 if (x > -spriteSize) {
532 // update row according to obj-interlace
533 if (obj_interlace_) row = row * 2 + (even_frame ? 0 : 1);
534 // get some data for the sprite and y-flip row if needed
535 int tile = oam[index + 1] & 0xff;
536 int palette = (oam[index + 1] & 0xe00) >> 9;
537 bool hFlipped = oam[index + 1] & 0x4000;
538 if (oam[index + 1] & 0x8000) row = spriteSize - 1 - row;
539 // fetch all tiles in x-range
540 for (int col = 0; col < spriteSize; col += 8) {
541 if (col + x > -8 && col + x < 256) {
542 // break if we found > 34 8*1 slivers already
543 tilesFound++;
544 if (tilesFound > 34) {
545 time_over_ = true;
546 break;
547 }
548 // figure out which tile this uses, looping within 16x16 pages, and
549 // get it's data
550 int usedCol = hFlipped ? spriteSize - 1 - col : col;
551 uint8_t usedTile = (((tile >> 4) + (row / 8)) << 4) |
552 (((tile & 0xf) + (usedCol / 8)) & 0xf);
553 uint16_t objAdr =
554 (oam[index + 1] & 0x100) ? obj_tile_adr2_ : obj_tile_adr1_;
555 uint16_t plane1 =
556 vram[(objAdr + usedTile * 16 + (row & 0x7)) & 0x7fff];
557 uint16_t plane2 =
558 vram[(objAdr + usedTile * 16 + 8 + (row & 0x7)) & 0x7fff];
559 // go over each pixel
560 for (int px = 0; px < 8; px++) {
561 int shift = hFlipped ? px : 7 - px;
562 int pixel = (plane1 >> shift) & 1;
563 pixel |= ((plane1 >> (8 + shift)) & 1) << 1;
564 pixel |= ((plane2 >> shift) & 1) << 2;
565 pixel |= ((plane2 >> (8 + shift)) & 1) << 3;
566 // draw it in the buffer if there is a pixel here
567 int screenCol = col + x + px;
568 if (pixel > 0 && screenCol >= 0 && screenCol < 256) {
569 obj_pixel_buffer_[screenCol] = 0x80 + 16 * palette + pixel;
570 obj_priority_buffer_[screenCol] = (oam[index + 1] & 0x3000) >> 12;
571 }
572 }
573 }
574 }
575 if (tilesFound > 34)
576 break; // break out of sprite-loop if max tiles found
577 }
578 }
579}
580
582 // expand 13-bit values to signed values
583 int hScroll = ((int16_t)(m7matrix[6] << 3)) >> 3;
584 int vScroll = ((int16_t)(m7matrix[7] << 3)) >> 3;
585 int xCenter = ((int16_t)(m7matrix[4] << 3)) >> 3;
586 int yCenter = ((int16_t)(m7matrix[5] << 3)) >> 3;
587 // do calculation
588 int clippedH = hScroll - xCenter;
589 int clippedV = vScroll - yCenter;
590 clippedH = (clippedH & 0x2000) ? (clippedH | ~1023) : (clippedH & 1023);
591 clippedV = (clippedV & 0x2000) ? (clippedV | ~1023) : (clippedV & 1023);
592 if (bg_layer_[0].mosaicEnabled && mosaic_size_ > 1) {
593 y -= (y - mosaic_startline_) % mosaic_size_;
594 }
595 uint8_t ry = m7yFlip ? 255 - y : y;
596 m7startX = (((m7matrix[0] * clippedH) & ~63) + ((m7matrix[1] * ry) & ~63) +
597 ((m7matrix[1] * clippedV) & ~63) + (xCenter << 8));
598 m7startY = (((m7matrix[2] * clippedH) & ~63) + ((m7matrix[3] * ry) & ~63) +
599 ((m7matrix[3] * clippedV) & ~63) + (yCenter << 8));
600}
601
603 // called either right after CheckOverscan at (0,225), or at (0,240)
604 if (!forced_blank_) {
607 oam_second_write_ = false;
608 }
609 frame_interlace = interlace; // set if we have a interlaced frame
610}
611
612uint8_t Ppu::Read(uint8_t adr, bool latch) {
613 switch (adr) {
614 case 0x04:
615 case 0x14:
616 case 0x24:
617 case 0x05:
618 case 0x15:
619 case 0x25:
620 case 0x06:
621 case 0x16:
622 case 0x26:
623 case 0x08:
624 case 0x18:
625 case 0x28:
626 case 0x09:
627 case 0x19:
628 case 0x29:
629 case 0x0a:
630 case 0x1a:
631 case 0x2a: {
632 return ppu1_open_bus_;
633 }
634 case 0x34:
635 case 0x35:
636 case 0x36: {
637 int result = m7matrix[0] * (m7matrix[1] >> 8);
638 ppu1_open_bus_ = (result >> (8 * (adr - 0x34))) & 0xff;
639 return ppu1_open_bus_;
640 }
641 case 0x37: {
642 // TODO: only when ppulatch is set
643 if (latch) {
644 LatchHV();
645 }
646 return memory_.open_bus();
647 }
648 case 0x38: {
649 uint8_t ret = 0;
650 if (oam_in_high_) {
651 ret = high_oam_[((oam_adr_ & 0xf) << 1) | oam_second_write_];
652 if (oam_second_write_) {
653 oam_adr_++;
654 if (oam_adr_ == 0) oam_in_high_ = false;
655 }
656 } else {
657 if (!oam_second_write_) {
658 ret = oam[oam_adr_] & 0xff;
659 } else {
660 ret = oam[oam_adr_++] >> 8;
661 if (oam_adr_ == 0) oam_in_high_ = true;
662 }
663 }
665 ppu1_open_bus_ = ret;
666 return ret;
667 }
668 case 0x39: {
669 uint16_t val = vram_read_buffer_;
671 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
673 }
674 ppu1_open_bus_ = val & 0xff;
675 return val & 0xff;
676 }
677 case 0x3a: {
678 uint16_t val = vram_read_buffer_;
680 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
682 }
683 ppu1_open_bus_ = val >> 8;
684 return val >> 8;
685 }
686 case 0x3b: {
687 uint8_t ret = 0;
688 if (!cgram_second_write_) {
689 ret = cgram[cgram_pointer_] & 0xff;
690 } else {
691 ret = ((cgram[cgram_pointer_++] >> 8) & 0x7f) | (ppu2_open_bus_ & 0x80);
692 }
694 ppu2_open_bus_ = ret;
695 return ret;
696 }
697 case 0x3c: {
698 uint8_t val = 0;
699 if (h_count_second_) {
700 val = ((h_count_ >> 8) & 1) | (ppu2_open_bus_ & 0xfe);
701 } else {
702 val = h_count_ & 0xff;
703 }
705 ppu2_open_bus_ = val;
706 return val;
707 }
708 case 0x3d: {
709 uint8_t val = 0;
710 if (v_count_second_) {
711 val = ((v_count_ >> 8) & 1) | (ppu2_open_bus_ & 0xfe);
712 } else {
713 val = v_count_ & 0xff;
714 }
716 ppu2_open_bus_ = val;
717 return val;
718 }
719 case 0x3e: {
720 uint8_t val = 0x1; // ppu1 version (4 bit)
721 val |= ppu1_open_bus_ & 0x10;
722 val |= range_over_ << 6;
723 val |= time_over_ << 7;
724 ppu1_open_bus_ = val;
725 return val;
726 }
727 case 0x3f: {
728 uint8_t val = 0x3; // ppu2 version (4 bit)
729 val |= memory_.pal_timing() << 4; // ntsc/pal
730 val |= ppu2_open_bus_ & 0x20;
731 val |= counters_latched_ << 6;
732 val |= even_frame << 7;
733 if (latch) {
734 counters_latched_ = false;
735 h_count_second_ = false;
736 v_count_second_ = false;
737 }
738 ppu2_open_bus_ = val;
739 return val;
740 }
741 default: {
742 return memory_.open_bus();
743 }
744 }
745}
746
747void Ppu::Write(uint8_t adr, uint8_t val) {
748 switch (adr) {
749 case 0x00: {
750 // TODO: oam address reset when written on first line of vblank, (and when
751 // forced blank is disabled?)
752 brightness = val & 0xf;
753 forced_blank_ = val & 0x80;
754 break;
755 }
756 case 0x01: {
757 obj_size_ = val >> 5;
758 obj_tile_adr1_ = (val & 7) << 13;
759 obj_tile_adr2_ = obj_tile_adr1_ + (((val & 0x18) + 8) << 9);
760 break;
761 }
762 case 0x02: {
763 oam_adr_ = val;
766 oam_second_write_ = false;
767 break;
768 }
769 case 0x03: {
770 obj_priority_ = val & 0x80;
771 oam_in_high_ = val & 1;
774 oam_second_write_ = false;
775 break;
776 }
777 case 0x04: {
778 if (oam_in_high_) {
779 high_oam_[((oam_adr_ & 0xf) << 1) | oam_second_write_] = val;
780 if (oam_second_write_) {
781 oam_adr_++;
782 if (oam_adr_ == 0) oam_in_high_ = false;
783 }
784 } else {
785 if (!oam_second_write_) {
786 oam_buffer_ = val;
787 } else {
788 oam[oam_adr_++] = (val << 8) | oam_buffer_;
789 if (oam_adr_ == 0) oam_in_high_ = true;
790 }
791 }
793 break;
794 }
795 case 0x05: {
796 mode = val & 0x7;
797 bg3priority = val & 0x8;
798 bg_layer_[0].bigTiles = val & 0x10;
799 bg_layer_[1].bigTiles = val & 0x20;
800 bg_layer_[2].bigTiles = val & 0x40;
801 bg_layer_[3].bigTiles = val & 0x80;
802 break;
803 }
804 case 0x06: {
805 // TODO: mosaic line reset specifics
806 bg_layer_[0].mosaicEnabled = val & 0x1;
807 bg_layer_[1].mosaicEnabled = val & 0x2;
808 bg_layer_[2].mosaicEnabled = val & 0x4;
809 bg_layer_[3].mosaicEnabled = val & 0x8;
810 mosaic_size_ = (val >> 4) + 1;
812 break;
813 }
814 case 0x07:
815 case 0x08:
816 case 0x09:
817 case 0x0a: {
818 bg_layer_[adr - 7].tilemapWider = val & 0x1;
819 bg_layer_[adr - 7].tilemapHigher = val & 0x2;
820 bg_layer_[adr - 7].tilemapAdr = (val & 0xfc) << 8;
821 break;
822 }
823 case 0x0b: {
824 bg_layer_[0].tileAdr = (val & 0xf) << 12;
825 bg_layer_[1].tileAdr = (val & 0xf0) << 8;
826 break;
827 }
828 case 0x0c: {
829 bg_layer_[2].tileAdr = (val & 0xf) << 12;
830 bg_layer_[3].tileAdr = (val & 0xf0) << 8;
831 break;
832 }
833 case 0x0d: {
834 m7matrix[6] = ((val << 8) | m7prev) & 0x1fff;
835 m7prev = val;
836 // fallthrough to normal layer BG-HOFS
837 }
838 case 0x0f:
839 case 0x11:
840 case 0x13: {
841 bg_layer_[(adr - 0xd) / 2].hScroll =
842 ((val << 8) | (scroll_prev_ & 0xf8) | (scroll_prev2_ & 0x7)) & 0x3ff;
843 scroll_prev_ = val;
844 scroll_prev2_ = val;
845 break;
846 }
847 case 0x0e: {
848 m7matrix[7] = ((val << 8) | m7prev) & 0x1fff;
849 m7prev = val;
850 // fallthrough to normal layer BG-VOFS
851 }
852 case 0x10:
853 case 0x12:
854 case 0x14: {
855 bg_layer_[(adr - 0xe) / 2].vScroll = ((val << 8) | scroll_prev_) & 0x3ff;
856 scroll_prev_ = val;
857 break;
858 }
859 case 0x15: {
860 if ((val & 3) == 0) {
861 vram_increment_ = 1;
862 } else if ((val & 3) == 1) {
863 vram_increment_ = 32;
864 } else {
865 vram_increment_ = 128;
866 }
867 vram_remap_mode_ = (val & 0xc) >> 2;
868 vram_increment_on_high_ = val & 0x80;
869 break;
870 }
871 case 0x16: {
872 vram_pointer = (vram_pointer & 0xff00) | val;
873 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
874 break;
875 }
876 case 0x17: {
877 vram_pointer = (vram_pointer & 0x00ff) | (val << 8);
878 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
879 break;
880 }
881 case 0x18: {
882 // TODO: vram access during rendering (also cgram and oam)
883 uint16_t vramAdr = GetVramRemap();
884 vram[vramAdr & 0x7fff] = (vram[vramAdr & 0x7fff] & 0xff00) | val;
886 break;
887 }
888 case 0x19: {
889 uint16_t vramAdr = GetVramRemap();
890 vram[vramAdr & 0x7fff] = (vram[vramAdr & 0x7fff] & 0x00ff) | (val << 8);
892 break;
893 }
894 case 0x1a: {
895 m7largeField = val & 0x80;
896 m7charFill = val & 0x40;
897 m7yFlip = val & 0x2;
898 m7xFlip = val & 0x1;
899 break;
900 }
901 case 0x1b:
902 case 0x1c:
903 case 0x1d:
904 case 0x1e: {
905 m7matrix[adr - 0x1b] = (val << 8) | m7prev;
906 m7prev = val;
907 break;
908 }
909 case 0x1f:
910 case 0x20: {
911 m7matrix[adr - 0x1b] = ((val << 8) | m7prev) & 0x1fff;
912 m7prev = val;
913 break;
914 }
915 case 0x21: {
916 cgram_pointer_ = val;
917 cgram_second_write_ = false;
918 break;
919 }
920 case 0x22: {
921 if (!cgram_second_write_) {
922 cgram_buffer_ = val;
923 } else {
924 cgram[cgram_pointer_++] = (val << 8) | cgram_buffer_;
925 }
927 break;
928 }
929 case 0x23:
930 case 0x24:
931 case 0x25: {
932 windowLayer[(adr - 0x23) * 2].window1inversed = val & 0x1;
933 windowLayer[(adr - 0x23) * 2].window1enabled = val & 0x2;
934 windowLayer[(adr - 0x23) * 2].window2inversed = val & 0x4;
935 windowLayer[(adr - 0x23) * 2].window2enabled = val & 0x8;
936 windowLayer[(adr - 0x23) * 2 + 1].window1inversed = val & 0x10;
937 windowLayer[(adr - 0x23) * 2 + 1].window1enabled = val & 0x20;
938 windowLayer[(adr - 0x23) * 2 + 1].window2inversed = val & 0x40;
939 windowLayer[(adr - 0x23) * 2 + 1].window2enabled = val & 0x80;
940 break;
941 }
942 case 0x26: {
943 window1left = val;
944 break;
945 }
946 case 0x27: {
947 window1right = val;
948 break;
949 }
950 case 0x28: {
951 window2left = val;
952 break;
953 }
954 case 0x29: {
955 window2right = val;
956 break;
957 }
958 case 0x2a: {
959 windowLayer[0].maskLogic = val & 0x3;
960 windowLayer[1].maskLogic = (val >> 2) & 0x3;
961 windowLayer[2].maskLogic = (val >> 4) & 0x3;
962 windowLayer[3].maskLogic = (val >> 6) & 0x3;
963 break;
964 }
965 case 0x2b: {
966 windowLayer[4].maskLogic = val & 0x3;
967 windowLayer[5].maskLogic = (val >> 2) & 0x3;
968 break;
969 }
970 case 0x2c: {
971 layer_[0].mainScreenEnabled = val & 0x1;
972 layer_[1].mainScreenEnabled = val & 0x2;
973 layer_[2].mainScreenEnabled = val & 0x4;
974 layer_[3].mainScreenEnabled = val & 0x8;
975 layer_[4].mainScreenEnabled = val & 0x10;
976 break;
977 }
978 case 0x2d: {
979 layer_[0].subScreenEnabled = val & 0x1;
980 layer_[1].subScreenEnabled = val & 0x2;
981 layer_[2].subScreenEnabled = val & 0x4;
982 layer_[3].subScreenEnabled = val & 0x8;
983 layer_[4].subScreenEnabled = val & 0x10;
984 break;
985 }
986 case 0x2e: {
987 layer_[0].mainScreenWindowed = val & 0x1;
988 layer_[1].mainScreenWindowed = val & 0x2;
989 layer_[2].mainScreenWindowed = val & 0x4;
990 layer_[3].mainScreenWindowed = val & 0x8;
991 layer_[4].mainScreenWindowed = val & 0x10;
992 break;
993 }
994 case 0x2f: {
995 layer_[0].subScreenWindowed = val & 0x1;
996 layer_[1].subScreenWindowed = val & 0x2;
997 layer_[2].subScreenWindowed = val & 0x4;
998 layer_[3].subScreenWindowed = val & 0x8;
999 layer_[4].subScreenWindowed = val & 0x10;
1000 break;
1001 }
1002 case 0x30: {
1003 direct_color_ = val & 0x1;
1004 add_subscreen_ = val & 0x2;
1005 prevent_math_mode_ = (val & 0x30) >> 4;
1006 clip_mode_ = (val & 0xc0) >> 6;
1007 break;
1008 }
1009 case 0x31: {
1010 subtract_color_ = val & 0x80;
1011 half_color_ = val & 0x40;
1012 for (int i = 0; i < 6; i++) {
1013 math_enabled_array_[i] = val & (1 << i);
1014 }
1015 break;
1016 }
1017 case 0x32: {
1018 if (val & 0x80) fixed_color_b_ = val & 0x1f;
1019 if (val & 0x40) fixed_color_g_ = val & 0x1f;
1020 if (val & 0x20) fixed_color_r_ = val & 0x1f;
1021 break;
1022 }
1023 case 0x33: {
1024 interlace = val & 0x1;
1025 obj_interlace_ = val & 0x2;
1026 overscan_ = val & 0x4;
1027 pseudo_hires_ = val & 0x8;
1028 m7extBg = val & 0x40;
1029 break;
1030 }
1031 default: {
1032 break;
1033 }
1034 }
1035}
1036
1038 uint16_t adr = vram_pointer;
1039 switch (vram_remap_mode_) {
1040 case 0:
1041 return adr;
1042 case 1:
1043 return (adr & 0xff00) | ((adr & 0xe0) >> 5) | ((adr & 0x1f) << 3);
1044 case 2:
1045 return (adr & 0xfe00) | ((adr & 0x1c0) >> 6) | ((adr & 0x3f) << 3);
1046 case 3:
1047 return (adr & 0xfc00) | ((adr & 0x380) >> 7) | ((adr & 0x7f) << 3);
1048 }
1049 return adr;
1050}
1051
1052void Ppu::PutPixels(uint8_t* pixels) {
1053 for (int y = 0; y < (frame_overscan_ ? 239 : 224); y++) {
1054 int dest = y * 2 + (frame_overscan_ ? 2 : 16);
1055 int y1 = y, y2 = y + 239;
1056 if (!frame_interlace) {
1057 y1 = y + (even_frame ? 0 : 239);
1058 y2 = y1;
1059 }
1060 memcpy(pixels + (dest * 2048), &pixelBuffer[y1 * 2048], 2048);
1061 memcpy(pixels + ((dest + 1) * 2048), &pixelBuffer[y2 * 2048], 2048);
1062 }
1063 // clear top 2 lines, and following 14 and last 16 lines if not overscanning
1064 memset(pixels, 0, 2048 * 2);
1065 if (!frame_overscan_) {
1066 memset(pixels + (2 * 2048), 0, 2048 * 14);
1067 memset(pixels + (464 * 2048), 0, 2048 * 16);
1068 }
1069}
1070
1071} // namespace emu
1072} // namespace yaze
virtual auto pal_timing() const -> bool=0
virtual auto v_pos() const -> uint16_t=0
virtual uint8_t open_bus() const =0
uint16_t vram_pointer
Definition ppu.h:337
uint8_t obj_size_
Definition ppu.h:369
void LatchHV()
Definition ppu.h:271
void HandleOPT(int layer, int *lx, int *ly)
Definition ppu.cc:370
void HandlePixel(int x, int y)
Definition ppu.cc:157
void EvaluateSprites(int line)
Definition ppu.cc:486
bool direct_color_
Definition ppu.h:299
bool m7largeField
Definition ppu.h:390
uint16_t obj_tile_adr1_
Definition ppu.h:367
int32_t m7startX
Definition ppu.h:397
uint8_t ppu1_open_bus_
Definition ppu.h:427
uint8_t pixelBuffer[512 *4 *239 *2]
Definition ppu.h:418
uint8_t scroll_prev2_
Definition ppu.h:413
uint8_t vram_remap_mode_
Definition ppu.h:340
int GetPixel(int x, int y, bool sub, int *r, int *g, int *b)
Definition ppu.cc:244
BgLayer bg_layer_[4]
Definition ppu.h:411
bool counters_latched_
Definition ppu.h:426
uint8_t oam_buffer_
Definition ppu.h:360
uint8_t oam_adr_
Definition ppu.h:355
bool m7charFill
Definition ppu.h:391
bool pseudo_hires_
Definition ppu.h:295
bool frame_interlace
Definition ppu.h:297
bool v_count_second_
Definition ppu.h:425
bool m7yFlip
Definition ppu.h:393
bool forced_blank_
Definition ppu.h:290
uint8_t prevent_math_mode_
Definition ppu.h:375
void Reset()
Definition ppu.cc:39
bool GetWindowState(int layer, int x)
Definition ppu.cc:340
void CalculateMode7Starts(int y)
Definition ppu.cc:581
uint8_t m7prev
Definition ppu.h:389
bool subtract_color_
Definition ppu.h:378
uint16_t oam[0x100]
Definition ppu.h:353
uint8_t oam_adr_written_
Definition ppu.h:356
bool vram_increment_on_high_
Definition ppu.h:338
uint8_t scroll_prev_
Definition ppu.h:412
uint8_t clip_mode_
Definition ppu.h:374
uint8_t high_oam_[0x20]
Definition ppu.h:354
void HandleFrameStart()
Definition ppu.cc:137
void Write(uint8_t adr, uint8_t val)
Definition ppu.cc:747
uint16_t h_count_
Definition ppu.h:422
std::array< uint8_t, 256 > obj_pixel_buffer_
Definition ppu.h:370
uint16_t vram_increment_
Definition ppu.h:339
bool obj_priority_
Definition ppu.h:366
void PutPixels(uint8_t *pixel_data)
Definition ppu.cc:1052
std::array< uint8_t, 256 > obj_priority_buffer_
Definition ppu.h:371
bool time_over_
Definition ppu.h:363
uint8_t Read(uint8_t adr, bool latch)
Definition ppu.cc:612
uint8_t mosaic_startline_
Definition ppu.h:409
uint8_t window2left
Definition ppu.h:404
bool frame_overscan_
Definition ppu.h:286
bool even_frame
Definition ppu.h:294
bool interlace
Definition ppu.h:296
uint16_t obj_tile_adr2_
Definition ppu.h:368
bool oam_in_high_written_
Definition ppu.h:358
Memory & memory_
Definition ppu.h:435
Layer layer_[5]
Definition ppu.h:385
uint8_t fixed_color_g_
Definition ppu.h:381
bool oam_in_high_
Definition ppu.h:357
bool oam_second_write_
Definition ppu.h:359
bool obj_interlace_
Definition ppu.h:365
bool h_count_second_
Definition ppu.h:424
uint8_t mode
Definition ppu.h:292
bool m7xFlip
Definition ppu.h:392
uint8_t window2right
Definition ppu.h:405
uint8_t pixelOutputFormat
Definition ppu.h:419
uint16_t GetOffsetValue(int col, int row)
Definition ppu.cc:405
uint8_t window1left
Definition ppu.h:402
uint16_t vram[0x8000]
Definition ppu.h:336
int16_t m7matrix[8]
Definition ppu.h:388
void HandleVblank()
Definition ppu.cc:602
bool half_color_
Definition ppu.h:379
uint8_t ppu2_open_bus_
Definition ppu.h:428
bool add_subscreen_
Definition ppu.h:377
uint8_t mosaic_size_
Definition ppu.h:414
bool m7extBg
Definition ppu.h:394
uint8_t cgram_buffer_
Definition ppu.h:350
bool range_over_
Definition ppu.h:364
uint8_t fixed_color_b_
Definition ppu.h:382
uint8_t cgram_pointer_
Definition ppu.h:348
bool math_enabled_array_[6]
Definition ppu.h:376
int GetPixelForBgLayer(int x, int y, int layer, bool priority)
Definition ppu.cc:419
bool overscan_
Definition ppu.h:287
int32_t m7startY
Definition ppu.h:398
uint16_t GetVramRemap()
Definition ppu.cc:1037
int GetPixelForMode7(int x, int layer, bool priority)
Definition ppu.cc:321
uint16_t cgram[0x100]
Definition ppu.h:344
uint8_t fixed_color_r_
Definition ppu.h:380
uint8_t window1right
Definition ppu.h:403
uint16_t v_count_
Definition ppu.h:423
WindowLayer windowLayer[6]
Definition ppu.h:401
uint16_t vram_read_buffer_
Definition ppu.h:341
bool cgram_second_write_
Definition ppu.h:349
void RunLine(int line)
Definition ppu.cc:145
bool bg3priority
Definition ppu.h:293
uint8_t brightness
Definition ppu.h:291
Main namespace for the application.
bool tilemapHigher
Definition ppu.h:222
uint16_t vScroll
Definition ppu.h:220
bool mosaicEnabled
Definition ppu.h:226
uint16_t hScroll
Definition ppu.h:219
bool tilemapWider
Definition ppu.h:221
uint16_t tileAdr
Definition ppu.h:224
uint16_t tilemapAdr
Definition ppu.h:223
bool subScreenWindowed
Definition ppu.h:215
bool mainScreenWindowed
Definition ppu.h:214
bool subScreenEnabled
Definition ppu.h:213
bool mainScreenEnabled
Definition ppu.h:212
uint8_t maskLogic
Definition ppu.h:234