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 <cstdio>
5#include <iostream>
6#include <vector>
7
9
10namespace yaze {
11namespace emu {
12
13// array for layer definitions per mode:
14// 0-7: mode 0-7; 8: mode 1 + l3prio; 9: mode 7 + extbg
15
16// 0-3; layers 1-4; 4: sprites; 5: nonexistent
17static const int kLayersPerMode[10][12] = {
18 {4, 0, 1, 4, 0, 1, 4, 2, 3, 4, 2, 3}, {4, 0, 1, 4, 0, 1, 4, 2, 4, 2, 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, 1, 4, 0, 4, 1, 5, 5, 5, 5}, {4, 0, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5},
21 {4, 0, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5}, {4, 4, 4, 0, 4, 5, 5, 5, 5, 5, 5, 5},
22 {2, 4, 0, 1, 4, 0, 1, 4, 4, 2, 5, 5}, {4, 4, 1, 4, 0, 4, 1, 5, 5, 5, 5, 5}};
23
24static const int kPrioritysPerMode[10][12] = {
25 {3, 1, 1, 2, 0, 0, 1, 1, 1, 0, 0, 0}, {3, 1, 1, 2, 0, 0, 1, 1, 0, 0, 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, 1, 0, 0, 0, 5, 5, 5, 5}, {3, 1, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5},
28 {3, 1, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5}, {3, 2, 1, 0, 0, 5, 5, 5, 5, 5, 5, 5},
29 {1, 3, 1, 1, 2, 0, 0, 1, 0, 0, 5, 5}, {3, 2, 1, 1, 0, 0, 0, 5, 5, 5, 5, 5}};
30
31static const int kLayerCountPerMode[10] = {12, 10, 8, 8, 8, 8, 6, 5, 10, 7};
32
33static const int kBitDepthsPerMode[10][4] = {
34 {2, 2, 2, 2}, {4, 4, 2, 5}, {4, 4, 5, 5}, {8, 4, 5, 5}, {8, 2, 5, 5},
35 {4, 2, 5, 5}, {4, 5, 5, 5}, {8, 5, 5, 5}, {4, 4, 2, 5}, {8, 7, 5, 5}};
36
37static const int kSpriteSizes[8][2] = {{8, 16}, {8, 32}, {8, 64}, {16, 32},
38 {16, 64}, {32, 64}, {16, 32}, {16, 32}};
39
40void Ppu::Reset() {
41 memset(vram, 0, sizeof(vram));
42 vram_pointer = 0;
47 memset(cgram, 0, sizeof(cgram));
49 cgram_second_write_ = false;
50 cgram_buffer_ = 0;
51 memset(oam, 0, sizeof(oam));
52 memset(high_oam_, 0, sizeof(high_oam_));
53 oam_adr_ = 0;
55 oam_in_high_ = false;
57 oam_second_write_ = false;
58 oam_buffer_ = 0;
59 obj_priority_ = false;
62 obj_size_ = 0;
63 obj_pixel_buffer_.fill(0);
65 time_over_ = false;
66 range_over_ = false;
67 obj_interlace_ = false;
68 for (int i = 0; i < 4; i++) {
69 bg_layer_[i].hScroll = 0;
70 bg_layer_[i].vScroll = 0;
71 bg_layer_[i].tilemapWider = false;
72 bg_layer_[i].tilemapHigher = false;
73 bg_layer_[i].tilemapAdr = 0;
74 bg_layer_[i].tileAdr = 0;
75 bg_layer_[i].bigTiles = false;
76 bg_layer_[i].mosaicEnabled = false;
77 }
78 scroll_prev_ = 0;
79 scroll_prev2_ = 0;
80 mosaic_size_ = 1;
82 for (int i = 0; i < 5; i++) {
83 layer_[i].mainScreenEnabled = false;
84 layer_[i].subScreenEnabled = false;
85 layer_[i].mainScreenWindowed = false;
86 layer_[i].subScreenWindowed = false;
87 }
88 memset(m7matrix, 0, sizeof(m7matrix));
89 m7prev = 0;
90 m7largeField = false;
91 m7charFill = false;
92 m7xFlip = false;
93 m7yFlip = false;
94 m7extBg = false;
95 m7startX = 0;
96 m7startY = 0;
97 for (int i = 0; i < 6; i++) {
98 windowLayer[i].window1enabled = false;
99 windowLayer[i].window2enabled = false;
100 windowLayer[i].window1inversed = false;
101 windowLayer[i].window2inversed = false;
102 windowLayer[i].maskLogic = 0;
103 }
104 window1left = 0;
105 window1right = 0;
106 window2left = 0;
107 window2right = 0;
108 clip_mode_ = 0;
110 add_subscreen_ = false;
111 subtract_color_ = false;
112 half_color_ = false;
113 memset(math_enabled_array_, 0, sizeof(math_enabled_array_));
114 fixed_color_r_ = 0;
115 fixed_color_g_ = 0;
116 fixed_color_b_ = 0;
117 forced_blank_ = true;
118 brightness = 15; // SNES hardware default is maximum brightness
119 mode = 0;
120 bg3priority = false;
121 even_frame = false;
122 pseudo_hires_ = false;
123 overscan_ = false;
124 frame_overscan_ = false;
125 interlace = false;
126 frame_interlace = false;
127 direct_color_ = false;
128 h_count_ = 0;
129 v_count_ = 0;
130 h_count_second_ = false;
131 v_count_second_ = false;
132 counters_latched_ = false;
133 ppu1_open_bus_ = 0;
134 ppu2_open_bus_ = 0;
135 memset(pixelBuffer, 0, sizeof(pixelBuffer));
137}
138
140 // called at (0, 0)
142 range_over_ = false;
143 time_over_ = false;
145}
146
147void Ppu::StartLine(int line) {
148 current_scanline_ = line;
150
151 // evaluate sprites
152 obj_pixel_buffer_.fill(0);
153 if (!forced_blank_)
154 EvaluateSprites(line - 1);
155 // actual line
156 if (mode == 7)
158}
159
160void Ppu::CatchUp(int h_pos) {
161 // h_pos is in master cycles. 1 pixel = 4 cycles.
162 // Visible pixels are 0-255, corresponding to h_pos 0-1024 roughly.
163 int target_x = h_pos / 4;
164
165 // Clamp to screen width
166 if (target_x > 256) target_x = 256;
167 if (target_x <= last_rendered_x_) return;
168
169 for (int x = last_rendered_x_; x < target_x; x++) {
171 }
172 last_rendered_x_ = target_x;
173}
174
175void Ppu::RunLine(int line) {
176 // Legacy wrapper - renders the whole line at once
177 StartLine(line);
178 CatchUp(2000); // Ensure full line (256 pixels * 4 = 1024)
179}
180
181void Ppu::HandlePixel(int x, int y) {
182 int r = 0, r2 = 0;
183 int g = 0, g2 = 0;
184 int b = 0, b2 = 0;
185 if (!forced_blank_) {
186 int mainLayer = GetPixel(x, y, false, &r, &g, &b);
187 bool colorWindowState = GetWindowState(5, x);
188 if (clip_mode_ == 3 || (clip_mode_ == 2 && colorWindowState) ||
189 (clip_mode_ == 1 && !colorWindowState)) {
190 r = 0;
191 g = 0;
192 b = 0;
193 }
194 int secondLayer = 5; // backdrop
195 bool mathEnabled = mainLayer < 6 && math_enabled_array_[mainLayer] &&
196 !(prevent_math_mode_ == 3 ||
197 (prevent_math_mode_ == 2 && colorWindowState) ||
198 (prevent_math_mode_ == 1 && !colorWindowState));
199 if ((mathEnabled && add_subscreen_) || pseudo_hires_ || mode == 5 ||
200 mode == 6) {
201 secondLayer = GetPixel(x, y, true, &r2, &g2, &b2);
202 }
203 // TODO: subscreen pixels can be clipped to black as well
204 // TODO: math for subscreen pixels (add/sub sub to main)
205 if (mathEnabled) {
206 if (subtract_color_) {
207 r -= (add_subscreen_ && secondLayer != 5) ? r2 : fixed_color_r_;
208 g -= (add_subscreen_ && secondLayer != 5) ? g2 : fixed_color_g_;
209 b -= (add_subscreen_ && secondLayer != 5) ? b2 : fixed_color_b_;
210 } else {
211 r += (add_subscreen_ && secondLayer != 5) ? r2 : fixed_color_r_;
212 g += (add_subscreen_ && secondLayer != 5) ? g2 : fixed_color_g_;
213 b += (add_subscreen_ && secondLayer != 5) ? b2 : fixed_color_b_;
214 }
215 if (half_color_ && (secondLayer != 5 || !add_subscreen_)) {
216 r >>= 1;
217 g >>= 1;
218 b >>= 1;
219 }
220 if (r > 31)
221 r = 31;
222 if (g > 31)
223 g = 31;
224 if (b > 31)
225 b = 31;
226 if (r < 0)
227 r = 0;
228 if (g < 0)
229 g = 0;
230 if (b < 0)
231 b = 0;
232 }
233 if (!(pseudo_hires_ || mode == 5 || mode == 6)) {
234 r2 = r;
235 g2 = g;
236 b2 = b;
237 }
238 }
239 int row = (y - 1) + (even_frame ? 0 : 239);
240
241 // SDL_PIXELFORMAT_ARGB8888 with pixelOutputFormat=0 (BGRX)
242 // Memory layout: [B][G][R][A] at offsets 0,1,2,3 respectively
243 // Convert 5-bit SNES color (0-31) to 8-bit (0-255) via (val << 3) | (val >>
244 // 2) Two pixels per X position for hi-res support:
245 // pixel1 at x*8 + 0..3, pixel2 at x*8 + 4..7
246
247 // Apply brightness
248 r = r * brightness / 15;
249 g = g * brightness / 15;
250 b = b * brightness / 15;
251 r2 = r2 * brightness / 15;
252 g2 = g2 * brightness / 15;
253 b2 = b2 * brightness / 15;
254
255 // First pixel (hi-res/main screen)
256 pixelBuffer[row * 2048 + x * 8 + 0 + pixelOutputFormat] =
257 ((b2 << 3) | (b2 >> 2)); // Blue channel
258 pixelBuffer[row * 2048 + x * 8 + 1 + pixelOutputFormat] =
259 ((g2 << 3) | (g2 >> 2)); // Green channel
260 pixelBuffer[row * 2048 + x * 8 + 2 + pixelOutputFormat] =
261 ((r2 << 3) | (r2 >> 2)); // Red channel
262 pixelBuffer[row * 2048 + x * 8 + 3 + pixelOutputFormat] =
263 0xFF; // Alpha (opaque)
264
265 // Second pixel (lo-res/subscreen)
266 pixelBuffer[row * 2048 + x * 8 + 4 + pixelOutputFormat] =
267 ((b << 3) | (b >> 2)); // Blue channel
268 pixelBuffer[row * 2048 + x * 8 + 5 + pixelOutputFormat] =
269 ((g << 3) | (g >> 2)); // Green channel
270 pixelBuffer[row * 2048 + x * 8 + 6 + pixelOutputFormat] =
271 ((r << 3) | (r >> 2)); // Red channel
272 pixelBuffer[row * 2048 + x * 8 + 7 + pixelOutputFormat] =
273 0xFF; // Alpha (opaque)
274}
275
276int Ppu::GetPixel(int x, int y, bool subscreen, int* r, int* g, int* b) {
277 // figure out which color is on this location on main- or subscreen, sets it
278 // in r, g, b
279 // returns which layer it is: 0-3 for bg layer, 4 or 6 for sprites (depending
280 // on palette), 5 for backdrop
281 int actMode = mode == 1 && bg3priority ? 8 : mode;
282 actMode = mode == 7 && m7extBg ? 9 : actMode;
283 int layer = 5;
284 int pixel = 0;
285 for (int i = 0; i < kLayerCountPerMode[actMode]; i++) {
286 int curLayer = kLayersPerMode[actMode][i];
287 int curPriority = kPrioritysPerMode[actMode][i];
288 bool layerActive = false;
289 if (!subscreen) {
290 layerActive = layer_[curLayer].mainScreenEnabled &&
291 (!layer_[curLayer].mainScreenWindowed ||
292 !GetWindowState(curLayer, x));
293 } else {
294 layerActive =
295 layer_[curLayer].subScreenEnabled &&
296 (!layer_[curLayer].subScreenWindowed || !GetWindowState(curLayer, x));
297 }
298 if (layerActive) {
299 if (curLayer < 4) {
300 // bg layer
301 int lx = x;
302 int ly = y;
303 if (bg_layer_[curLayer].mosaicEnabled && mosaic_size_ > 1) {
304 lx -= lx % mosaic_size_;
305 ly -= (ly - mosaic_startline_) % mosaic_size_;
306 }
307 if (mode == 7) {
308 pixel = GetPixelForMode7(lx, curLayer, curPriority);
309 } else {
310 lx += bg_layer_[curLayer].hScroll;
311 if (mode == 5 || mode == 6) {
312 lx *= 2;
313 lx += (subscreen || bg_layer_[curLayer].mosaicEnabled) ? 0 : 1;
314 if (interlace) {
315 ly *= 2;
316 ly += (even_frame || bg_layer_[curLayer].mosaicEnabled) ? 0 : 1;
317 }
318 }
319 ly += bg_layer_[curLayer].vScroll;
320 if (mode == 2 || mode == 4 || mode == 6) {
321 HandleOPT(curLayer, &lx, &ly);
322 }
323 pixel =
324 GetPixelForBgLayer(lx & 0x3ff, ly & 0x3ff, curLayer, curPriority);
325 }
326 } else {
327 // get a pixel from the sprite buffer
328 pixel = 0;
329 if (obj_priority_buffer_[x] == curPriority)
330 pixel = obj_pixel_buffer_[x];
331 }
332 }
333 if (pixel > 0) {
334 layer = curLayer;
335 break;
336 }
337 }
338 if (direct_color_ && layer < 4 && kBitDepthsPerMode[actMode][layer] == 8) {
339 *r = ((pixel & 0x7) << 2) | ((pixel & 0x100) >> 7);
340 *g = ((pixel & 0x38) >> 1) | ((pixel & 0x200) >> 8);
341 *b = ((pixel & 0xc0) >> 3) | ((pixel & 0x400) >> 8);
342 } else {
343 uint16_t color = cgram[pixel & 0xff];
344 *r = color & 0x1f;
345 *g = (color >> 5) & 0x1f;
346 *b = (color >> 10) & 0x1f;
347 }
348 if (layer == 4 && pixel < 0xc0)
349 layer = 6; // sprites with palette color < 0xc0
350 return layer;
351}
352
353int Ppu::GetPixelForMode7(int x, int layer, bool priority) {
354 uint8_t rx = m7xFlip ? 255 - x : x;
355 int xPos = (m7startX + m7matrix[0] * rx) >> 8;
356 int yPos = (m7startY + m7matrix[2] * rx) >> 8;
357 bool outsideMap = xPos < 0 || xPos >= 1024 || yPos < 0 || yPos >= 1024;
358 xPos &= 0x3ff;
359 yPos &= 0x3ff;
360 if (!m7largeField)
361 outsideMap = false;
362 uint8_t tile = outsideMap ? 0 : vram[(yPos >> 3) * 128 + (xPos >> 3)] & 0xff;
363 uint8_t pixel = outsideMap && !m7charFill
364 ? 0
365 : vram[tile * 64 + (yPos & 7) * 8 + (xPos & 7)] >> 8;
366 if (layer == 1) {
367 if (((bool)(pixel & 0x80)) != priority)
368 return 0;
369 return pixel & 0x7f;
370 }
371 return pixel;
372}
373
374bool Ppu::GetWindowState(int layer, int x) {
375 if (!windowLayer[layer].window1enabled &&
376 !windowLayer[layer].window2enabled) {
377 return false;
378 }
379 if (windowLayer[layer].window1enabled && !windowLayer[layer].window2enabled) {
380 bool test = x >= window1left && x <= window1right;
381 return windowLayer[layer].window1inversed ? !test : test;
382 }
383 if (!windowLayer[layer].window1enabled && windowLayer[layer].window2enabled) {
384 bool test = x >= window2left && x <= window2right;
385 return windowLayer[layer].window2inversed ? !test : test;
386 }
387 bool test1 = x >= window1left && x <= window1right;
388 bool test2 = x >= window2left && x <= window2right;
389 if (windowLayer[layer].window1inversed)
390 test1 = !test1;
391 if (windowLayer[layer].window2inversed)
392 test2 = !test2;
393 switch (windowLayer[layer].maskLogic) {
394 case 0:
395 return test1 || test2;
396 case 1:
397 return test1 && test2;
398 case 2:
399 return test1 != test2;
400 case 3:
401 return test1 == test2;
402 }
403 return false;
404}
405
406void Ppu::HandleOPT(int layer, int* lx, int* ly) {
407 int x = *lx;
408 int y = *ly;
409 int column = 0;
410 if (mode == 6) {
411 column = ((x - (x & 0xf)) - ((bg_layer_[layer].hScroll * 2) & 0xfff0)) >> 4;
412 } else {
413 column = ((x - (x & 0x7)) - (bg_layer_[layer].hScroll & 0xfff8)) >> 3;
414 }
415 if (column > 0) {
416 // fetch offset values from layer 3 tilemap
417 int valid = layer == 0 ? 0x2000 : 0x4000;
418 uint16_t hOffset = GetOffsetValue(column - 1, 0);
419 uint16_t vOffset = 0;
420 if (mode == 4) {
421 if (hOffset & 0x8000) {
422 vOffset = hOffset;
423 hOffset = 0;
424 }
425 } else {
426 vOffset = GetOffsetValue(column - 1, 1);
427 }
428 if (mode == 6) {
429 // TODO: not sure if correct
430 if (hOffset & valid)
431 *lx = (((hOffset & 0x3f8) + (column * 8)) * 2) | (x & 0xf);
432 } else {
433 if (hOffset & valid)
434 *lx = ((hOffset & 0x3f8) + (column * 8)) | (x & 0x7);
435 }
436 // TODO: not sure if correct for interlace
437 if (vOffset & valid)
438 *ly = (vOffset & 0x3ff) + (y - bg_layer_[layer].vScroll);
439 }
440}
441
442uint16_t Ppu::GetOffsetValue(int col, int row) {
443 int x = col * 8 + bg_layer_[2].hScroll;
444 int y = row * 8 + bg_layer_[2].vScroll;
445 int tileBits = bg_layer_[2].bigTiles ? 4 : 3;
446 int tileHighBit = bg_layer_[2].bigTiles ? 0x200 : 0x100;
447 uint16_t tilemapAdr =
449 (((y >> tileBits) & 0x1f) << 5 | ((x >> tileBits) & 0x1f));
450 if ((x & tileHighBit) && bg_layer_[2].tilemapWider)
451 tilemapAdr += 0x400;
452 if ((y & tileHighBit) && bg_layer_[2].tilemapHigher)
453 tilemapAdr += bg_layer_[2].tilemapWider ? 0x800 : 0x400;
454 return vram[tilemapAdr & 0x7fff];
455}
456
457int Ppu::GetPixelForBgLayer(int x, int y, int layer, bool priority) {
458 // figure out address of tilemap word and read it
459 bool wideTiles = bg_layer_[layer].bigTiles || mode == 5 || mode == 6;
460 int tileBitsX = wideTiles ? 4 : 3;
461 int tileHighBitX = wideTiles ? 0x200 : 0x100;
462 int tileBitsY = bg_layer_[layer].bigTiles ? 4 : 3;
463 int tileHighBitY = bg_layer_[layer].bigTiles ? 0x200 : 0x100;
464 uint16_t tilemapAdr =
465 bg_layer_[layer].tilemapAdr +
466 (((y >> tileBitsY) & 0x1f) << 5 | ((x >> tileBitsX) & 0x1f));
467 if ((x & tileHighBitX) && bg_layer_[layer].tilemapWider)
468 tilemapAdr += 0x400;
469 if ((y & tileHighBitY) && bg_layer_[layer].tilemapHigher)
470 tilemapAdr += bg_layer_[layer].tilemapWider ? 0x800 : 0x400;
471 uint16_t tile = vram[tilemapAdr & 0x7fff];
472 // check priority, get palette
473 if (((bool)(tile & 0x2000)) != priority)
474 return 0; // wrong priority
475 int paletteNum = (tile & 0x1c00) >> 10;
476 // figure out position within tile
477 int row = (tile & 0x8000) ? 7 - (y & 0x7) : (y & 0x7);
478 int col = (tile & 0x4000) ? (x & 0x7) : 7 - (x & 0x7);
479 int tileNum = tile & 0x3ff;
480 if (wideTiles) {
481 // if unflipped right half of tile, or flipped left half of tile
482 if (((bool)(x & 8)) ^ ((bool)(tile & 0x4000)))
483 tileNum += 1;
484 }
485 if (bg_layer_[layer].bigTiles) {
486 // if unflipped bottom half of tile, or flipped upper half of tile
487 if (((bool)(y & 8)) ^ ((bool)(tile & 0x8000)))
488 tileNum += 0x10;
489 }
490 // read tiledata, ajust palette for mode 0
491 int bitDepth = kBitDepthsPerMode[mode][layer];
492 if (mode == 0)
493 paletteNum += 8 * layer;
494 // plane 1 (always)
495 int paletteSize = 4;
496 uint16_t plane1 = vram[(bg_layer_[layer].tileAdr +
497 ((tileNum & 0x3ff) * 4 * bitDepth) + row) &
498 0x7fff];
499 int pixel = (plane1 >> col) & 1;
500 pixel |= ((plane1 >> (8 + col)) & 1) << 1;
501 // plane 2 (for 4bpp, 8bpp)
502 if (bitDepth > 2) {
503 paletteSize = 16;
504 uint16_t plane2 = vram[(bg_layer_[layer].tileAdr +
505 ((tileNum & 0x3ff) * 4 * bitDepth) + 8 + row) &
506 0x7fff];
507 pixel |= ((plane2 >> col) & 1) << 2;
508 pixel |= ((plane2 >> (8 + col)) & 1) << 3;
509 }
510 // plane 3 & 4 (for 8bpp)
511 if (bitDepth > 4) {
512 paletteSize = 256;
513 uint16_t plane3 = vram[(bg_layer_[layer].tileAdr +
514 ((tileNum & 0x3ff) * 4 * bitDepth) + 16 + row) &
515 0x7fff];
516 pixel |= ((plane3 >> col) & 1) << 4;
517 pixel |= ((plane3 >> (8 + col)) & 1) << 5;
518 uint16_t plane4 = vram[(bg_layer_[layer].tileAdr +
519 ((tileNum & 0x3ff) * 4 * bitDepth) + 24 + row) &
520 0x7fff];
521 pixel |= ((plane4 >> col) & 1) << 6;
522 pixel |= ((plane4 >> (8 + col)) & 1) << 7;
523 }
524 // return cgram index, or 0 if transparent, palette number in bits 10-8 for
525 // 8-color layers
526 return pixel == 0 ? 0 : paletteSize * paletteNum + pixel;
527}
528
529void Ppu::EvaluateSprites(int line) {
530 // TODO: rectangular sprites, wierdness with sprites at -256
531 uint8_t index = obj_priority_ ? (oam_adr_ & 0xfe) : 0;
532 int spritesFound = 0;
533 int tilesFound = 0;
534 uint8_t foundSprites[32] = {};
535 // iterate over oam to find sprites in range
536 for (int i = 0; i < 128; i++) {
537 uint8_t y = oam[index] >> 8;
538 // check if the sprite is on this line and get the sprite size
539 uint8_t row = line - y;
540 int spriteSize =
541 kSpriteSizes[obj_size_]
542 [(high_oam_[index >> 3] >> ((index & 7) + 1)) & 1];
543 int spriteHeight = obj_interlace_ ? spriteSize / 2 : spriteSize;
544 if (row < spriteHeight) {
545 // in y-range, get the x location, using the high bit as well
546 int x = oam[index] & 0xff;
547 x |= ((high_oam_[index >> 3] >> (index & 7)) & 1) << 8;
548 if (x > 255)
549 x -= 512;
550 // if in x-range, record
551 if (x > -spriteSize) {
552 // break if we found 32 sprites already
553 spritesFound++;
554 if (spritesFound > 32) {
555 range_over_ = true;
556 spritesFound = 32;
557 break;
558 }
559 foundSprites[spritesFound - 1] = index;
560 }
561 }
562 index += 2;
563 }
564 // iterate over found sprites backwards to fetch max 34 tile slivers
565 for (int i = spritesFound; i > 0; i--) {
566 index = foundSprites[i - 1];
567 uint8_t y = oam[index] >> 8;
568 uint8_t row = line - y;
569 int spriteSize =
570 kSpriteSizes[obj_size_]
571 [(high_oam_[index >> 3] >> ((index & 7) + 1)) & 1];
572 int x = oam[index] & 0xff;
573 x |= ((high_oam_[index >> 3] >> (index & 7)) & 1) << 8;
574 if (x > 255)
575 x -= 512;
576 if (x > -spriteSize) {
577 // update row according to obj-interlace
578 if (obj_interlace_)
579 row = row * 2 + (even_frame ? 0 : 1);
580 // get some data for the sprite and y-flip row if needed
581 int tile = oam[index + 1] & 0xff;
582 int palette = (oam[index + 1] & 0xe00) >> 9;
583 bool hFlipped = oam[index + 1] & 0x4000;
584 if (oam[index + 1] & 0x8000)
585 row = spriteSize - 1 - row;
586 // fetch all tiles in x-range
587 for (int col = 0; col < spriteSize; col += 8) {
588 if (col + x > -8 && col + x < 256) {
589 // break if we found > 34 8*1 slivers already
590 tilesFound++;
591 if (tilesFound > 34) {
592 time_over_ = true;
593 break;
594 }
595 // figure out which tile this uses, looping within 16x16 pages, and
596 // get it's data
597 int usedCol = hFlipped ? spriteSize - 1 - col : col;
598 uint8_t usedTile = (((tile >> 4) + (row / 8)) << 4) |
599 (((tile & 0xf) + (usedCol / 8)) & 0xf);
600 uint16_t objAdr =
601 (oam[index + 1] & 0x100) ? obj_tile_adr2_ : obj_tile_adr1_;
602 uint16_t plane1 =
603 vram[(objAdr + usedTile * 16 + (row & 0x7)) & 0x7fff];
604 uint16_t plane2 =
605 vram[(objAdr + usedTile * 16 + 8 + (row & 0x7)) & 0x7fff];
606 // go over each pixel
607 for (int px = 0; px < 8; px++) {
608 int shift = hFlipped ? px : 7 - px;
609 int pixel = (plane1 >> shift) & 1;
610 pixel |= ((plane1 >> (8 + shift)) & 1) << 1;
611 pixel |= ((plane2 >> shift) & 1) << 2;
612 pixel |= ((plane2 >> (8 + shift)) & 1) << 3;
613 // draw it in the buffer if there is a pixel here
614 int screenCol = col + x + px;
615 if (pixel > 0 && screenCol >= 0 && screenCol < 256) {
616 obj_pixel_buffer_[screenCol] = 0x80 + 16 * palette + pixel;
617 obj_priority_buffer_[screenCol] = (oam[index + 1] & 0x3000) >> 12;
618 }
619 }
620 }
621 }
622 if (tilesFound > 34)
623 break; // break out of sprite-loop if max tiles found
624 }
625 }
626}
627
629 // expand 13-bit values to signed values
630 int hScroll = ((int16_t)(m7matrix[6] << 3)) >> 3;
631 int vScroll = ((int16_t)(m7matrix[7] << 3)) >> 3;
632 int xCenter = ((int16_t)(m7matrix[4] << 3)) >> 3;
633 int yCenter = ((int16_t)(m7matrix[5] << 3)) >> 3;
634 // do calculation
635 int clippedH = hScroll - xCenter;
636 int clippedV = vScroll - yCenter;
637 clippedH = (clippedH & 0x2000) ? (clippedH | ~1023) : (clippedH & 1023);
638 clippedV = (clippedV & 0x2000) ? (clippedV | ~1023) : (clippedV & 1023);
639 if (bg_layer_[0].mosaicEnabled && mosaic_size_ > 1) {
640 y -= (y - mosaic_startline_) % mosaic_size_;
641 }
642 uint8_t ry = m7yFlip ? 255 - y : y;
643 m7startX = (((m7matrix[0] * clippedH) & ~63) + ((m7matrix[1] * ry) & ~63) +
644 ((m7matrix[1] * clippedV) & ~63) + (xCenter << 8));
645 m7startY = (((m7matrix[2] * clippedH) & ~63) + ((m7matrix[3] * ry) & ~63) +
646 ((m7matrix[3] * clippedV) & ~63) + (yCenter << 8));
647}
648
650 // called either right after CheckOverscan at (0,225), or at (0,240)
651 if (!forced_blank_) {
654 oam_second_write_ = false;
655 }
656 frame_interlace = interlace; // set if we have a interlaced frame
657
658 // Debug: Dump PPU state every 120 frames (~2 seconds)
659 if (enable_debug_dump_) {
660 static int vblank_dump_counter = 0;
661 if (++vblank_dump_counter >= 120) {
662 vblank_dump_counter = 0;
663 DumpState();
664 }
665 }
666}
667
668uint8_t Ppu::Read(uint8_t adr, bool latch) {
669 switch (adr) {
670 case 0x04:
671 case 0x14:
672 case 0x24:
673 case 0x05:
674 case 0x15:
675 case 0x25:
676 case 0x06:
677 case 0x16:
678 case 0x26:
679 case 0x08:
680 case 0x18:
681 case 0x28:
682 case 0x09:
683 case 0x19:
684 case 0x29:
685 case 0x0a:
686 case 0x1a:
687 case 0x2a: {
688 return ppu1_open_bus_;
689 }
690 case 0x34:
691 case 0x35:
692 case 0x36: {
693 int result = m7matrix[0] * (m7matrix[1] >> 8);
694 ppu1_open_bus_ = (result >> (8 * (adr - 0x34))) & 0xff;
695 return ppu1_open_bus_;
696 }
697 case 0x37: {
698 // TODO: only when ppulatch is set
699 if (latch) {
700 LatchHV();
701 }
702 return memory_.open_bus();
703 }
704 case 0x38: {
705 uint8_t ret = 0;
706 if (oam_in_high_) {
707 ret = high_oam_[((oam_adr_ & 0xf) << 1) | oam_second_write_];
708 if (oam_second_write_) {
709 oam_adr_++;
710 if (oam_adr_ == 0)
711 oam_in_high_ = false;
712 }
713 } else {
714 if (!oam_second_write_) {
715 ret = oam[oam_adr_] & 0xff;
716 } else {
717 ret = oam[oam_adr_++] >> 8;
718 if (oam_adr_ == 0)
719 oam_in_high_ = true;
720 }
721 }
723 ppu1_open_bus_ = ret;
724 return ret;
725 }
726 case 0x39: {
727 uint16_t val = vram_read_buffer_;
729 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
731 }
732 ppu1_open_bus_ = val & 0xff;
733 return val & 0xff;
734 }
735 case 0x3a: {
736 uint16_t val = vram_read_buffer_;
738 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
740 }
741 ppu1_open_bus_ = val >> 8;
742 return val >> 8;
743 }
744 case 0x3b: {
745 uint8_t ret = 0;
746 if (!cgram_second_write_) {
747 ret = cgram[cgram_pointer_] & 0xff;
748 } else {
749 ret = ((cgram[cgram_pointer_++] >> 8) & 0x7f) | (ppu2_open_bus_ & 0x80);
750 }
752 ppu2_open_bus_ = ret;
753 return ret;
754 }
755 case 0x3c: {
756 uint8_t val = 0;
757 if (h_count_second_) {
758 val = ((h_count_ >> 8) & 1) | (ppu2_open_bus_ & 0xfe);
759 } else {
760 val = h_count_ & 0xff;
761 }
763 ppu2_open_bus_ = val;
764 return val;
765 }
766 case 0x3d: {
767 uint8_t val = 0;
768 if (v_count_second_) {
769 val = ((v_count_ >> 8) & 1) | (ppu2_open_bus_ & 0xfe);
770 } else {
771 val = v_count_ & 0xff;
772 }
774 ppu2_open_bus_ = val;
775 return val;
776 }
777 case 0x3e: {
778 uint8_t val = 0x1; // ppu1 version (4 bit)
779 val |= ppu1_open_bus_ & 0x10;
780 val |= range_over_ << 6;
781 val |= time_over_ << 7;
782 ppu1_open_bus_ = val;
783 return val;
784 }
785 case 0x3f: {
786 uint8_t val = 0x3; // ppu2 version (4 bit)
787 val |= memory_.pal_timing() << 4; // ntsc/pal
788 val |= ppu2_open_bus_ & 0x20;
789 val |= counters_latched_ << 6;
790 val |= even_frame << 7;
791 if (latch) {
792 counters_latched_ = false;
793 h_count_second_ = false;
794 v_count_second_ = false;
795 }
796 ppu2_open_bus_ = val;
797 return val;
798 }
799 default: {
800 return memory_.open_bus();
801 }
802 }
803}
804
805void Ppu::Write(uint8_t adr, uint8_t val) {
806 switch (adr) {
807 case 0x00: {
808 // TODO: oam address reset when written on first line of vblank, (and when
809 // forced blank is disabled?)
810 brightness = val & 0xf;
811 forced_blank_ = val & 0x80;
812 break;
813 }
814 case 0x01: {
815 obj_size_ = val >> 5;
816 obj_tile_adr1_ = (val & 7) << 13;
817 obj_tile_adr2_ = obj_tile_adr1_ + (((val & 0x18) + 8) << 9);
818 break;
819 }
820 case 0x02: {
821 oam_adr_ = val;
824 oam_second_write_ = false;
825 break;
826 }
827 case 0x03: {
828 obj_priority_ = val & 0x80;
829 oam_in_high_ = val & 1;
832 oam_second_write_ = false;
833 break;
834 }
835 case 0x04: {
836 if (oam_in_high_) {
837 high_oam_[((oam_adr_ & 0xf) << 1) | oam_second_write_] = val;
838 if (oam_second_write_) {
839 oam_adr_++;
840 if (oam_adr_ == 0)
841 oam_in_high_ = false;
842 }
843 } else {
844 if (!oam_second_write_) {
845 oam_buffer_ = val;
846 } else {
847 oam[oam_adr_++] = (val << 8) | oam_buffer_;
848 if (oam_adr_ == 0)
849 oam_in_high_ = true;
850 }
851 }
853 break;
854 }
855 case 0x05: {
856 mode = val & 0x7;
857 bg3priority = val & 0x8;
858 bg_layer_[0].bigTiles = val & 0x10;
859 bg_layer_[1].bigTiles = val & 0x20;
860 bg_layer_[2].bigTiles = val & 0x40;
861 bg_layer_[3].bigTiles = val & 0x80;
862 break;
863 }
864 case 0x06: {
865 // TODO: mosaic line reset specifics
866 bg_layer_[0].mosaicEnabled = val & 0x1;
867 bg_layer_[1].mosaicEnabled = val & 0x2;
868 bg_layer_[2].mosaicEnabled = val & 0x4;
869 bg_layer_[3].mosaicEnabled = val & 0x8;
870 mosaic_size_ = (val >> 4) + 1;
872 break;
873 }
874 case 0x07:
875 case 0x08:
876 case 0x09:
877 case 0x0a: {
878 bg_layer_[adr - 7].tilemapWider = val & 0x1;
879 bg_layer_[adr - 7].tilemapHigher = val & 0x2;
880 bg_layer_[adr - 7].tilemapAdr = (val & 0xfc) << 8;
881 break;
882 }
883 case 0x0b: {
884 bg_layer_[0].tileAdr = (val & 0xf) << 12;
885 bg_layer_[1].tileAdr = (val & 0xf0) << 8;
886 break;
887 }
888 case 0x0c: {
889 bg_layer_[2].tileAdr = (val & 0xf) << 12;
890 bg_layer_[3].tileAdr = (val & 0xf0) << 8;
891 break;
892 }
893 case 0x0d: {
894 m7matrix[6] = ((val << 8) | m7prev) & 0x1fff;
895 m7prev = val;
896 // fallthrough to normal layer BG-HOFS
897 }
898 case 0x0f:
899 case 0x11:
900 case 0x13: {
901 bg_layer_[(adr - 0xd) / 2].hScroll =
902 ((val << 8) | (scroll_prev_ & 0xf8) | (scroll_prev2_ & 0x7)) & 0x3ff;
903 scroll_prev_ = val;
904 scroll_prev2_ = val;
905 break;
906 }
907 case 0x0e: {
908 m7matrix[7] = ((val << 8) | m7prev) & 0x1fff;
909 m7prev = val;
910 // fallthrough to normal layer BG-VOFS
911 }
912 case 0x10:
913 case 0x12:
914 case 0x14: {
915 bg_layer_[(adr - 0xe) / 2].vScroll = ((val << 8) | scroll_prev_) & 0x3ff;
916 scroll_prev_ = val;
917 break;
918 }
919 case 0x15: {
920 if ((val & 3) == 0) {
921 vram_increment_ = 1;
922 } else if ((val & 3) == 1) {
923 vram_increment_ = 32;
924 } else {
925 vram_increment_ = 128;
926 }
927 vram_remap_mode_ = (val & 0xc) >> 2;
928 vram_increment_on_high_ = val & 0x80;
929 break;
930 }
931 case 0x16: {
932 vram_pointer = (vram_pointer & 0xff00) | val;
933 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
934 break;
935 }
936 case 0x17: {
937 vram_pointer = (vram_pointer & 0x00ff) | (val << 8);
938 vram_read_buffer_ = vram[GetVramRemap() & 0x7fff];
939 break;
940 }
941 case 0x18: {
942 // TODO: vram access during rendering (also cgram and oam)
943 uint16_t vramAdr = GetVramRemap();
944 vram[vramAdr & 0x7fff] = (vram[vramAdr & 0x7fff] & 0xff00) | val;
947 break;
948 }
949 case 0x19: {
950 uint16_t vramAdr = GetVramRemap();
951 vram[vramAdr & 0x7fff] = (vram[vramAdr & 0x7fff] & 0x00ff) | (val << 8);
954 break;
955 }
956 case 0x1a: {
957 m7largeField = val & 0x80;
958 m7charFill = val & 0x40;
959 m7yFlip = val & 0x2;
960 m7xFlip = val & 0x1;
961 break;
962 }
963 case 0x1b:
964 case 0x1c:
965 case 0x1d:
966 case 0x1e: {
967 m7matrix[adr - 0x1b] = (val << 8) | m7prev;
968 m7prev = val;
969 break;
970 }
971 case 0x1f:
972 case 0x20: {
973 m7matrix[adr - 0x1b] = ((val << 8) | m7prev) & 0x1fff;
974 m7prev = val;
975 break;
976 }
977 case 0x21: {
978 cgram_pointer_ = val;
979 cgram_second_write_ = false;
980 break;
981 }
982 case 0x22: {
983 if (!cgram_second_write_) {
984 cgram_buffer_ = val;
985 } else {
986 cgram[cgram_pointer_++] = (val << 8) | cgram_buffer_;
987 }
989 break;
990 }
991 case 0x23:
992 case 0x24:
993 case 0x25: {
994 windowLayer[(adr - 0x23) * 2].window1inversed = val & 0x1;
995 windowLayer[(adr - 0x23) * 2].window1enabled = val & 0x2;
996 windowLayer[(adr - 0x23) * 2].window2inversed = val & 0x4;
997 windowLayer[(adr - 0x23) * 2].window2enabled = val & 0x8;
998 windowLayer[(adr - 0x23) * 2 + 1].window1inversed = val & 0x10;
999 windowLayer[(adr - 0x23) * 2 + 1].window1enabled = val & 0x20;
1000 windowLayer[(adr - 0x23) * 2 + 1].window2inversed = val & 0x40;
1001 windowLayer[(adr - 0x23) * 2 + 1].window2enabled = val & 0x80;
1002 break;
1003 }
1004 case 0x26: {
1005 window1left = val;
1006 break;
1007 }
1008 case 0x27: {
1009 window1right = val;
1010 break;
1011 }
1012 case 0x28: {
1013 window2left = val;
1014 break;
1015 }
1016 case 0x29: {
1017 window2right = val;
1018 break;
1019 }
1020 case 0x2a: {
1021 windowLayer[0].maskLogic = val & 0x3;
1022 windowLayer[1].maskLogic = (val >> 2) & 0x3;
1023 windowLayer[2].maskLogic = (val >> 4) & 0x3;
1024 windowLayer[3].maskLogic = (val >> 6) & 0x3;
1025 break;
1026 }
1027 case 0x2b: {
1028 windowLayer[4].maskLogic = val & 0x3;
1029 windowLayer[5].maskLogic = (val >> 2) & 0x3;
1030 break;
1031 }
1032 case 0x2c: {
1033 layer_[0].mainScreenEnabled = val & 0x1;
1034 layer_[1].mainScreenEnabled = val & 0x2;
1035 layer_[2].mainScreenEnabled = val & 0x4;
1036 layer_[3].mainScreenEnabled = val & 0x8;
1037 layer_[4].mainScreenEnabled = val & 0x10;
1038 break;
1039 }
1040 case 0x2d: {
1041 layer_[0].subScreenEnabled = val & 0x1;
1042 layer_[1].subScreenEnabled = val & 0x2;
1043 layer_[2].subScreenEnabled = val & 0x4;
1044 layer_[3].subScreenEnabled = val & 0x8;
1045 layer_[4].subScreenEnabled = val & 0x10;
1046 break;
1047 }
1048 case 0x2e: {
1049 layer_[0].mainScreenWindowed = val & 0x1;
1050 layer_[1].mainScreenWindowed = val & 0x2;
1051 layer_[2].mainScreenWindowed = val & 0x4;
1052 layer_[3].mainScreenWindowed = val & 0x8;
1053 layer_[4].mainScreenWindowed = val & 0x10;
1054 break;
1055 }
1056 case 0x2f: {
1057 layer_[0].subScreenWindowed = val & 0x1;
1058 layer_[1].subScreenWindowed = val & 0x2;
1059 layer_[2].subScreenWindowed = val & 0x4;
1060 layer_[3].subScreenWindowed = val & 0x8;
1061 layer_[4].subScreenWindowed = val & 0x10;
1062 break;
1063 }
1064 case 0x30: {
1065 direct_color_ = val & 0x1;
1066 add_subscreen_ = val & 0x2;
1067 prevent_math_mode_ = (val & 0x30) >> 4;
1068 clip_mode_ = (val & 0xc0) >> 6;
1069 break;
1070 }
1071 case 0x31: {
1072 subtract_color_ = val & 0x80;
1073 half_color_ = val & 0x40;
1074 for (int i = 0; i < 6; i++) {
1075 math_enabled_array_[i] = val & (1 << i);
1076 }
1077 break;
1078 }
1079 case 0x32: {
1080 if (val & 0x80)
1081 fixed_color_b_ = val & 0x1f;
1082 if (val & 0x40)
1083 fixed_color_g_ = val & 0x1f;
1084 if (val & 0x20)
1085 fixed_color_r_ = val & 0x1f;
1086 break;
1087 }
1088 case 0x33: {
1089 interlace = val & 0x1;
1090 obj_interlace_ = val & 0x2;
1091 overscan_ = val & 0x4;
1092 pseudo_hires_ = val & 0x8;
1093 m7extBg = val & 0x40;
1094 break;
1095 }
1096 default: {
1097 break;
1098 }
1099 }
1100}
1101
1103 uint16_t adr = vram_pointer;
1104 switch (vram_remap_mode_) {
1105 case 0:
1106 return adr;
1107 case 1:
1108 return (adr & 0xff00) | ((adr & 0xe0) >> 5) | ((adr & 0x1f) << 3);
1109 case 2:
1110 return (adr & 0xfe00) | ((adr & 0x1c0) >> 6) | ((adr & 0x3f) << 3);
1111 case 3:
1112 return (adr & 0xfc00) | ((adr & 0x380) >> 7) | ((adr & 0x7f) << 3);
1113 }
1114 return adr;
1115}
1116
1117void Ppu::PutPixels(uint8_t* pixels) {
1118 for (int y = 0; y < (frame_overscan_ ? 239 : 224); y++) {
1119 int dest = y * 2 + (frame_overscan_ ? 2 : 16);
1120 int y1 = y, y2 = y + 239;
1121 if (!frame_interlace) {
1122 y1 = y + (even_frame ? 0 : 239);
1123 y2 = y1;
1124 }
1125 memcpy(pixels + (dest * 2048), &pixelBuffer[y1 * 2048], 2048);
1126 memcpy(pixels + ((dest + 1) * 2048), &pixelBuffer[y2 * 2048], 2048);
1127 }
1128 // clear top 2 lines, and following 14 and last 16 lines if not overscanning
1129 memset(pixels, 0, 2048 * 2);
1130 if (!frame_overscan_) {
1131 memset(pixels + (2 * 2048), 0, 2048 * 14);
1132 memset(pixels + (464 * 2048), 0, 2048 * 16);
1133 }
1134}
1135
1136void Ppu::DumpState() const {
1137 printf("=== PPU State Dump ===\n");
1138 printf("$2100: forced_blank=%d brightness=%d\n", forced_blank_ ? 1 : 0,
1139 brightness);
1140 printf("$2105: mode=%d bg3priority=%d\n", mode, bg3priority ? 1 : 0);
1141 printf("$212C (Main Screen): BG1=%d BG2=%d BG3=%d BG4=%d OBJ=%d\n",
1142 layer_[0].mainScreenEnabled ? 1 : 0,
1143 layer_[1].mainScreenEnabled ? 1 : 0,
1144 layer_[2].mainScreenEnabled ? 1 : 0,
1145 layer_[3].mainScreenEnabled ? 1 : 0,
1146 layer_[4].mainScreenEnabled ? 1 : 0);
1147 printf("$212D (Sub Screen): BG1=%d BG2=%d BG3=%d BG4=%d OBJ=%d\n",
1148 layer_[0].subScreenEnabled ? 1 : 0,
1149 layer_[1].subScreenEnabled ? 1 : 0,
1150 layer_[2].subScreenEnabled ? 1 : 0,
1151 layer_[3].subScreenEnabled ? 1 : 0,
1152 layer_[4].subScreenEnabled ? 1 : 0);
1153 for (int i = 0; i < 4; i++) {
1154 printf("BG%d: tilemapAdr=$%04X tileAdr=$%04X hScroll=%d vScroll=%d "
1155 "bigTiles=%d\n",
1156 i + 1, bg_layer_[i].tilemapAdr, bg_layer_[i].tileAdr,
1157 bg_layer_[i].hScroll, bg_layer_[i].vScroll,
1158 bg_layer_[i].bigTiles ? 1 : 0);
1159 }
1160 // Check VRAM at BG1 tilemap address first to get actual palette number
1161 uint16_t tm_addr = bg_layer_[0].tilemapAdr;
1162 uint16_t first_entry = vram[tm_addr];
1163 int actual_pal = (first_entry >> 10) & 7;
1164 printf("First tilemap entry: $%04X (tile=$%03X, pal=%d, pri=%d, hflip=%d, "
1165 "vflip=%d)\n",
1166 first_entry, first_entry & 0x3FF, actual_pal,
1167 (first_entry >> 13) & 1, (first_entry >> 14) & 1,
1168 (first_entry >> 15) & 1);
1169 // Check palette entries - dump palette 0 and the actual palette being used
1170 printf("CGRAM Pal0[0-15]: %04X %04X %04X %04X %04X %04X %04X %04X "
1171 "%04X %04X %04X %04X %04X %04X %04X %04X\n",
1172 cgram[0], cgram[1], cgram[2], cgram[3], cgram[4], cgram[5], cgram[6],
1173 cgram[7], cgram[8], cgram[9], cgram[10], cgram[11], cgram[12],
1174 cgram[13], cgram[14], cgram[15]);
1175 // Dump the ACTUAL palette being used by the first tile
1176 int pal_start = actual_pal * 16;
1177 printf("CGRAM Pal%d[0-15]: %04X %04X %04X %04X %04X %04X %04X %04X "
1178 "%04X %04X %04X %04X %04X %04X %04X %04X\n",
1179 actual_pal, cgram[pal_start], cgram[pal_start + 1],
1180 cgram[pal_start + 2], cgram[pal_start + 3], cgram[pal_start + 4],
1181 cgram[pal_start + 5], cgram[pal_start + 6], cgram[pal_start + 7],
1182 cgram[pal_start + 8], cgram[pal_start + 9], cgram[pal_start + 10],
1183 cgram[pal_start + 11], cgram[pal_start + 12], cgram[pal_start + 13],
1184 cgram[pal_start + 14], cgram[pal_start + 15]);
1185 // Check VRAM at BG1 tilemap address (first 8 words)
1186 printf("VRAM@$%04X (BG1 tilemap): %04X %04X %04X %04X %04X %04X %04X %04X\n",
1187 tm_addr, vram[tm_addr], vram[tm_addr + 1], vram[tm_addr + 2],
1188 vram[tm_addr + 3], vram[tm_addr + 4], vram[tm_addr + 5],
1189 vram[tm_addr + 6], vram[tm_addr + 7]);
1190 // Check actual tile data for tile index from tilemap (4bpp = 16 words/tile)
1191 uint16_t first_tile = vram[tm_addr] & 0x3FF; // Lower 10 bits = tile index
1192 uint16_t actual_tile_addr = bg_layer_[0].tileAdr + (first_tile * 16);
1193 printf("Tile $%03X @ VRAM $%04X: %04X %04X %04X %04X %04X %04X %04X %04X\n",
1194 first_tile, actual_tile_addr, vram[actual_tile_addr & 0x7FFF],
1195 vram[(actual_tile_addr + 1) & 0x7FFF],
1196 vram[(actual_tile_addr + 2) & 0x7FFF],
1197 vram[(actual_tile_addr + 3) & 0x7FFF],
1198 vram[(actual_tile_addr + 4) & 0x7FFF],
1199 vram[(actual_tile_addr + 5) & 0x7FFF],
1200 vram[(actual_tile_addr + 6) & 0x7FFF],
1201 vram[(actual_tile_addr + 7) & 0x7FFF]);
1202 printf("=== End PPU Dump ===\n");
1203 fflush(stdout);
1204}
1205
1206void Ppu::SaveState(std::ostream& stream) {
1207 // POD members
1208 stream.write(reinterpret_cast<const char*>(&frame_overscan_), sizeof(frame_overscan_));
1209 stream.write(reinterpret_cast<const char*>(&overscan_), sizeof(overscan_));
1210 stream.write(reinterpret_cast<const char*>(&forced_blank_), sizeof(forced_blank_));
1211 stream.write(reinterpret_cast<const char*>(&brightness), sizeof(brightness));
1212 stream.write(reinterpret_cast<const char*>(&mode), sizeof(mode));
1213 stream.write(reinterpret_cast<const char*>(&bg3priority), sizeof(bg3priority));
1214 stream.write(reinterpret_cast<const char*>(&even_frame), sizeof(even_frame));
1215 stream.write(reinterpret_cast<const char*>(&pseudo_hires_), sizeof(pseudo_hires_));
1216 stream.write(reinterpret_cast<const char*>(&interlace), sizeof(interlace));
1217 stream.write(reinterpret_cast<const char*>(&frame_interlace), sizeof(frame_interlace));
1218 stream.write(reinterpret_cast<const char*>(&direct_color_), sizeof(direct_color_));
1219
1220 stream.write(reinterpret_cast<const char*>(&cycle_count_), sizeof(cycle_count_));
1221 stream.write(reinterpret_cast<const char*>(&current_scanline_), sizeof(current_scanline_));
1222
1223 // Arrays
1224 stream.write(reinterpret_cast<const char*>(vram), sizeof(vram));
1225 stream.write(reinterpret_cast<const char*>(&vram_pointer), sizeof(vram_pointer));
1226 stream.write(reinterpret_cast<const char*>(&vram_increment_on_high_), sizeof(vram_increment_on_high_));
1227 stream.write(reinterpret_cast<const char*>(&vram_increment_), sizeof(vram_increment_));
1228 stream.write(reinterpret_cast<const char*>(&vram_remap_mode_), sizeof(vram_remap_mode_));
1229 stream.write(reinterpret_cast<const char*>(&vram_read_buffer_), sizeof(vram_read_buffer_));
1230
1231 stream.write(reinterpret_cast<const char*>(cgram), sizeof(cgram));
1232 stream.write(reinterpret_cast<const char*>(&last_rendered_x_), sizeof(last_rendered_x_));
1233 stream.write(reinterpret_cast<const char*>(&cgram_pointer_), sizeof(cgram_pointer_));
1234 stream.write(reinterpret_cast<const char*>(&cgram_second_write_), sizeof(cgram_second_write_));
1235 stream.write(reinterpret_cast<const char*>(&cgram_buffer_), sizeof(cgram_buffer_));
1236
1237 stream.write(reinterpret_cast<const char*>(oam), sizeof(oam));
1238 stream.write(reinterpret_cast<const char*>(high_oam_), sizeof(high_oam_));
1239 stream.write(reinterpret_cast<const char*>(&oam_adr_), sizeof(oam_adr_));
1240 stream.write(reinterpret_cast<const char*>(&oam_adr_written_), sizeof(oam_adr_written_));
1241 stream.write(reinterpret_cast<const char*>(&oam_in_high_), sizeof(oam_in_high_));
1242 stream.write(reinterpret_cast<const char*>(&oam_in_high_written_), sizeof(oam_in_high_written_));
1243 stream.write(reinterpret_cast<const char*>(&oam_second_write_), sizeof(oam_second_write_));
1244 stream.write(reinterpret_cast<const char*>(&oam_buffer_), sizeof(oam_buffer_));
1245
1246 stream.write(reinterpret_cast<const char*>(&obj_priority_), sizeof(obj_priority_));
1247 stream.write(reinterpret_cast<const char*>(&obj_tile_adr1_), sizeof(obj_tile_adr1_));
1248 stream.write(reinterpret_cast<const char*>(&obj_tile_adr2_), sizeof(obj_tile_adr2_));
1249 stream.write(reinterpret_cast<const char*>(&obj_size_), sizeof(obj_size_));
1250
1251 // std::array buffers
1252 stream.write(reinterpret_cast<const char*>(obj_pixel_buffer_.data()), sizeof(obj_pixel_buffer_));
1253 stream.write(reinterpret_cast<const char*>(obj_priority_buffer_.data()), sizeof(obj_priority_buffer_));
1254
1255 stream.write(reinterpret_cast<const char*>(&time_over_), sizeof(time_over_));
1256 stream.write(reinterpret_cast<const char*>(&range_over_), sizeof(range_over_));
1257 stream.write(reinterpret_cast<const char*>(&obj_interlace_), sizeof(obj_interlace_));
1258
1259 stream.write(reinterpret_cast<const char*>(&clip_mode_), sizeof(clip_mode_));
1260 stream.write(reinterpret_cast<const char*>(&prevent_math_mode_), sizeof(prevent_math_mode_));
1261 stream.write(reinterpret_cast<const char*>(math_enabled_array_), sizeof(math_enabled_array_));
1262 stream.write(reinterpret_cast<const char*>(&add_subscreen_), sizeof(add_subscreen_));
1263 stream.write(reinterpret_cast<const char*>(&subtract_color_), sizeof(subtract_color_));
1264 stream.write(reinterpret_cast<const char*>(&half_color_), sizeof(half_color_));
1265 stream.write(reinterpret_cast<const char*>(&fixed_color_r_), sizeof(fixed_color_r_));
1266 stream.write(reinterpret_cast<const char*>(&fixed_color_g_), sizeof(fixed_color_g_));
1267 stream.write(reinterpret_cast<const char*>(&fixed_color_b_), sizeof(fixed_color_b_));
1268
1269 for (const auto& layer : layer_) {
1270 uint8_t flags[4] = {static_cast<uint8_t>(layer.mainScreenEnabled),
1271 static_cast<uint8_t>(layer.subScreenEnabled),
1272 static_cast<uint8_t>(layer.mainScreenWindowed),
1273 static_cast<uint8_t>(layer.subScreenWindowed)};
1274 stream.write(reinterpret_cast<const char*>(flags), sizeof(flags));
1275 }
1276
1277 stream.write(reinterpret_cast<const char*>(m7matrix), sizeof(m7matrix));
1278 stream.write(reinterpret_cast<const char*>(&m7prev), sizeof(m7prev));
1279 stream.write(reinterpret_cast<const char*>(&m7largeField), sizeof(m7largeField));
1280 stream.write(reinterpret_cast<const char*>(&m7charFill), sizeof(m7charFill));
1281 stream.write(reinterpret_cast<const char*>(&m7xFlip), sizeof(m7xFlip));
1282 stream.write(reinterpret_cast<const char*>(&m7yFlip), sizeof(m7yFlip));
1283 stream.write(reinterpret_cast<const char*>(&m7extBg), sizeof(m7extBg));
1284 stream.write(reinterpret_cast<const char*>(&m7startX), sizeof(m7startX));
1285 stream.write(reinterpret_cast<const char*>(&m7startY), sizeof(m7startY));
1286
1287 for (const auto& win : windowLayer) {
1288 uint8_t encoded[5] = {static_cast<uint8_t>(win.window1enabled),
1289 static_cast<uint8_t>(win.window2enabled),
1290 static_cast<uint8_t>(win.window1inversed),
1291 static_cast<uint8_t>(win.window2inversed), win.maskLogic};
1292 stream.write(reinterpret_cast<const char*>(encoded), sizeof(encoded));
1293 }
1294 stream.write(reinterpret_cast<const char*>(&window1left), sizeof(window1left));
1295 stream.write(reinterpret_cast<const char*>(&window1right), sizeof(window1right));
1296 stream.write(reinterpret_cast<const char*>(&window2left), sizeof(window2left));
1297 stream.write(reinterpret_cast<const char*>(&window2right), sizeof(window2right));
1298
1299 // BgLayer array (POD structs)
1300 for (const auto& bg : bg_layer_) {
1301 stream.write(reinterpret_cast<const char*>(&bg.hScroll), sizeof(bg.hScroll));
1302 stream.write(reinterpret_cast<const char*>(&bg.vScroll), sizeof(bg.vScroll));
1303 uint8_t flags[4] = {static_cast<uint8_t>(bg.tilemapWider),
1304 static_cast<uint8_t>(bg.tilemapHigher),
1305 static_cast<uint8_t>(bg.bigTiles),
1306 static_cast<uint8_t>(bg.mosaicEnabled)};
1307 stream.write(reinterpret_cast<const char*>(flags), sizeof(flags));
1308 stream.write(reinterpret_cast<const char*>(&bg.tilemapAdr), sizeof(bg.tilemapAdr));
1309 stream.write(reinterpret_cast<const char*>(&bg.tileAdr), sizeof(bg.tileAdr));
1310 }
1311
1312 stream.write(reinterpret_cast<const char*>(&scroll_prev_), sizeof(scroll_prev_));
1313 stream.write(reinterpret_cast<const char*>(&scroll_prev2_), sizeof(scroll_prev2_));
1314 stream.write(reinterpret_cast<const char*>(&mosaic_size_), sizeof(mosaic_size_));
1315 stream.write(reinterpret_cast<const char*>(&mosaic_startline_), sizeof(mosaic_startline_));
1316
1317 stream.write(reinterpret_cast<const char*>(&pixelOutputFormat), sizeof(pixelOutputFormat));
1318
1319 stream.write(reinterpret_cast<const char*>(&h_count_), sizeof(h_count_));
1320 stream.write(reinterpret_cast<const char*>(&v_count_), sizeof(v_count_));
1321 stream.write(reinterpret_cast<const char*>(&h_count_second_), sizeof(h_count_second_));
1322 stream.write(reinterpret_cast<const char*>(&v_count_second_), sizeof(v_count_second_));
1323 stream.write(reinterpret_cast<const char*>(&counters_latched_), sizeof(counters_latched_));
1324 stream.write(reinterpret_cast<const char*>(&ppu1_open_bus_), sizeof(ppu1_open_bus_));
1325 stream.write(reinterpret_cast<const char*>(&ppu2_open_bus_), sizeof(ppu2_open_bus_));
1326
1327 stream.write(reinterpret_cast<const char*>(&tile_data_size_), sizeof(tile_data_size_));
1328 stream.write(reinterpret_cast<const char*>(&vram_base_address_), sizeof(vram_base_address_));
1329 stream.write(reinterpret_cast<const char*>(&tilemap_base_address_), sizeof(tilemap_base_address_));
1330 stream.write(reinterpret_cast<const char*>(&screen_brightness_), sizeof(screen_brightness_));
1331
1332 stream.write(reinterpret_cast<const char*>(&bg_mode_), sizeof(bg_mode_));
1333
1334 // Registers
1335 stream.write(reinterpret_cast<const char*>(&oam_size_), sizeof(oam_size_));
1336 stream.write(reinterpret_cast<const char*>(&oam_address_), sizeof(oam_address_));
1337 stream.write(reinterpret_cast<const char*>(&mosaic_), sizeof(mosaic_));
1338 stream.write(reinterpret_cast<const char*>(&bgsc_), sizeof(bgsc_));
1339 stream.write(reinterpret_cast<const char*>(&bgnba_), sizeof(bgnba_));
1340 stream.write(reinterpret_cast<const char*>(&bghofs_), sizeof(bghofs_));
1341 stream.write(reinterpret_cast<const char*>(&bgvofs_), sizeof(bgvofs_));
1342}
1343
1344void Ppu::LoadState(std::istream& stream) {
1345 // POD members
1346 stream.read(reinterpret_cast<char*>(&frame_overscan_), sizeof(frame_overscan_));
1347 stream.read(reinterpret_cast<char*>(&overscan_), sizeof(overscan_));
1348 stream.read(reinterpret_cast<char*>(&forced_blank_), sizeof(forced_blank_));
1349 stream.read(reinterpret_cast<char*>(&brightness), sizeof(brightness));
1350 stream.read(reinterpret_cast<char*>(&mode), sizeof(mode));
1351 stream.read(reinterpret_cast<char*>(&bg3priority), sizeof(bg3priority));
1352 stream.read(reinterpret_cast<char*>(&even_frame), sizeof(even_frame));
1353 stream.read(reinterpret_cast<char*>(&pseudo_hires_), sizeof(pseudo_hires_));
1354 stream.read(reinterpret_cast<char*>(&interlace), sizeof(interlace));
1355 stream.read(reinterpret_cast<char*>(&frame_interlace), sizeof(frame_interlace));
1356 stream.read(reinterpret_cast<char*>(&direct_color_), sizeof(direct_color_));
1357
1358 stream.read(reinterpret_cast<char*>(&cycle_count_), sizeof(cycle_count_));
1359 stream.read(reinterpret_cast<char*>(&current_scanline_), sizeof(current_scanline_));
1360
1361 // Arrays
1362 stream.read(reinterpret_cast<char*>(vram), sizeof(vram));
1363 stream.read(reinterpret_cast<char*>(&vram_pointer), sizeof(vram_pointer));
1364 stream.read(reinterpret_cast<char*>(&vram_increment_on_high_), sizeof(vram_increment_on_high_));
1365 stream.read(reinterpret_cast<char*>(&vram_increment_), sizeof(vram_increment_));
1366 stream.read(reinterpret_cast<char*>(&vram_remap_mode_), sizeof(vram_remap_mode_));
1367 stream.read(reinterpret_cast<char*>(&vram_read_buffer_), sizeof(vram_read_buffer_));
1368
1369 stream.read(reinterpret_cast<char*>(cgram), sizeof(cgram));
1370 stream.read(reinterpret_cast<char*>(&last_rendered_x_), sizeof(last_rendered_x_));
1371 stream.read(reinterpret_cast<char*>(&cgram_pointer_), sizeof(cgram_pointer_));
1372 stream.read(reinterpret_cast<char*>(&cgram_second_write_), sizeof(cgram_second_write_));
1373 stream.read(reinterpret_cast<char*>(&cgram_buffer_), sizeof(cgram_buffer_));
1374
1375 stream.read(reinterpret_cast<char*>(oam), sizeof(oam));
1376 stream.read(reinterpret_cast<char*>(high_oam_), sizeof(high_oam_));
1377 stream.read(reinterpret_cast<char*>(&oam_adr_), sizeof(oam_adr_));
1378 stream.read(reinterpret_cast<char*>(&oam_adr_written_), sizeof(oam_adr_written_));
1379 stream.read(reinterpret_cast<char*>(&oam_in_high_), sizeof(oam_in_high_));
1380 stream.read(reinterpret_cast<char*>(&oam_in_high_written_), sizeof(oam_in_high_written_));
1381 stream.read(reinterpret_cast<char*>(&oam_second_write_), sizeof(oam_second_write_));
1382 stream.read(reinterpret_cast<char*>(&oam_buffer_), sizeof(oam_buffer_));
1383
1384 stream.read(reinterpret_cast<char*>(&obj_priority_), sizeof(obj_priority_));
1385 stream.read(reinterpret_cast<char*>(&obj_tile_adr1_), sizeof(obj_tile_adr1_));
1386 stream.read(reinterpret_cast<char*>(&obj_tile_adr2_), sizeof(obj_tile_adr2_));
1387 stream.read(reinterpret_cast<char*>(&obj_size_), sizeof(obj_size_));
1388
1389 // std::array buffers
1390 stream.read(reinterpret_cast<char*>(obj_pixel_buffer_.data()), sizeof(obj_pixel_buffer_));
1391 stream.read(reinterpret_cast<char*>(obj_priority_buffer_.data()), sizeof(obj_priority_buffer_));
1392
1393 stream.read(reinterpret_cast<char*>(&time_over_), sizeof(time_over_));
1394 stream.read(reinterpret_cast<char*>(&range_over_), sizeof(range_over_));
1395 stream.read(reinterpret_cast<char*>(&obj_interlace_), sizeof(obj_interlace_));
1396
1397 stream.read(reinterpret_cast<char*>(&clip_mode_), sizeof(clip_mode_));
1398 stream.read(reinterpret_cast<char*>(&prevent_math_mode_), sizeof(prevent_math_mode_));
1399 stream.read(reinterpret_cast<char*>(math_enabled_array_), sizeof(math_enabled_array_));
1400 stream.read(reinterpret_cast<char*>(&add_subscreen_), sizeof(add_subscreen_));
1401 stream.read(reinterpret_cast<char*>(&subtract_color_), sizeof(subtract_color_));
1402 stream.read(reinterpret_cast<char*>(&half_color_), sizeof(half_color_));
1403 stream.read(reinterpret_cast<char*>(&fixed_color_r_), sizeof(fixed_color_r_));
1404 stream.read(reinterpret_cast<char*>(&fixed_color_g_), sizeof(fixed_color_g_));
1405 stream.read(reinterpret_cast<char*>(&fixed_color_b_), sizeof(fixed_color_b_));
1406
1407 for (auto& layer : layer_) {
1408 uint8_t flags[4];
1409 stream.read(reinterpret_cast<char*>(flags), sizeof(flags));
1410 layer.mainScreenEnabled = flags[0];
1411 layer.subScreenEnabled = flags[1];
1412 layer.mainScreenWindowed = flags[2];
1413 layer.subScreenWindowed = flags[3];
1414 }
1415
1416 stream.read(reinterpret_cast<char*>(m7matrix), sizeof(m7matrix));
1417 stream.read(reinterpret_cast<char*>(&m7prev), sizeof(m7prev));
1418 stream.read(reinterpret_cast<char*>(&m7largeField), sizeof(m7largeField));
1419 stream.read(reinterpret_cast<char*>(&m7charFill), sizeof(m7charFill));
1420 stream.read(reinterpret_cast<char*>(&m7xFlip), sizeof(m7xFlip));
1421 stream.read(reinterpret_cast<char*>(&m7yFlip), sizeof(m7yFlip));
1422 stream.read(reinterpret_cast<char*>(&m7extBg), sizeof(m7extBg));
1423 stream.read(reinterpret_cast<char*>(&m7startX), sizeof(m7startX));
1424 stream.read(reinterpret_cast<char*>(&m7startY), sizeof(m7startY));
1425
1426 for (auto& win : windowLayer) {
1427 uint8_t encoded[5];
1428 stream.read(reinterpret_cast<char*>(encoded), sizeof(encoded));
1429 win.window1enabled = encoded[0];
1430 win.window2enabled = encoded[1];
1431 win.window1inversed = encoded[2];
1432 win.window2inversed = encoded[3];
1433 win.maskLogic = encoded[4];
1434 }
1435 stream.read(reinterpret_cast<char*>(&window1left), sizeof(window1left));
1436 stream.read(reinterpret_cast<char*>(&window1right), sizeof(window1right));
1437 stream.read(reinterpret_cast<char*>(&window2left), sizeof(window2left));
1438 stream.read(reinterpret_cast<char*>(&window2right), sizeof(window2right));
1439
1440 // BgLayer array (POD structs)
1441 for (auto& bg : bg_layer_) {
1442 stream.read(reinterpret_cast<char*>(&bg.hScroll), sizeof(bg.hScroll));
1443 stream.read(reinterpret_cast<char*>(&bg.vScroll), sizeof(bg.vScroll));
1444 uint8_t flags[4];
1445 stream.read(reinterpret_cast<char*>(flags), sizeof(flags));
1446 bg.tilemapWider = flags[0];
1447 bg.tilemapHigher = flags[1];
1448 bg.bigTiles = flags[2];
1449 bg.mosaicEnabled = flags[3];
1450 stream.read(reinterpret_cast<char*>(&bg.tilemapAdr), sizeof(bg.tilemapAdr));
1451 stream.read(reinterpret_cast<char*>(&bg.tileAdr), sizeof(bg.tileAdr));
1452 }
1453
1454 stream.read(reinterpret_cast<char*>(&scroll_prev_), sizeof(scroll_prev_));
1455 stream.read(reinterpret_cast<char*>(&scroll_prev2_), sizeof(scroll_prev2_));
1456 stream.read(reinterpret_cast<char*>(&mosaic_size_), sizeof(mosaic_size_));
1457 stream.read(reinterpret_cast<char*>(&mosaic_startline_), sizeof(mosaic_startline_));
1458
1459 stream.read(reinterpret_cast<char*>(&pixelOutputFormat), sizeof(pixelOutputFormat));
1460
1461 stream.read(reinterpret_cast<char*>(&h_count_), sizeof(h_count_));
1462 stream.read(reinterpret_cast<char*>(&v_count_), sizeof(v_count_));
1463 stream.read(reinterpret_cast<char*>(&h_count_second_), sizeof(h_count_second_));
1464 stream.read(reinterpret_cast<char*>(&v_count_second_), sizeof(v_count_second_));
1465 stream.read(reinterpret_cast<char*>(&counters_latched_), sizeof(counters_latched_));
1466 stream.read(reinterpret_cast<char*>(&ppu1_open_bus_), sizeof(ppu1_open_bus_));
1467 stream.read(reinterpret_cast<char*>(&ppu2_open_bus_), sizeof(ppu2_open_bus_));
1468
1469 stream.read(reinterpret_cast<char*>(&tile_data_size_), sizeof(tile_data_size_));
1470 stream.read(reinterpret_cast<char*>(&vram_base_address_), sizeof(vram_base_address_));
1471 stream.read(reinterpret_cast<char*>(&tilemap_base_address_), sizeof(tilemap_base_address_));
1472 stream.read(reinterpret_cast<char*>(&screen_brightness_), sizeof(screen_brightness_));
1473
1474 stream.read(reinterpret_cast<char*>(&bg_mode_), sizeof(bg_mode_));
1475
1476 // Registers
1477 stream.read(reinterpret_cast<char*>(&oam_size_), sizeof(oam_size_));
1478 stream.read(reinterpret_cast<char*>(&oam_address_), sizeof(oam_address_));
1479 stream.read(reinterpret_cast<char*>(&mosaic_), sizeof(mosaic_));
1480 stream.read(reinterpret_cast<char*>(&bgsc_), sizeof(bgsc_));
1481 stream.read(reinterpret_cast<char*>(&bgnba_), sizeof(bgnba_));
1482 stream.read(reinterpret_cast<char*>(&bghofs_), sizeof(bghofs_));
1483 stream.read(reinterpret_cast<char*>(&bgvofs_), sizeof(bgvofs_));
1484}
1485} // namespace emu
1486} // 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:349
uint8_t obj_size_
Definition ppu.h:382
void LatchHV()
Definition ppu.h:277
void HandleOPT(int layer, int *lx, int *ly)
Definition ppu.cc:406
void HandlePixel(int x, int y)
Definition ppu.cc:181
void EvaluateSprites(int line)
Definition ppu.cc:529
bool direct_color_
Definition ppu.h:305
void CatchUp(int h_pos)
Definition ppu.cc:160
bool m7largeField
Definition ppu.h:403
uint16_t obj_tile_adr1_
Definition ppu.h:380
int32_t m7startX
Definition ppu.h:410
uint8_t ppu1_open_bus_
Definition ppu.h:440
uint8_t pixelBuffer[512 *4 *239 *2]
Definition ppu.h:431
uint16_t screen_brightness_
Definition ppu.h:446
uint8_t scroll_prev2_
Definition ppu.h:426
uint8_t vram_remap_mode_
Definition ppu.h:352
int GetPixel(int x, int y, bool sub, int *r, int *g, int *b)
Definition ppu.cc:276
BgLayer bg_layer_[4]
Definition ppu.h:424
uint16_t tilemap_base_address_
Definition ppu.h:445
bool counters_latched_
Definition ppu.h:439
void DumpState() const
Definition ppu.cc:1136
uint8_t oam_buffer_
Definition ppu.h:373
uint8_t oam_adr_
Definition ppu.h:368
OAMAddress oam_address_
Definition ppu.h:458
bool m7charFill
Definition ppu.h:404
bool pseudo_hires_
Definition ppu.h:301
bool frame_interlace
Definition ppu.h:303
bool v_count_second_
Definition ppu.h:438
bool m7yFlip
Definition ppu.h:406
void StartLine(int line)
Definition ppu.cc:147
bool forced_blank_
Definition ppu.h:296
std::array< BGVOFS, 4 > bgvofs_
Definition ppu.h:463
uint8_t prevent_math_mode_
Definition ppu.h:388
void Reset()
Definition ppu.cc:40
bool GetWindowState(int layer, int x)
Definition ppu.cc:374
void CalculateMode7Starts(int y)
Definition ppu.cc:628
uint8_t m7prev
Definition ppu.h:402
bool subtract_color_
Definition ppu.h:391
uint16_t oam[0x100]
Definition ppu.h:366
uint8_t oam_adr_written_
Definition ppu.h:369
bool vram_increment_on_high_
Definition ppu.h:350
void SaveState(std::ostream &stream)
Definition ppu.cc:1206
uint8_t scroll_prev_
Definition ppu.h:425
uint8_t clip_mode_
Definition ppu.h:387
uint8_t high_oam_[0x20]
Definition ppu.h:367
uint16_t vram_base_address_
Definition ppu.h:444
Mosaic mosaic_
Definition ppu.h:459
void HandleFrameStart()
Definition ppu.cc:139
void Write(uint8_t adr, uint8_t val)
Definition ppu.cc:805
std::array< BGHOFS, 4 > bghofs_
Definition ppu.h:462
std::array< BGNBA, 4 > bgnba_
Definition ppu.h:461
uint16_t h_count_
Definition ppu.h:435
int current_scanline_
Definition ppu.h:345
std::array< uint8_t, 256 > obj_pixel_buffer_
Definition ppu.h:383
uint16_t vram_increment_
Definition ppu.h:351
bool obj_priority_
Definition ppu.h:379
void PutPixels(uint8_t *pixel_data)
Definition ppu.cc:1117
std::array< uint8_t, 256 > obj_priority_buffer_
Definition ppu.h:384
bool time_over_
Definition ppu.h:376
uint8_t Read(uint8_t adr, bool latch)
Definition ppu.cc:668
uint8_t mosaic_startline_
Definition ppu.h:422
uint8_t window2left
Definition ppu.h:417
bool frame_overscan_
Definition ppu.h:292
bool even_frame
Definition ppu.h:300
bool interlace
Definition ppu.h:302
uint16_t obj_tile_adr2_
Definition ppu.h:381
bool oam_in_high_written_
Definition ppu.h:371
Memory & memory_
Definition ppu.h:448
Layer layer_[5]
Definition ppu.h:398
uint8_t fixed_color_g_
Definition ppu.h:394
bool oam_in_high_
Definition ppu.h:370
bool oam_second_write_
Definition ppu.h:372
bool obj_interlace_
Definition ppu.h:378
bool h_count_second_
Definition ppu.h:437
int cycle_count_
Definition ppu.h:344
uint8_t mode
Definition ppu.h:298
bool m7xFlip
Definition ppu.h:405
uint8_t window2right
Definition ppu.h:418
uint8_t pixelOutputFormat
Definition ppu.h:432
uint16_t GetOffsetValue(int col, int row)
Definition ppu.cc:442
uint16_t tile_data_size_
Definition ppu.h:443
uint8_t window1left
Definition ppu.h:415
uint16_t vram[0x8000]
Definition ppu.h:348
int16_t m7matrix[8]
Definition ppu.h:401
void HandleVblank()
Definition ppu.cc:649
bool half_color_
Definition ppu.h:392
int last_rendered_x_
Definition ppu.h:359
uint8_t ppu2_open_bus_
Definition ppu.h:441
bool enable_debug_dump_
Definition ppu.h:342
bool add_subscreen_
Definition ppu.h:390
BackgroundMode bg_mode_
Definition ppu.h:451
uint8_t mosaic_size_
Definition ppu.h:427
bool m7extBg
Definition ppu.h:407
uint8_t cgram_buffer_
Definition ppu.h:363
bool range_over_
Definition ppu.h:377
uint8_t fixed_color_b_
Definition ppu.h:395
uint8_t cgram_pointer_
Definition ppu.h:361
std::array< BGSC, 4 > bgsc_
Definition ppu.h:460
bool math_enabled_array_[6]
Definition ppu.h:389
int GetPixelForBgLayer(int x, int y, int layer, bool priority)
Definition ppu.cc:457
bool overscan_
Definition ppu.h:293
int32_t m7startY
Definition ppu.h:411
uint16_t GetVramRemap()
Definition ppu.cc:1102
int GetPixelForMode7(int x, int layer, bool priority)
Definition ppu.cc:353
uint16_t cgram[0x100]
Definition ppu.h:356
uint8_t fixed_color_r_
Definition ppu.h:393
uint8_t window1right
Definition ppu.h:416
uint16_t v_count_
Definition ppu.h:436
OAMSize oam_size_
Definition ppu.h:457
WindowLayer windowLayer[6]
Definition ppu.h:414
uint16_t vram_read_buffer_
Definition ppu.h:353
bool cgram_second_write_
Definition ppu.h:362
void RunLine(int line)
Definition ppu.cc:175
void LoadState(std::istream &stream)
Definition ppu.cc:1344
bool bg3priority
Definition ppu.h:299
uint8_t brightness
Definition ppu.h:297
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