2 Nintendo Family BASIC Emulator 'eFamilyBASIC'
\r
5 Author : Takeda.Toshiya
\r
12 #include "../../fileio.h"
\r
14 static const uint8 palette[64][3] = {
\r
15 {0x75, 0x75, 0x75}, {0x27, 0x1b, 0x8f}, {0x00, 0x00, 0xab}, {0x47, 0x00, 0x9f},
\r
16 {0x8f, 0x00, 0x77}, {0xab, 0x00, 0x13}, {0xa7, 0x00, 0x00}, {0x7f, 0x0b, 0x00},
\r
17 {0x43, 0x2f, 0x00}, {0x00, 0x47, 0x00}, {0x00, 0x51, 0x00}, {0x00, 0x3f, 0x17},
\r
18 {0x1b, 0x3f, 0x5f}, {0x00, 0x00, 0x00}, {0x05, 0x05, 0x05}, {0x05, 0x05, 0x05},
\r
19 {0xbc, 0xbc, 0xbc}, {0x00, 0x73, 0xef}, {0x23, 0x3b, 0xef}, {0x83, 0x00, 0xf3},
\r
20 {0xbf, 0x00, 0xbf}, {0xe7, 0x00, 0x5b}, {0xdb, 0x2b, 0x00}, {0xcb, 0x4f, 0x0f},
\r
21 {0x8b, 0x73, 0x00}, {0x00, 0x97, 0x00}, {0x00, 0xab, 0x00}, {0x00, 0x93, 0x3b},
\r
22 {0x00, 0x83, 0x8b}, {0x11, 0x11, 0x11}, {0x09, 0x09, 0x09}, {0x09, 0x09, 0x09},
\r
23 {0xff, 0xff, 0xff}, {0x3f, 0xbf, 0xff}, {0x5f, 0x97, 0xff}, {0xa7, 0x8b, 0xfd},
\r
24 {0xf7, 0x7b, 0xff}, {0xff, 0x77, 0xb7}, {0xff, 0x77, 0x63}, {0xff, 0x9b, 0x3b},
\r
25 {0xf3, 0xbf, 0x3f}, {0x83, 0xd3, 0x13}, {0x4f, 0xdf, 0x4b}, {0x58, 0xf8, 0x98},
\r
26 {0x00, 0xeb, 0xdb}, {0x66, 0x66, 0x66}, {0x0d, 0x0d, 0x0d}, {0x0d, 0x0d, 0x0d},
\r
27 {0xff, 0xff, 0xff}, {0xab, 0xe7, 0xff}, {0xc7, 0xd7, 0xff}, {0xd7, 0xcb, 0xff},
\r
28 {0xff, 0xc7, 0xff}, {0xff, 0xc7, 0xdb}, {0xff, 0xbf, 0xb3}, {0xff, 0xdb, 0xab},
\r
29 {0xff, 0xe7, 0xa3}, {0xe3, 0xff, 0xa3}, {0xab, 0xf3, 0xbf}, {0xb3, 0xff, 0xcf},
\r
30 {0x9f, 0xff, 0xf3}, {0xdd, 0xdd, 0xdd}, {0x11, 0x11, 0x11}, {0x11, 0x11, 0x11}
\r
33 #define VRAM(addr) banks[((addr) >> 10) & 0x0f][(addr) & 0x3ff]
\r
35 #define NMI_enabled() (regs[0] & 0x80)
\r
36 #define sprites_8x16() (regs[0] & 0x20)
\r
37 #define spr_enabled() (regs[1] & 0x10)
\r
38 #define bg_enabled() (regs[1] & 0x08)
\r
39 #define spr_clip() (!(regs[1] & 0x04))
\r
40 #define bg_clip() (!(regs[1] & 0x02))
\r
41 #define monochrome() (regs[1] & 0x01)
\r
42 #define rgb_pal() (regs[1] & 0xe0)
\r
43 #define sprite0_hit() (regs[2] & 0x40)
\r
45 #define LOOPY_SCANLINE_START(v, t) { \
\r
46 v = (v & 0xfbe0) | (t & 0x041f); \
\r
49 #define LOOPY_NEXT_LINE(v) { \
\r
50 if((v & 0x7000) == 0x7000) { \
\r
52 if((v & 0x3e0) == 0x3a0) { \
\r
56 if((v & 0x3e0) == 0x3e0) { \
\r
67 #define LOOPY_NEXT_TILE(v) { \
\r
68 if((v & 0x1f) == 0x1f) { \
\r
76 #define LOOPY_NEXT_PIXEL(v, x) { \
\r
78 LOOPY_NEXT_TILE(v); \
\r
85 void PPU::initialize()
\r
88 register_vline_event(this);
\r
91 void PPU::load_rom_image(_TCHAR *file_name)
\r
93 FILEIO* fio = new FILEIO();
\r
94 bool file_open = false;
\r
96 if(fio->Fopen(emu->bios_path(file_name), FILEIO_READ_BINARY)) {
\r
98 } else if(fio->Fopen(emu->bios_path(_T("BASIC.NES")), FILEIO_READ_BINARY)) {
\r
99 // for compatibility
\r
104 fio->Fread(header, sizeof(header), 1);
\r
105 // skip program rom
\r
106 fio->Fseek(header[4] * 0x4000, FILEIO_SEEK_CUR);
\r
107 // read chr rom (max 8kb)
\r
108 fio->Fread(chr_rom, sizeof(chr_rom), 1);
\r
111 memset(header, 0, sizeof(header));
\r
112 memset(chr_rom, 0xff, sizeof(chr_rom));
\r
119 // set up PPU memory space table
\r
120 for(int i = 0; i < 8; i++) {
\r
121 banks[i] = chr_rom + 0x400 * i;
\r
126 if(header[6] & 8) {
\r
127 // 4 screen mirroring
\r
128 banks[ 8] = banks[12] = name_tables;
\r
129 banks[ 9] = banks[13] = name_tables + 0x400;
\r
130 banks[10] = banks[14] = name_tables + 0x800;
\r
131 banks[11] = banks[15] = name_tables + 0xc00;
\r
132 } else if(header[6] & 1) {
\r
134 // vertical mirroring
\r
135 banks[ 8] = banks[10] = banks[12] = banks[14] = name_tables;
\r
136 banks[ 9] = banks[11] = banks[13] = banks[15] = name_tables + 0x400;
\r
139 // horizontal mirroring
\r
140 banks[ 8] = banks[ 9] = banks[12] = banks[13] = name_tables;
\r
141 banks[10] = banks[11] = banks[14] = banks[15] = name_tables + 0x400;
\r
145 memset(bg_pal, 0, sizeof(bg_pal));
\r
146 memset(spr_pal, 0, sizeof(spr_pal));
\r
147 memset(solid_buf, 0, sizeof(solid_buf));
\r
148 memset(name_tables, 0, sizeof(name_tables));
\r
150 memset(spr_ram, 0, sizeof(spr_ram));
\r
151 spr_ram_rw_ptr = 0;
\r
153 memset(regs, 0, sizeof(regs));
\r
154 bg_pattern_table_addr = 0;
\r
155 spr_pattern_table_addr = 0;
\r
158 toggle_2005_2006 = false;
\r
159 read_2007_buffer = 0;
\r
165 // reset emphasised palette
\r
169 void PPU::write_data8(uint32 addr, uint32 data)
\r
173 regs[addr & 7] = data;
\r
175 switch(addr & 0xe007) {
\r
177 bg_pattern_table_addr = (data & 0x10) ? 0x1000 : 0;
\r
178 spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0;
\r
179 ppu_addr_inc = (data & 0x04) ? 32 : 1;
\r
180 loopy_t = (loopy_t & 0xf3ff) | (((uint16)(data & 0x03)) << 10);
\r
183 if(rgb_bak != (data & 0xe0)) {
\r
185 rgb_bak = data & 0xe0;
\r
189 spr_ram_rw_ptr = data;
\r
192 spr_ram[spr_ram_rw_ptr++] = data;
\r
195 toggle_2005_2006 = !toggle_2005_2006;
\r
196 if(toggle_2005_2006) {
\r
198 loopy_t = (loopy_t & 0xffe0) | (((uint16)(data & 0xf8)) >> 3);
\r
199 loopy_x = data & 0x07;
\r
202 loopy_t = (loopy_t & 0xfc1f) | (((uint16)(data & 0xf8)) << 2);
\r
203 loopy_t = (loopy_t & 0x8fff) | (((uint16)(data & 0x07)) << 12);
\r
207 toggle_2005_2006 = !toggle_2005_2006;
\r
208 if(toggle_2005_2006) {
\r
210 loopy_t = (loopy_t & 0x00ff) | (((uint16)(data & 0x3f)) << 8);
\r
213 loopy_t = (loopy_t & 0xff00) | ((uint16)data);
\r
218 ofs = loopy_v & 0x3fff;
\r
219 loopy_v += ppu_addr_inc;
\r
220 if(ofs >= 0x3000) {
\r
221 // is it a palette entry?
\r
222 if(ofs >= 0x3f00) {
\r
224 if(!(ofs & 0x000f)) {
\r
225 bg_pal[0] = spr_pal[0] = data;
\r
226 } else if(!(ofs & 0x10)) {
\r
227 bg_pal[ofs & 0x000f] = data;
\r
229 spr_pal[ofs & 0x000f] = data;
\r
233 // handle mirroring
\r
236 if(ofs >= 0x2000) {
\r
243 uint32 PPU::read_data8(uint32 addr)
\r
248 switch(addr & 0xe007) {
\r
251 toggle_2005_2006 = false;
\r
253 // clear v-blank flag
\r
257 ofs = loopy_v & 0x3fff;
\r
258 loopy_v += ppu_addr_inc;
\r
259 if(ofs >= 0x3000) {
\r
260 // is it a palette entry?
\r
261 if(ofs >= 0x3f00) {
\r
262 if(!(ofs & 0x0010)) {
\r
263 return bg_pal[ofs & 0x000f];
\r
265 return spr_pal[ofs & 0x000f];
\r
268 // handle mirroring
\r
271 val = read_2007_buffer;
\r
272 read_2007_buffer = VRAM(ofs);
\r
275 return regs[addr & 7];
\r
278 void PPU::event_vline(int v, int clock)
\r
282 if(spr_enabled() || bg_enabled()) {
\r
287 // set vblank register flag
\r
289 if(NMI_enabled()) {
\r
290 d_cpu->write_signal(SIG_CPU_NMI, 1, 1);
\r
294 // reset vblank register flag and sprite0 hit flag1
\r
299 render_scanline(v);
\r
303 void PPU::draw_screen()
\r
306 for(int y = 0; y < 240; y++) {
\r
307 scrntype* dest = emu->screen_buffer(y);
\r
308 uint8* src = screen[y];
\r
310 for(int x = 0; x < 256; x++) {
\r
311 dest[x] = palette_pc[src[x + 8] & 0x3f];
\r
316 void PPU::update_palette()
\r
318 for(int i = 0; i < 64; i++) {
\r
319 uint8 r = palette[i][0];
\r
320 uint8 g = palette[i][1];
\r
321 uint8 b = palette[i][2];
\r
323 switch(rgb_pal()) {
\r
325 g = (uint8)(g * 0.80);
\r
326 b = (uint8)(b * 0.73);
\r
329 r = (uint8)(r * 0.73);
\r
330 b = (uint8)(b * 0.70);
\r
333 r = (uint8)(r * 0.76);
\r
334 g = (uint8)(g * 0.78);
\r
335 b = (uint8)(b * 0.58);
\r
338 r = (uint8)(r * 0.86);
\r
339 g = (uint8)(g * 0.80);
\r
342 r = (uint8)(r * 0.83);
\r
343 g = (uint8)(g * 0.68);
\r
344 b = (uint8)(b * 0.85);
\r
347 r = (uint8)(r * 0.67);
\r
348 g = (uint8)(g * 0.77);
\r
349 b = (uint8)(b * 0.83);
\r
352 r = (uint8)(r * 0.68);
\r
353 g = (uint8)(g * 0.68);
\r
354 b = (uint8)(b * 0.68);
\r
357 palette_pc[i] = RGB_COLOR(r, g, b);
\r
361 void PPU::render_scanline(int v)
\r
363 uint8* buf = screen[v];
\r
365 if(!bg_enabled()) {
\r
366 // set to background color
\r
367 memset(screen[v], bg_pal[0], 256 + 16);
\r
369 if(spr_enabled() || bg_enabled()) {
\r
370 LOOPY_SCANLINE_START(loopy_v, loopy_t);
\r
374 memset(solid_buf, 0, sizeof(solid_buf));
\r
376 if(spr_enabled()) {
\r
380 LOOPY_NEXT_LINE(loopy_v);
\r
384 #define BG_WRITTEN_FLAG 1
\r
385 #define SPR_WRITTEN_FLAG 2
\r
387 #define DRAW_BG_PIXEL() \
\r
388 col = attrib_bits; \
\r
389 if(pattern_lo & pattern_mask) { \
\r
392 if(pattern_hi & pattern_mask) { \
\r
395 *p++ = monochrome() ? (bg_pal[col] & 0xf0) : bg_pal[col]; \
\r
396 *solid++ = (col & 3) ? BG_WRITTEN_FLAG : 0; \
\r
398 void PPU::render_bg(int v)
\r
400 uint32 tile_x = (loopy_v & 0x001f);
\r
401 uint32 tile_y = (loopy_v & 0x03e0) >> 5;
\r
402 uint32 name_addr = 0x2000 + (loopy_v & 0x0fff);
\r
403 uint32 attrib_addr = 0x2000 + (loopy_v & 0x0c00) + 0x03c0 + ((tile_y & 0xfffc) << 1) + (tile_x >> 2);
\r
406 if(!(tile_y & 2)) {
\r
407 if(!(tile_x & 2)) {
\r
408 attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
\r
410 attrib_bits = (VRAM(attrib_addr) & 0x0C);
\r
413 if(!(tile_x & 2)) {
\r
414 attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
\r
416 attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
\r
419 uint8 *p = screen[v] + (8 - loopy_x);
\r
420 uint8 *solid = solid_buf + (8 - loopy_x);
\r
422 for(int i = 33; i; i--) {
\r
423 uint32 pattern_addr = bg_pattern_table_addr + ((int32)VRAM(name_addr) << 4) + ((loopy_v & 0x7000) >> 12);
\r
424 uint8 pattern_lo = VRAM(pattern_addr);
\r
425 uint8 pattern_hi = VRAM(pattern_addr + 8);
\r
426 uint8 pattern_mask = 0x80;
\r
430 pattern_mask >>= 1;
\r
432 pattern_mask >>= 1;
\r
434 pattern_mask >>= 1;
\r
436 pattern_mask >>= 1;
\r
438 pattern_mask >>= 1;
\r
440 pattern_mask >>= 1;
\r
442 pattern_mask >>= 1;
\r
448 if(!(tile_x & 1)) {
\r
449 if(!(tile_x & 3)) {
\r
450 if(!(tile_x & 0x1f)) {
\r
451 name_addr ^= 0x0400; // switch name tables
\r
452 attrib_addr ^= 0x0400;
\r
453 name_addr -= 0x0020;
\r
454 attrib_addr -= 0x0008;
\r
459 if(!(tile_y & 2)) {
\r
460 if(!(tile_x & 2)) {
\r
461 attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
\r
463 attrib_bits = (VRAM(attrib_addr) & 0x0c);
\r
466 if(!(tile_x & 2)) {
\r
467 attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
\r
469 attrib_bits = (VRAM(attrib_addr) & 0xc0) >> 4;
\r
475 memset(&screen[v][8], bg_pal[0], 8);
\r
476 memset(solid + 8, 0, 8);
\r
480 void PPU::render_spr(int v)
\r
482 int num_sprites = 0;
\r
483 int spr_height = sprites_8x16() ? 16 : 8;
\r
485 for(int s = 0; s < 64; s++) {
\r
486 uint8* spr = &spr_ram[s << 2];
\r
487 int spr_y = spr[0] + 1;
\r
489 if(spr_y > v || (spr_y + spr_height) <= v) {
\r
493 if(num_sprites > 8) {
\r
496 int spr_x = spr[3];
\r
501 if((spr_x + 7) > 255) {
\r
502 end_x -= ((spr_x + 7) - 255);
\r
504 if((spr_x < 8) && (spr_clip())) {
\r
508 start_x += (8 - spr_x);
\r
512 uint8 *p = &screen[v][8 + spr_x + start_x];
\r
513 uint8 *solid = &solid_buf[8 + spr_x + start_x];
\r
515 if(spr[2] & 0x40) {
\r
516 start_x = (8 - 1) - start_x;
\r
517 end_x = (8 - 1) - end_x;
\r
520 if(spr[2] & 0x80) {
\r
521 y = (spr_height - 1) - y;
\r
523 uint8 priority = spr[2] & 0x20;
\r
525 for(int x = start_x; x != end_x; x += inc_x) {
\r
530 if(!((*solid) & SPR_WRITTEN_FLAG)) {
\r
531 if(sprites_8x16()) {
\r
532 tile_addr = spr[1] << 4;
\r
533 if(spr[1] & 0x01) {
\r
534 tile_addr += 0x1000;
\r
543 tile_addr += y & 0x07;
\r
544 tile_mask = (0x80 >> (x & 0x07));
\r
546 tile_addr = spr[1] << 4;
\r
547 tile_addr += y & 0x07;
\r
548 tile_addr += spr_pattern_table_addr;
\r
549 tile_mask = (0x80 >> (x & 0x07));
\r
551 if(VRAM(tile_addr) & tile_mask) {
\r
555 if(VRAM(tile_addr) & tile_mask) {
\r
565 if(s && (*solid & BG_WRITTEN_FLAG)) {
\r
569 *solid |= SPR_WRITTEN_FLAG;
\r
570 if(!(*solid & BG_WRITTEN_FLAG)) {
\r
571 *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
\r
574 if(!(*solid & SPR_WRITTEN_FLAG)) {
\r
575 *p = monochrome() ? (spr_pal[col] & 0xf0) : spr_pal[col];
\r
576 *solid |= SPR_WRITTEN_FLAG;
\r
585 if(num_sprites >= 8) {
\r