1 #ifdef PPU_CPP
2 
latch_counters()3 void PPU::latch_counters() {
4   regs.hcounter = cpu.hdot();
5   regs.vcounter = cpu.vcounter();
6   regs.counters_latched = true;
7 }
8 
get_vram_address()9 uint16 PPU::get_vram_address() {
10   uint16 addr = regs.vram_addr;
11   switch(regs.vram_mapping) {
12     case 0: break;  //direct mapping
13     case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break;
14     case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break;
15     case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break;
16   }
17   return (addr << 1);
18 }
19 
20 //NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will
21 //not be written anywhere at all. The below address ranges for where writes are invalid have
22 //been validated on hardware, as has the edge case where the S-CPU MDR can be written if the
23 //write occurs during the very last clock cycle of vblank.
24 
vram_mmio_read(uint16 addr)25 uint8 PPU::vram_mmio_read(uint16 addr) {
26   uint8 data;
27 
28   if(regs.display_disabled == true) {
29     data = memory::vram[addr];
30   } else {
31     uint16 v = cpu.vcounter();
32     uint16 h = cpu.hcounter();
33     uint16 ls = ((system.region() == System::NTSC ? 525 : 625) >> 1) - 1;
34     if(interlace() && !cpu.field()) ls++;
35 
36     if(v == ls && h == 1362) {
37       data = 0x00;
38     } else if(v < (!overscan() ? 224 : 239)) {
39       data = 0x00;
40     } else if(v == (!overscan() ? 224 : 239)) {
41       if(h == 1362) {
42         data = memory::vram[addr];
43       } else {
44         data = 0x00;
45       }
46     } else {
47       data = memory::vram[addr];
48     }
49   }
50 
51   return data;
52 }
53 
vram_mmio_write(uint16 addr,uint8 data)54 void PPU::vram_mmio_write(uint16 addr, uint8 data) {
55   if(regs.display_disabled == true) {
56     memory::vram[addr] = data;
57   } else {
58     uint16 v = cpu.vcounter();
59     uint16 h = cpu.hcounter();
60     if(v == 0) {
61       if(h <= 4) {
62         memory::vram[addr] = data;
63       } else if(h == 6) {
64         memory::vram[addr] = cpu.regs.mdr;
65       } else {
66         //no write
67       }
68     } else if(v < (!overscan() ? 225 : 240)) {
69       //no write
70     } else if(v == (!overscan() ? 225 : 240)) {
71       if(h <= 4) {
72         //no write
73       } else {
74         memory::vram[addr] = data;
75       }
76     } else {
77       memory::vram[addr] = data;
78     }
79   }
80 }
81 
82 //NOTE: OAM accesses during active display are rerouted to 0x0218 ... this can be considered
83 //a hack. The actual address varies during rendering, as the S-PPU reads in data itself for
84 //processing. Unfortunately, we have yet to determine how this works. The algorithm cannot be
85 //reverse engineered using a scanline renderer such as this, and at this time, there does not
86 //exist a more accurate SNES PPU emulator to work from. The only known game to actually access
87 //OAM during active display is Uniracers. It expects accesses to map to offset 0x0218.
88 //It was decided by public consensus to map writes to this address to match Uniracers, primarily
89 //because it is the only game observed to do this, but also because mapping to this address does
90 //not contradict any of our findings, because we have no findings whatsoever on this behavior.
91 //Think of this what you will, I openly admit that this is a hack. But it is more accurate than
92 //writing to the 'expected' address set by $2102,$2103, and will catch problems in software that
93 //accidentally accesses OAM during active display by virtue of not returning the expected data.
94 
oam_mmio_read(uint16 addr)95 uint8 PPU::oam_mmio_read(uint16 addr) {
96   addr &= 0x03ff;
97   if(addr & 0x0200) addr &= 0x021f;
98   uint8 data;
99 
100   if(regs.display_disabled == true) {
101     data = memory::oam[addr];
102   } else {
103     if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
104       data = memory::oam[0x0218];
105     } else {
106       data = memory::oam[addr];
107     }
108   }
109 
110   return data;
111 }
112 
oam_mmio_write(uint16 addr,uint8 data)113 void PPU::oam_mmio_write(uint16 addr, uint8 data) {
114   addr &= 0x03ff;
115   if(addr & 0x0200) addr &= 0x021f;
116 
117   sprite_list_valid = false;
118 
119   if(regs.display_disabled == true) {
120     memory::oam[addr] = data;
121   } else {
122     if(cpu.vcounter() < (!overscan() ? 225 : 240)) {
123       memory::oam[0x0218] = data;
124     } else {
125       memory::oam[addr] = data;
126     }
127   }
128 }
129 
130 //NOTE: CGRAM writes during hblank are valid. During active display, the actual address the
131 //data is written to varies, as the S-PPU itself changes the address. Like OAM, we do not know
132 //the exact algorithm used, but we have zero known examples of any commercial software that
133 //attempts to do this. Therefore, the addresses are mapped to 0x01ff. There is nothing special
134 //about this address, it is simply more accurate to invalidate the 'expected' address than not.
135 
cgram_mmio_read(uint16 addr)136 uint8 PPU::cgram_mmio_read(uint16 addr) {
137   addr &= 0x01ff;
138   uint8 data;
139 
140   if(regs.display_disabled == true) {
141     data = memory::cgram[addr];
142   } else {
143     uint16 v = cpu.vcounter();
144     uint16 h = cpu.hcounter();
145     if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
146       data = memory::cgram[0x01ff] & 0x7f;
147     } else {
148       data = memory::cgram[addr];
149     }
150   }
151 
152   if(addr & 1) data &= 0x7f;
153   return data;
154 }
155 
cgram_mmio_write(uint16 addr,uint8 data)156 void PPU::cgram_mmio_write(uint16 addr, uint8 data) {
157   addr &= 0x01ff;
158   if(addr & 1) data &= 0x7f;
159 
160   if(regs.display_disabled == true) {
161     memory::cgram[addr] = data;
162   } else {
163     uint16 v = cpu.vcounter();
164     uint16 h = cpu.hcounter();
165     if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) {
166       memory::cgram[0x01ff] = data & 0x7f;
167     } else {
168       memory::cgram[addr] = data;
169     }
170   }
171 }
172 
173 #endif
174