OSDN Git Service

[VM][State] Apply new state framework to some VMs.
[csp-qt/common_source_project-fm7.git] / source / src / vm / familybasic / ppu.cpp
1 /*
2         Nintendo Family BASIC Emulator 'eFamilyBASIC'
3
4         Origin : nester
5         Author : Takeda.Toshiya
6         Date   : 2010.08.11-
7
8         [ PPU ]
9 */
10
11 #include "ppu.h"
12 #include "memory.h"
13
14 static const uint8_t palette[64][3] = {
15         {0x75, 0x75, 0x75}, {0x27, 0x1b, 0x8f}, {0x00, 0x00, 0xab}, {0x47, 0x00, 0x9f},
16         {0x8f, 0x00, 0x77}, {0xab, 0x00, 0x13}, {0xa7, 0x00, 0x00}, {0x7f, 0x0b, 0x00},
17         {0x43, 0x2f, 0x00}, {0x00, 0x47, 0x00}, {0x00, 0x51, 0x00}, {0x00, 0x3f, 0x17},
18         {0x1b, 0x3f, 0x5f}, {0x00, 0x00, 0x00}, {0x05, 0x05, 0x05}, {0x05, 0x05, 0x05},
19         {0xbc, 0xbc, 0xbc}, {0x00, 0x73, 0xef}, {0x23, 0x3b, 0xef}, {0x83, 0x00, 0xf3},
20         {0xbf, 0x00, 0xbf}, {0xe7, 0x00, 0x5b}, {0xdb, 0x2b, 0x00}, {0xcb, 0x4f, 0x0f},
21         {0x8b, 0x73, 0x00}, {0x00, 0x97, 0x00}, {0x00, 0xab, 0x00}, {0x00, 0x93, 0x3b},
22         {0x00, 0x83, 0x8b}, {0x11, 0x11, 0x11}, {0x09, 0x09, 0x09}, {0x09, 0x09, 0x09},
23         {0xff, 0xff, 0xff}, {0x3f, 0xbf, 0xff}, {0x5f, 0x97, 0xff}, {0xa7, 0x8b, 0xfd},
24         {0xf7, 0x7b, 0xff}, {0xff, 0x77, 0xb7}, {0xff, 0x77, 0x63}, {0xff, 0x9b, 0x3b},
25         {0xf3, 0xbf, 0x3f}, {0x83, 0xd3, 0x13}, {0x4f, 0xdf, 0x4b}, {0x58, 0xf8, 0x98},
26         {0x00, 0xeb, 0xdb}, {0x66, 0x66, 0x66}, {0x0d, 0x0d, 0x0d}, {0x0d, 0x0d, 0x0d},
27         {0xff, 0xff, 0xff}, {0xab, 0xe7, 0xff}, {0xc7, 0xd7, 0xff}, {0xd7, 0xcb, 0xff},
28         {0xff, 0xc7, 0xff}, {0xff, 0xc7, 0xdb}, {0xff, 0xbf, 0xb3}, {0xff, 0xdb, 0xab},
29         {0xff, 0xe7, 0xa3}, {0xe3, 0xff, 0xa3}, {0xab, 0xf3, 0xbf}, {0xb3, 0xff, 0xcf},
30         {0x9f, 0xff, 0xf3}, {0xdd, 0xdd, 0xdd}, {0x11, 0x11, 0x11}, {0x11, 0x11, 0x11}
31 };
32
33 #define VRAM(addr)      bank_ptr[((addr) >> 10) & 0x0f][(addr) & 0x3ff]
34
35 #define NMI_enabled()   (regs[0] & 0x80)
36 #define sprites_8x16()  (regs[0] & 0x20)
37 //#define spr_enabled() (regs[1] & 0x10)
38 //#define bg_enabled()  (regs[1] & 0x08)
39 #define spr_clip()      (!(regs[1] & 0x04))
40 #define bg_clip()       (!(regs[1] & 0x02))
41 #define monochrome()    (regs[1] & 0x01)
42 #define rgb_pal()       (regs[1] & 0xe0)
43 #define sprite0_hit()   (regs[2] & 0x40)
44
45 #define LOOPY_SCANLINE_START(v, t) { \
46         v = (v & 0xfbe0) | (t & 0x041f); \
47 }
48
49 #define LOOPY_NEXT_LINE(v) { \
50         if((v & 0x7000) == 0x7000) { \
51                 v &= 0x8fff; \
52                 if((v & 0x3e0) == 0x3a0) { \
53                         v ^= 0x0800; \
54                         v &= 0xfc1f; \
55                 } else { \
56                         if((v & 0x3e0) == 0x3e0) { \
57                                 v &= 0xfc1f; \
58                         } else { \
59                                 v += 0x20; \
60                         } \
61                 } \
62         } else { \
63                 v += 0x1000; \
64         } \
65 }
66
67 #define LOOPY_NEXT_TILE(v) { \
68         if((v & 0x1f) == 0x1f) { \
69                 v ^= 0x0400; \
70                 v &= 0xffe0; \
71         } else { \
72                 v++; \
73         } \
74 }
75
76 #define LOOPY_NEXT_PIXEL(v, x) { \
77         if(x == 7) { \
78                 LOOPY_NEXT_TILE(v); \
79                 x = 0; \
80         } else { \
81                 x++; \
82         } \
83 }
84
85 void PPU::initialize()
86 {
87         chr_rom = NULL;
88         
89         // register event
90         register_vline_event(this);
91 }
92
93 void PPU::release()
94 {
95         if(chr_rom != NULL) {
96                 free(chr_rom);
97         }
98 }
99
100 void PPU::load_rom_image(const _TCHAR *file_name)
101 {
102         FILEIO* fio = new FILEIO();
103         
104         if(chr_rom != NULL) {
105                 free(chr_rom);
106         }
107         chr_rom_size = 0;
108         
109         memset(&header, 0x00, sizeof(header));
110         
111         if(!fio->Fopen(create_local_path(file_name), FILEIO_READ_BINARY)) {
112                 // for compatibility
113                 fio->Fopen(create_local_path(_T("BASIC.NES")), FILEIO_READ_BINARY);
114         }
115         if(fio->IsOpened()) {
116                 // read header
117                 fio->Fread(&header, sizeof(header), 1);
118                 // skip program rom
119                 fio->Fseek(0x2000 * header.num_8k_rom_banks(), FILEIO_SEEK_CUR);
120                 // read chr rom
121                 if((chr_rom_size = 8192 * header.num_8k_vrom_banks) != 0) {
122                         for(uint32_t bit = 0x40000; bit != 0; bit >>= 1) {
123                                 if(chr_rom_size & bit) {
124                                         if(chr_rom_size & (bit - 1)) {
125                                                 chr_rom_size = (chr_rom_size | (bit - 1)) + 1;
126                                         }
127                                         break;
128                                 }
129                         }
130                         chr_rom = (uint8_t *)calloc(chr_rom_size, 1);
131                         fio->Fread(chr_rom, 8192 * header.num_8k_vrom_banks, 1);
132                 }
133                 fio->Fclose();
134         }
135         delete fio;
136         
137         if(chr_rom_size == 0) {
138                 chr_rom_size = 8192;
139                 chr_rom = (uint8_t *)calloc(chr_rom_size, 1);
140         }
141         chr_rom_mask = (chr_rom_size / 0x400) - 1;
142 }
143
144 void PPU::reset()
145 {
146         // set up PPU memory space table
147         for(int i = 0; i < 8; i++) {
148                 set_ppu_bank(i, i);
149         }
150         
151         // set mirroring
152         if(header.flags_1 & 8) {
153                 set_mirroring(MIRROR_4SCREEN);
154         } else if(header.flags_1 & 1) {
155                 set_mirroring(MIRROR_VERT);
156         } else {
157                 set_mirroring(MIRROR_HORIZ);
158         }
159         
160         memset(bg_pal, 0, sizeof(bg_pal));
161         memset(spr_pal, 0, sizeof(spr_pal));
162         memset(solid_buf, 0, sizeof(solid_buf));
163         memset(name_tables, 0, sizeof(name_tables));
164         
165         memset(spr_ram, 0, sizeof(spr_ram));
166         spr_ram_rw_ptr = 0;
167         
168         memset(regs, 0, sizeof(regs));
169         bg_pattern_table_addr = 0;
170         spr_pattern_table_addr = 0;
171         ppu_addr_inc = 0;
172         rgb_bak = 0;
173         toggle_2005_2006 = false;
174         read_2007_buffer = 0;
175         
176         loopy_v = 0;
177         loopy_t = 0;
178         loopy_x = 0;
179         
180         // reset emphasised palette
181         update_palette();
182 }
183
184 void PPU::write_data8(uint32_t addr, uint32_t data)
185 {
186         uint16_t ofs;
187         
188         regs[addr & 7] = data;
189         
190         switch(addr & 0xe007) {
191         case 0x2000:
192                 bg_pattern_table_addr = (data & 0x10) ? 0x1000 : 0;
193                 spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0;
194                 ppu_addr_inc = (data & 0x04) ? 32 : 1;
195                 loopy_t = (loopy_t & 0xf3ff) | (((uint16_t)(data & 0x03)) << 10);
196                 break;
197         case 0x2001:
198                 if(rgb_bak != (data & 0xe0)) {
199                         update_palette();
200                         rgb_bak = data & 0xe0;
201                 }
202                 break;
203         case 0x2003:
204                 spr_ram_rw_ptr = data;
205                 break;
206         case 0x2004:
207                 spr_ram[spr_ram_rw_ptr++] = data;
208                 break;
209         case 0x2005:
210                 toggle_2005_2006 = !toggle_2005_2006;
211                 if(toggle_2005_2006) {
212                         // first write
213                         loopy_t = (loopy_t & 0xffe0) | (((uint16_t)(data & 0xf8)) >> 3);
214                         loopy_x = data & 0x07;
215                 } else {
216                         // second write
217                         loopy_t = (loopy_t & 0xfc1f) | (((uint16_t)(data & 0xf8)) << 2);
218                         loopy_t = (loopy_t & 0x8fff) | (((uint16_t)(data & 0x07)) << 12);
219                 }
220                 break;
221         case 0x2006:
222                 toggle_2005_2006 = !toggle_2005_2006;
223                 if(toggle_2005_2006) {
224                         // first write
225                         loopy_t = (loopy_t & 0x00ff) | (((uint16_t)(data & 0x3f)) << 8);
226                 } else {
227                         // second write
228                         loopy_t = (loopy_t & 0xff00) | ((uint16_t)data);
229                         loopy_v = loopy_t;
230                 }
231                 break;
232         case 0x2007:
233                 ofs = loopy_v & 0x3fff;
234                 loopy_v += ppu_addr_inc;
235                 if(ofs >= 0x3000) {
236                         // is it a palette entry?
237                         if(ofs >= 0x3f00) {
238                                 data &= 0x3f;
239                                 if(!(ofs & 0x000f)) {
240                                         bg_pal[0] = spr_pal[0] = data;
241                                 } else if(!(ofs & 0x10)) {
242                                         bg_pal[ofs & 0x000f] = data;
243                                 } else {
244                                         spr_pal[ofs & 0x000f] = data;
245                                 }
246                                 break;
247                         }
248                         // handle mirroring
249                         ofs &= 0xefff;
250                 }
251                 if(ofs >= 0x2000 || header.num_8k_vrom_banks == 0) {
252                         VRAM(ofs) = data;
253                 }
254                 break;
255         }
256 }
257
258 uint32_t PPU::read_data8(uint32_t addr)
259 {
260         uint16_t ofs;
261         uint8_t val;
262         
263         switch(addr & 0xe007) {
264         case 0x2002:
265                 // clear toggle
266                 toggle_2005_2006 = false;
267                 val = regs[2];
268                 // clear v-blank flag
269                 regs[2] &= ~0x80;
270                 return val;
271         case 0x2007:
272                 ofs = loopy_v & 0x3fff;
273                 loopy_v += ppu_addr_inc;
274                 if(ofs >= 0x3000) {
275                         // is it a palette entry?
276                         if(ofs >= 0x3f00) {
277                                 if(!(ofs & 0x0010)) {
278                                         return bg_pal[ofs & 0x000f];
279                                 } else {
280                                         return spr_pal[ofs & 0x000f];
281                                 }
282                         }
283                         // handle mirroring
284                         ofs &= 0xefff;
285                 }
286                 val = read_2007_buffer;
287                 read_2007_buffer = VRAM(ofs);
288                 return val;
289         }
290         return regs[addr & 7];
291 }
292
293 void PPU::event_vline(int v, int clock)
294 {
295         // 525 -> 262.5
296         if(v & 1) {
297                 return;
298         }
299         v >>= 1;
300         
301         switch(v) {
302         case 0:
303                 if(spr_enabled() || bg_enabled()) {
304                         loopy_v = loopy_t;
305                 }
306                 break;
307         case 241:
308                 // set vblank register flag
309                 regs[2] |= 0x80;
310                 if(NMI_enabled()) {
311                         d_cpu->write_signal(SIG_CPU_NMI, 1, 1);
312                 }
313                 break;
314         case 261:
315                 // reset vblank register flag and sprite0 hit flag1
316                 regs[2] &= 0x3F;
317                 break;
318         }
319         if(v < 240) {
320                 render_scanline(v);
321         }
322 }
323
324 void PPU::draw_screen()
325 {
326         
327         for(int y = 0; y < 240; y++) {
328                 scrntype_t* dest = emu->get_screen_buffer(y);
329                 uint8_t* src = screen[y];
330                 
331                 for(int x = 0; x < 256; x++) {
332                         dest[x] = palette_pc[src[x + 8] & 0x3f];
333                 }
334         }
335 }
336
337 void PPU::update_palette()
338 {
339         for(int i = 0; i < 64; i++) {
340                 uint8_t r = palette[i][0];
341                 uint8_t g = palette[i][1];
342                 uint8_t b = palette[i][2];
343                 
344                 switch(rgb_pal()) {
345                 case 0x20:
346                         g = (uint8_t)(g * 0.80);
347                         b = (uint8_t)(b * 0.73);
348                         break;
349                 case 0x40:
350                         r = (uint8_t)(r * 0.73);
351                         b = (uint8_t)(b * 0.70);
352                         break;
353                 case 0x60:
354                         r = (uint8_t)(r * 0.76);
355                         g = (uint8_t)(g * 0.78);
356                         b = (uint8_t)(b * 0.58);
357                         break;
358                 case 0x80:
359                         r = (uint8_t)(r * 0.86);
360                         g = (uint8_t)(g * 0.80);
361                         break;
362                 case 0xa0:
363                         r = (uint8_t)(r * 0.83);
364                         g = (uint8_t)(g * 0.68);
365                         b = (uint8_t)(b * 0.85);
366                         break;
367                 case 0xc0:
368                         r = (uint8_t)(r * 0.67);
369                         g = (uint8_t)(g * 0.77);
370                         b = (uint8_t)(b * 0.83);
371                         break;
372                 case 0xe0:
373                         r = (uint8_t)(r * 0.68);
374                         g = (uint8_t)(g * 0.68);
375                         b = (uint8_t)(b * 0.68);
376                         break;
377                 }
378                 palette_pc[i] = RGB_COLOR(r, g, b);
379         }
380 }
381
382 void PPU::render_scanline(int v)
383 {
384         uint8_t* buf = screen[v];
385         
386         if(!bg_enabled()) {
387                 // set to background color
388                 memset(screen[v], bg_pal[0], 256 + 16);
389         }
390         if(spr_enabled() || bg_enabled()) {
391                 LOOPY_SCANLINE_START(loopy_v, loopy_t);
392                 if(bg_enabled()) {
393                         render_bg(v);
394                 } else {
395                         memset(solid_buf, 0, sizeof(solid_buf));
396                 }
397                 if(spr_enabled()) {
398                         // draw sprites
399                         render_spr(v);
400                 }
401                 LOOPY_NEXT_LINE(loopy_v);
402         }
403 }
404
405 #define BG_WRITTEN_FLAG 1
406 #define SPR_WRITTEN_FLAG 2
407
408 #define DRAW_BG_PIXEL() \
409         col = attrib_bits; \
410         if(pattern_lo & pattern_mask) { \
411                 col |= 1; \
412         } \
413         if(pattern_hi & pattern_mask) { \
414                 col |= 2; \
415         } \
416         *p++ = monochrome() ? (bg_pal[col] & 0xf0) : bg_pal[col]; \
417         *solid++ = (col & 3) ? BG_WRITTEN_FLAG : 0; \
418
419 void PPU::render_bg(int v)
420 {
421         uint32_t tile_x = (loopy_v & 0x001f);
422         uint32_t tile_y = (loopy_v & 0x03e0) >> 5;
423         uint32_t name_addr = 0x2000 + (loopy_v & 0x0fff);
424         uint32_t attrib_addr = 0x2000 + (loopy_v & 0x0c00) + 0x03c0 + ((tile_y & 0xfffc) << 1) + (tile_x >> 2);
425         uint8_t attrib_bits;
426         
427         if(!(tile_y & 2)) {
428                 if(!(tile_x & 2)) {
429                         attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
430                 } else {
431                         attrib_bits = (VRAM(attrib_addr) & 0x0C);
432                 }
433         } else {
434                 if(!(tile_x & 2)) {
435                         attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
436                 } else {
437                         attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
438                 }
439         }
440         uint8_t *p = screen[v] + (8 - loopy_x);
441         uint8_t *solid = solid_buf + (8 - loopy_x);
442         
443         for(int i = 33; i; i--) {
444                 if(header.mapper() == 5) {
445                         uint8_t mmc5_pal = d_memory->mmc5_ppu_latch_render(1, name_addr & 0x03ff);
446                         if(mmc5_pal != 0) {
447                                 attrib_bits = mmc5_pal & 0x0c;
448                         }
449                 }
450                 try {
451                         uint32_t pattern_addr = bg_pattern_table_addr + ((int32_t)VRAM(name_addr) << 4) + ((loopy_v & 0x7000) >> 12);
452                         uint8_t pattern_lo = VRAM(pattern_addr);
453                         uint8_t pattern_hi = VRAM(pattern_addr + 8);
454                         uint8_t pattern_mask = 0x80;
455                         uint8_t col;
456                         
457                         DRAW_BG_PIXEL();
458                         pattern_mask >>= 1;
459                         DRAW_BG_PIXEL();
460                         pattern_mask >>= 1;
461                         DRAW_BG_PIXEL();
462                         pattern_mask >>= 1;
463                         DRAW_BG_PIXEL();
464                         pattern_mask >>= 1;
465                         DRAW_BG_PIXEL();
466                         pattern_mask >>= 1;
467                         DRAW_BG_PIXEL();
468                         pattern_mask >>= 1;
469                         DRAW_BG_PIXEL();
470                         pattern_mask >>= 1;
471                         DRAW_BG_PIXEL();
472                         
473                         tile_x++;
474                         name_addr++;
475                         
476                         if(!(tile_x & 1)) {
477                                 if(!(tile_x & 3)) {
478                                         if(!(tile_x & 0x1f)) {
479                                                 name_addr ^= 0x0400; // switch name tables
480                                                 attrib_addr ^= 0x0400;
481                                                 name_addr -= 0x0020;
482                                                 attrib_addr -= 0x0008;
483                                                 tile_x -= 0x0020;
484                                         }
485                                         attrib_addr++;
486                                 }
487                                 if(!(tile_y & 2)) {
488                                         if(!(tile_x & 2)) {
489                                                 attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
490                                         } else {
491                                                 attrib_bits = (VRAM(attrib_addr) & 0x0c);
492                                         }
493                                 } else {
494                                         if(!(tile_x & 2)) {
495                                                 attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
496                                         } else {
497                                                 attrib_bits = (VRAM(attrib_addr) & 0xc0) >> 4;
498                                         }
499                                 }
500                         }
501                 } catch(...) {
502                         // do nothing
503                 }
504         }
505         if(bg_clip()) {
506                 memset(&screen[v][8], bg_pal[0], 8);
507                 memset(solid + 8, 0, 8);
508         }
509 }
510
511 void PPU::render_spr(int v)
512 {
513         int num_sprites = 0;
514         int spr_height = sprites_8x16() ? 16 : 8;
515         
516         if(header.mapper() == 5) {
517                 d_memory->mmc5_ppu_latch_render(0, 0);
518         }
519         for(int s = 0; s < 64; s++) {
520                 uint8_t* spr = &spr_ram[s << 2];
521                 int spr_y = spr[0] + 1;
522                 
523                 if(spr_y > v || (spr_y + spr_height) <= v) {
524                         continue;
525                 }
526                 num_sprites++;
527                 if(num_sprites > 8) {
528                         break;
529                 }
530                 int spr_x = spr[3];
531                 int start_x = 0;
532                 int end_x = 8;
533                 int inc_x = 1;
534                 
535                 if((spr_x + 7) > 255) {
536                         end_x -= ((spr_x + 7) - 255);
537                 }
538                 if((spr_x < 8) && (spr_clip())) {
539                         if(!spr_x) {
540                                 continue;
541                         }
542                         start_x += (8 - spr_x);
543                 }
544                 int y = v - spr_y;
545                 
546                 uint8_t *p = &screen[v][8 + spr_x + start_x];
547                 uint8_t *solid = &solid_buf[8 + spr_x + start_x];
548                 
549                 if(spr[2] & 0x40) {
550                         start_x = (8 - 1) - start_x;
551                         end_x = (8 - 1) - end_x;
552                         inc_x = -1;
553                 }
554                 if(spr[2] & 0x80) {
555                         y = (spr_height - 1) - y;
556                 }
557                 uint8_t priority = spr[2] & 0x20;
558                 
559                 for(int x = start_x; x != end_x; x += inc_x) {
560                         uint8_t col = 0;
561                         uint32_t tile_addr;
562                         uint8_t tile_mask;
563                         
564                         if(!((*solid) & SPR_WRITTEN_FLAG)) {
565                                 if(sprites_8x16()) {
566                                         tile_addr = spr[1] << 4;
567                                         if(spr[1] & 0x01) {
568                                                 tile_addr += 0x1000;
569                                                 if(y < 8) {
570                                                         tile_addr -= 16;
571                                                 }
572                                         } else {
573                                                 if(y >= 8) {
574                                                         tile_addr += 16;
575                                                 }
576                                         }
577                                         tile_addr += y & 0x07;
578                                         tile_mask = (0x80 >> (x & 0x07));
579                                 } else {
580                                         tile_addr = spr[1] << 4;
581                                         tile_addr += y & 0x07;
582                                         tile_addr += spr_pattern_table_addr;
583                                         tile_mask = (0x80 >> (x & 0x07));
584                                 }
585                                 if(VRAM(tile_addr) & tile_mask) {
586                                         col |= 1;
587                                 }
588                                 tile_addr += 8;
589                                 if(VRAM(tile_addr) & tile_mask) {
590                                         col |= 2;
591                                 }
592                                 if(spr[2] & 2) {
593                                         col |= 8;
594                                 }
595                                 if(spr[2] & 1) {
596                                         col |= 4;
597                                 }
598                                 if(col & 3) {
599                                         if(s && (*solid & BG_WRITTEN_FLAG)) {
600                                                 regs[2] |= 0x40;
601                                         }
602                                         if(priority) {
603                                                 *solid |= SPR_WRITTEN_FLAG;
604                                                 if(!(*solid & BG_WRITTEN_FLAG)) {
605                                                         *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
606                                                 }
607                                         } else {
608                                                 if(!(*solid & SPR_WRITTEN_FLAG)) {
609                                                         *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
610                                                         *solid |= SPR_WRITTEN_FLAG;
611                                                 }
612                                         }
613                                 }
614                         }
615                         p++;
616                         solid++;
617                 }
618         }
619         if(num_sprites >= 8) {
620                 regs[2] |= 0x20;
621         } else {
622                 regs[2] &= ~0x20;
623         }
624 }
625
626 void PPU::set_ppu_bank(uint8_t bank, uint32_t bank_num)
627 {
628         if(bank < 8) {
629                 bank_ptr[bank] = chr_rom + 0x400 * (bank_num & chr_rom_mask);
630         } else if(bank < 12) {
631                 bank_ptr[bank] = bank_ptr[bank + 4] = name_tables + 0x400 * (bank_num & 0x03);
632         }
633         banks[bank] = bank_num;
634 }
635
636 void PPU::set_mirroring(int mirror)
637 {
638         switch(mirror) {
639         case MIRROR_HORIZ:
640                 // horizontal mirroring
641                 set_mirroring(0, 0, 1, 1);
642                 break;
643         case MIRROR_VERT:
644                 // vertical mirroring
645                 set_mirroring(0, 1, 0, 1);
646                 break;
647         case MIRROR_4SCREEN:
648                 // 4 screen mirroring
649                 set_mirroring(0, 1, 2, 3);
650                 break;
651         }
652 }
653
654 void PPU::set_mirroring(uint32_t nt0, uint32_t nt1, uint32_t nt2, uint32_t nt3)
655 {
656         set_ppu_bank( 8, nt0);
657         set_ppu_bank( 9, nt1);
658         set_ppu_bank(10, nt2);
659         set_ppu_bank(11, nt3);
660 }
661
662 #define STATE_VERSION   3
663
664 bool PPU::process_state(FILEIO* state_fio, bool loading)
665 {
666         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
667                 return false;
668         }
669         if(!state_fio->StateCheckInt32(this_device_id)) {
670                 return false;
671         }
672         //state_fio->StateBuffer(palette_pc, sizeof(palette_pc), 1);
673         if(loading) {
674                 for(int i = 0; i < (sizeof(palette_pc) / sizeof(scrntype_t)); i++) {
675                         uint8_t r, g, b;
676                         r = state_fio->FgetUint8();
677                         g = state_fio->FgetUint8();
678                         b = state_fio->FgetUint8();
679                         palette_pc[i] = RGB_COLOR(r, g, b);
680                 }
681         } else {
682                 for(int i = 0; i < (sizeof(palette_pc) / sizeof(scrntype_t)); i++) {
683                         uint8_t r, g, b;
684                         r = R_OF_COLOR(palette_pc[i]);
685                         g = G_OF_COLOR(palette_pc[i]);
686                         b = B_OF_COLOR(palette_pc[i]);
687                         state_fio->FputUint8(r);
688                         state_fio->FputUint8(g);
689                         state_fio->FputUint8(b);
690                 }
691         }
692         state_fio->StateBuffer(solid_buf, sizeof(solid_buf), 1);
693         state_fio->StateBuffer(&header, sizeof(header), 1); // OK?
694 //      state_fio->StateBuffer(banks, sizeof(banks), 1);
695         for(int i = 0; i < (sizeof(banks) / sizeof(uint32_t)); i++) {
696                 state_fio->StateUint32(banks[i]);
697         }
698         state_fio->StateUint32(chr_rom_size);
699 //      state_fio->StateInt32(chr_rom_mask);
700         if(loading) {
701                 chr_rom_mask = (chr_rom_size / 0x400) - 1;
702                 if(chr_rom != NULL) {
703                         free(chr_rom);
704                 }
705                 chr_rom = (uint8_t *)malloc(chr_rom_size);
706         }
707         state_fio->StateBuffer(chr_rom, chr_rom_size, 1);
708         state_fio->StateBuffer(name_tables, sizeof(name_tables), 1);
709         state_fio->StateBuffer(spr_ram, sizeof(spr_ram), 1);
710         state_fio->StateBuffer(bg_pal, sizeof(bg_pal), 1);
711         state_fio->StateBuffer(spr_pal, sizeof(spr_pal), 1);
712         state_fio->StateUint8(spr_ram_rw_ptr);
713         state_fio->StateBuffer(regs, sizeof(regs), 1);
714         state_fio->StateUint16(bg_pattern_table_addr);
715         state_fio->StateUint16(spr_pattern_table_addr);
716         state_fio->StateUint16(ppu_addr_inc);
717         state_fio->StateUint8(rgb_bak);
718         state_fio->StateBool(toggle_2005_2006);
719         state_fio->StateUint8(read_2007_buffer);
720         state_fio->StateUint16(loopy_v);
721         state_fio->StateUint16(loopy_t);
722         state_fio->StateUint8(loopy_x);
723         
724         // post process
725         if(loading) {
726                 for(int i = 0; i < 12; i++) {
727                         set_ppu_bank(i, banks[i]);
728                 }
729         }
730         return true;
731 }