2 Nintendo Family BASIC Emulator 'eFamilyBASIC'
5 Author : Takeda.Toshiya
13 static const uint8 palette[64][3] = {
14 {0x75, 0x75, 0x75}, {0x27, 0x1b, 0x8f}, {0x00, 0x00, 0xab}, {0x47, 0x00, 0x9f},
15 {0x8f, 0x00, 0x77}, {0xab, 0x00, 0x13}, {0xa7, 0x00, 0x00}, {0x7f, 0x0b, 0x00},
16 {0x43, 0x2f, 0x00}, {0x00, 0x47, 0x00}, {0x00, 0x51, 0x00}, {0x00, 0x3f, 0x17},
17 {0x1b, 0x3f, 0x5f}, {0x00, 0x00, 0x00}, {0x05, 0x05, 0x05}, {0x05, 0x05, 0x05},
18 {0xbc, 0xbc, 0xbc}, {0x00, 0x73, 0xef}, {0x23, 0x3b, 0xef}, {0x83, 0x00, 0xf3},
19 {0xbf, 0x00, 0xbf}, {0xe7, 0x00, 0x5b}, {0xdb, 0x2b, 0x00}, {0xcb, 0x4f, 0x0f},
20 {0x8b, 0x73, 0x00}, {0x00, 0x97, 0x00}, {0x00, 0xab, 0x00}, {0x00, 0x93, 0x3b},
21 {0x00, 0x83, 0x8b}, {0x11, 0x11, 0x11}, {0x09, 0x09, 0x09}, {0x09, 0x09, 0x09},
22 {0xff, 0xff, 0xff}, {0x3f, 0xbf, 0xff}, {0x5f, 0x97, 0xff}, {0xa7, 0x8b, 0xfd},
23 {0xf7, 0x7b, 0xff}, {0xff, 0x77, 0xb7}, {0xff, 0x77, 0x63}, {0xff, 0x9b, 0x3b},
24 {0xf3, 0xbf, 0x3f}, {0x83, 0xd3, 0x13}, {0x4f, 0xdf, 0x4b}, {0x58, 0xf8, 0x98},
25 {0x00, 0xeb, 0xdb}, {0x66, 0x66, 0x66}, {0x0d, 0x0d, 0x0d}, {0x0d, 0x0d, 0x0d},
26 {0xff, 0xff, 0xff}, {0xab, 0xe7, 0xff}, {0xc7, 0xd7, 0xff}, {0xd7, 0xcb, 0xff},
27 {0xff, 0xc7, 0xff}, {0xff, 0xc7, 0xdb}, {0xff, 0xbf, 0xb3}, {0xff, 0xdb, 0xab},
28 {0xff, 0xe7, 0xa3}, {0xe3, 0xff, 0xa3}, {0xab, 0xf3, 0xbf}, {0xb3, 0xff, 0xcf},
29 {0x9f, 0xff, 0xf3}, {0xdd, 0xdd, 0xdd}, {0x11, 0x11, 0x11}, {0x11, 0x11, 0x11}
32 #define VRAM(addr) banks[((addr) >> 10) & 0x0f][(addr) & 0x3ff]
34 #define NMI_enabled() (regs[0] & 0x80)
35 #define sprites_8x16() (regs[0] & 0x20)
36 #define spr_enabled() (regs[1] & 0x10)
37 #define bg_enabled() (regs[1] & 0x08)
38 #define spr_clip() (!(regs[1] & 0x04))
39 #define bg_clip() (!(regs[1] & 0x02))
40 #define monochrome() (regs[1] & 0x01)
41 #define rgb_pal() (regs[1] & 0xe0)
42 #define sprite0_hit() (regs[2] & 0x40)
44 #define LOOPY_SCANLINE_START(v, t) { \
45 v = (v & 0xfbe0) | (t & 0x041f); \
48 #define LOOPY_NEXT_LINE(v) { \
49 if((v & 0x7000) == 0x7000) { \
51 if((v & 0x3e0) == 0x3a0) { \
55 if((v & 0x3e0) == 0x3e0) { \
66 #define LOOPY_NEXT_TILE(v) { \
67 if((v & 0x1f) == 0x1f) { \
75 #define LOOPY_NEXT_PIXEL(v, x) { \
84 void PPU::initialize()
87 register_vline_event(this);
90 void PPU::load_rom_image(const _TCHAR *file_name)
92 FILEIO* fio = new FILEIO();
93 bool file_open = false;
95 if(fio->Fopen(create_local_path(file_name), FILEIO_READ_BINARY)) {
97 } else if(fio->Fopen(create_local_path(_T("BASIC.NES")), FILEIO_READ_BINARY)) {
103 fio->Fread(header, sizeof(header), 1);
105 fio->Fseek(header[4] * 0x4000, FILEIO_SEEK_CUR);
106 // read chr rom (max 8kb)
107 fio->Fread(chr_rom, sizeof(chr_rom), 1);
110 memset(header, 0, sizeof(header));
111 memset(chr_rom, 0xff, sizeof(chr_rom));
118 // set up PPU memory space table
119 for(int i = 0; i < 8; i++) {
120 banks[i] = chr_rom + 0x400 * i;
126 // 4 screen mirroring
127 banks[ 8] = banks[12] = name_tables;
128 banks[ 9] = banks[13] = name_tables + 0x400;
129 banks[10] = banks[14] = name_tables + 0x800;
130 banks[11] = banks[15] = name_tables + 0xc00;
131 } else if(header[6] & 1) {
133 // vertical mirroring
134 banks[ 8] = banks[10] = banks[12] = banks[14] = name_tables;
135 banks[ 9] = banks[11] = banks[13] = banks[15] = name_tables + 0x400;
138 // horizontal mirroring
139 banks[ 8] = banks[ 9] = banks[12] = banks[13] = name_tables;
140 banks[10] = banks[11] = banks[14] = banks[15] = name_tables + 0x400;
144 memset(bg_pal, 0, sizeof(bg_pal));
145 memset(spr_pal, 0, sizeof(spr_pal));
146 memset(solid_buf, 0, sizeof(solid_buf));
147 memset(name_tables, 0, sizeof(name_tables));
149 memset(spr_ram, 0, sizeof(spr_ram));
152 memset(regs, 0, sizeof(regs));
153 bg_pattern_table_addr = 0;
154 spr_pattern_table_addr = 0;
157 toggle_2005_2006 = false;
158 read_2007_buffer = 0;
164 // reset emphasised palette
168 void PPU::write_data8(uint32 addr, uint32 data)
172 regs[addr & 7] = data;
174 switch(addr & 0xe007) {
176 bg_pattern_table_addr = (data & 0x10) ? 0x1000 : 0;
177 spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0;
178 ppu_addr_inc = (data & 0x04) ? 32 : 1;
179 loopy_t = (loopy_t & 0xf3ff) | (((uint16)(data & 0x03)) << 10);
182 if(rgb_bak != (data & 0xe0)) {
184 rgb_bak = data & 0xe0;
188 spr_ram_rw_ptr = data;
191 spr_ram[spr_ram_rw_ptr++] = data;
194 toggle_2005_2006 = !toggle_2005_2006;
195 if(toggle_2005_2006) {
197 loopy_t = (loopy_t & 0xffe0) | (((uint16)(data & 0xf8)) >> 3);
198 loopy_x = data & 0x07;
201 loopy_t = (loopy_t & 0xfc1f) | (((uint16)(data & 0xf8)) << 2);
202 loopy_t = (loopy_t & 0x8fff) | (((uint16)(data & 0x07)) << 12);
206 toggle_2005_2006 = !toggle_2005_2006;
207 if(toggle_2005_2006) {
209 loopy_t = (loopy_t & 0x00ff) | (((uint16)(data & 0x3f)) << 8);
212 loopy_t = (loopy_t & 0xff00) | ((uint16)data);
217 ofs = loopy_v & 0x3fff;
218 loopy_v += ppu_addr_inc;
220 // is it a palette entry?
223 if(!(ofs & 0x000f)) {
224 bg_pal[0] = spr_pal[0] = data;
225 } else if(!(ofs & 0x10)) {
226 bg_pal[ofs & 0x000f] = data;
228 spr_pal[ofs & 0x000f] = data;
242 uint32 PPU::read_data8(uint32 addr)
247 switch(addr & 0xe007) {
250 toggle_2005_2006 = false;
252 // clear v-blank flag
256 ofs = loopy_v & 0x3fff;
257 loopy_v += ppu_addr_inc;
259 // is it a palette entry?
261 if(!(ofs & 0x0010)) {
262 return bg_pal[ofs & 0x000f];
264 return spr_pal[ofs & 0x000f];
270 val = read_2007_buffer;
271 read_2007_buffer = VRAM(ofs);
274 return regs[addr & 7];
277 void PPU::event_vline(int v, int clock)
281 if(spr_enabled() || bg_enabled()) {
286 // set vblank register flag
289 d_cpu->write_signal(SIG_CPU_NMI, 1, 1);
293 // reset vblank register flag and sprite0 hit flag1
302 void PPU::draw_screen()
305 for(int y = 0; y < 240; y++) {
306 scrntype* dest = emu->screen_buffer(y);
307 uint8* src = screen[y];
309 for(int x = 0; x < 256; x++) {
310 dest[x] = palette_pc[src[x + 8] & 0x3f];
315 void PPU::update_palette()
317 for(int i = 0; i < 64; i++) {
318 uint8 r = palette[i][0];
319 uint8 g = palette[i][1];
320 uint8 b = palette[i][2];
324 g = (uint8)(g * 0.80);
325 b = (uint8)(b * 0.73);
328 r = (uint8)(r * 0.73);
329 b = (uint8)(b * 0.70);
332 r = (uint8)(r * 0.76);
333 g = (uint8)(g * 0.78);
334 b = (uint8)(b * 0.58);
337 r = (uint8)(r * 0.86);
338 g = (uint8)(g * 0.80);
341 r = (uint8)(r * 0.83);
342 g = (uint8)(g * 0.68);
343 b = (uint8)(b * 0.85);
346 r = (uint8)(r * 0.67);
347 g = (uint8)(g * 0.77);
348 b = (uint8)(b * 0.83);
351 r = (uint8)(r * 0.68);
352 g = (uint8)(g * 0.68);
353 b = (uint8)(b * 0.68);
356 palette_pc[i] = RGB_COLOR(r, g, b);
360 void PPU::render_scanline(int v)
362 uint8* buf = screen[v];
365 // set to background color
366 memset(screen[v], bg_pal[0], 256 + 16);
368 if(spr_enabled() || bg_enabled()) {
369 LOOPY_SCANLINE_START(loopy_v, loopy_t);
373 memset(solid_buf, 0, sizeof(solid_buf));
379 LOOPY_NEXT_LINE(loopy_v);
383 #define BG_WRITTEN_FLAG 1
384 #define SPR_WRITTEN_FLAG 2
386 #define DRAW_BG_PIXEL() \
388 if(pattern_lo & pattern_mask) { \
391 if(pattern_hi & pattern_mask) { \
394 *p++ = monochrome() ? (bg_pal[col] & 0xf0) : bg_pal[col]; \
395 *solid++ = (col & 3) ? BG_WRITTEN_FLAG : 0; \
397 void PPU::render_bg(int v)
399 uint32 tile_x = (loopy_v & 0x001f);
400 uint32 tile_y = (loopy_v & 0x03e0) >> 5;
401 uint32 name_addr = 0x2000 + (loopy_v & 0x0fff);
402 uint32 attrib_addr = 0x2000 + (loopy_v & 0x0c00) + 0x03c0 + ((tile_y & 0xfffc) << 1) + (tile_x >> 2);
407 attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
409 attrib_bits = (VRAM(attrib_addr) & 0x0C);
413 attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
415 attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
418 uint8 *p = screen[v] + (8 - loopy_x);
419 uint8 *solid = solid_buf + (8 - loopy_x);
421 for(int i = 33; i; i--) {
422 uint32 pattern_addr = bg_pattern_table_addr + ((int32)VRAM(name_addr) << 4) + ((loopy_v & 0x7000) >> 12);
423 uint8 pattern_lo = VRAM(pattern_addr);
424 uint8 pattern_hi = VRAM(pattern_addr + 8);
425 uint8 pattern_mask = 0x80;
449 if(!(tile_x & 0x1f)) {
450 name_addr ^= 0x0400; // switch name tables
451 attrib_addr ^= 0x0400;
453 attrib_addr -= 0x0008;
460 attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
462 attrib_bits = (VRAM(attrib_addr) & 0x0c);
466 attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
468 attrib_bits = (VRAM(attrib_addr) & 0xc0) >> 4;
474 memset(&screen[v][8], bg_pal[0], 8);
475 memset(solid + 8, 0, 8);
479 void PPU::render_spr(int v)
482 int spr_height = sprites_8x16() ? 16 : 8;
484 for(int s = 0; s < 64; s++) {
485 uint8* spr = &spr_ram[s << 2];
486 int spr_y = spr[0] + 1;
488 if(spr_y > v || (spr_y + spr_height) <= v) {
492 if(num_sprites > 8) {
500 if((spr_x + 7) > 255) {
501 end_x -= ((spr_x + 7) - 255);
503 if((spr_x < 8) && (spr_clip())) {
507 start_x += (8 - spr_x);
511 uint8 *p = &screen[v][8 + spr_x + start_x];
512 uint8 *solid = &solid_buf[8 + spr_x + start_x];
515 start_x = (8 - 1) - start_x;
516 end_x = (8 - 1) - end_x;
520 y = (spr_height - 1) - y;
522 uint8 priority = spr[2] & 0x20;
524 for(int x = start_x; x != end_x; x += inc_x) {
529 if(!((*solid) & SPR_WRITTEN_FLAG)) {
531 tile_addr = spr[1] << 4;
542 tile_addr += y & 0x07;
543 tile_mask = (0x80 >> (x & 0x07));
545 tile_addr = spr[1] << 4;
546 tile_addr += y & 0x07;
547 tile_addr += spr_pattern_table_addr;
548 tile_mask = (0x80 >> (x & 0x07));
550 if(VRAM(tile_addr) & tile_mask) {
554 if(VRAM(tile_addr) & tile_mask) {
564 if(s && (*solid & BG_WRITTEN_FLAG)) {
568 *solid |= SPR_WRITTEN_FLAG;
569 if(!(*solid & BG_WRITTEN_FLAG)) {
570 *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
573 if(!(*solid & SPR_WRITTEN_FLAG)) {
574 *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
575 *solid |= SPR_WRITTEN_FLAG;
584 if(num_sprites >= 8) {
591 #define STATE_VERSION 1
593 void PPU::save_state(FILEIO* state_fio)
595 state_fio->FputUint32(STATE_VERSION);
596 state_fio->FputInt32(this_device_id);
598 state_fio->Fwrite(palette_pc, sizeof(palette_pc), 1);
599 state_fio->Fwrite(solid_buf, sizeof(solid_buf), 1);
600 state_fio->Fwrite(header, sizeof(header), 1);
601 state_fio->Fwrite(chr_rom, sizeof(chr_rom), 1);
602 state_fio->Fwrite(name_tables, sizeof(name_tables), 1);
603 state_fio->Fwrite(spr_ram, sizeof(spr_ram), 1);
604 state_fio->Fwrite(bg_pal, sizeof(bg_pal), 1);
605 state_fio->Fwrite(spr_pal, sizeof(spr_pal), 1);
606 state_fio->FputUint8(spr_ram_rw_ptr);
607 state_fio->Fwrite(regs, sizeof(regs), 1);
608 state_fio->FputUint16(bg_pattern_table_addr);
609 state_fio->FputUint16(spr_pattern_table_addr);
610 state_fio->FputUint16(ppu_addr_inc);
611 state_fio->FputUint8(rgb_bak);
612 state_fio->FputBool(toggle_2005_2006);
613 state_fio->FputUint8(read_2007_buffer);
614 state_fio->FputUint16(loopy_v);
615 state_fio->FputUint16(loopy_t);
616 state_fio->FputUint8(loopy_x);
619 bool PPU::load_state(FILEIO* state_fio)
621 if(state_fio->FgetUint32() != STATE_VERSION) {
624 if(state_fio->FgetInt32() != this_device_id) {
627 state_fio->Fread(palette_pc, sizeof(palette_pc), 1);
628 state_fio->Fread(solid_buf, sizeof(solid_buf), 1);
629 state_fio->Fread(header, sizeof(header), 1);
630 state_fio->Fread(chr_rom, sizeof(chr_rom), 1);
631 state_fio->Fread(name_tables, sizeof(name_tables), 1);
632 state_fio->Fread(spr_ram, sizeof(spr_ram), 1);
633 state_fio->Fread(bg_pal, sizeof(bg_pal), 1);
634 state_fio->Fread(spr_pal, sizeof(spr_pal), 1);
635 spr_ram_rw_ptr = state_fio->FgetUint8();
636 state_fio->Fread(regs, sizeof(regs), 1);
637 bg_pattern_table_addr = state_fio->FgetUint16();
638 spr_pattern_table_addr = state_fio->FgetUint16();
639 ppu_addr_inc = state_fio->FgetUint16();
640 rgb_bak = state_fio->FgetUint8();
641 toggle_2005_2006 = state_fio->FgetBool();
642 read_2007_buffer = state_fio->FgetUint8();
643 loopy_v = state_fio->FgetUint16();
644 loopy_t = state_fio->FgetUint16();
645 loopy_x = state_fio->FgetUint8();