OSDN Git Service

- ppu debug dump func updated
[motonesemu/motonesemu.git] / emulator / ppucore / ppucore.c
index e226647..5310e78 100644 (file)
@@ -1,49 +1,71 @@
 #include <string.h>
 #include <pthread.h>
 #include <limits.h>
+#include <stdio.h>
 
 #include "tools.h"
 #include "vram.h"
 #include "ppucore.h"
 #include "vga_xfer.h"
+#include "sprite.h"
+#include "clock.h"
 
-int vscreen_init(void);
+int init_vscreen(void);
 void clean_vscreen(void);
-int palette_init(void);
-void vga_xfer(void);
+int init_palette(void);
 void set_monocolor (int mono);
+void set_nmi_pin(int val);
+void set_bg_pattern_bank(unsigned char bank);
+void set_spr_pattern_bank(unsigned char bank);
+void set_bg_name_tbl_base(unsigned char sw);
+void spr_ram_tbl_set(unsigned short offset, unsigned char data);
+
+void set_vscreen_pos(int x, int y);
+int sprite_prefetch1(int srch_line);
+int sprite_prefetch2(int srch_line);
+int load_sprite(int background, int x, int y);
+int load_background(int x, int y);
+void vga_xfer(int x, int y);
+void vga_posinit(void);
+
+void dump_ppu_reg(void);
 
 /*
  * 6502 little endian
  * hi bit > low bit order
+ * but gcc generates low > hi order for bit field
  * */
 struct ppu_ctrl_reg1 {
-    unsigned int nmi_vblank     :1;
-    unsigned int ppu_mode       :1;
-    unsigned int sprite_size    :1;
-    unsigned int bg_ptn_addr_sel :1;
-    unsigned int sprite_ptn_sel :1;
-    unsigned int addr_inc_size  :1;
     unsigned int name_tbl_sel   :2;
+    unsigned int addr_inc_size  :1;
+    unsigned int sprite_ptn_sel :1;
+    unsigned int bg_ptn_addr_sel :1;
+    unsigned int sprite_size    :1;
+    unsigned int ppu_mode       :1;
+    unsigned int nmi_vblank     :1;
 } __attribute__ ((packed));
 
 struct ppu_ctrl_reg2 {
+    unsigned int color_mode     :1;
+    unsigned int show_left_8bg  :1;
+    unsigned int show_left_8sprite  :1;
+    unsigned int show_bg        :1;
+    unsigned int show_sprite    :1;
     unsigned int intense_b      :1;
     unsigned int intense_g      :1;
     unsigned int intense_r      :1;
-    unsigned int show_sprite    :1;
-    unsigned int show_bg        :1;
-    unsigned int show_left_8sprite  :1;
-    unsigned int show_left_8bg  :1;
-    unsigned int color_mode     :1;
 } __attribute__ ((packed));
 
+/*
+ * 6502 little endian
+ * but gcc generates low > hi order when bit field is used??
+ * */
 struct ppu_status_reg {
-    unsigned int vblank         :1;
-    unsigned int sprite_0_hit   :1;
-    unsigned int sprite_overflow    :1;
-    unsigned int vram_ignore    :1;
     unsigned int nouse          :4;
+    unsigned int vram_ignore    :1;
+    unsigned int sprite_overflow    :1;
+    unsigned int sprite0_hit   :1;
+    unsigned int vblank         :1;
 } __attribute__ ((packed));
 
 struct ppu_vram_addr_reg {
@@ -57,80 +79,136 @@ struct ppu_vram_addr_reg {
     unsigned int cnt :1;
 };
 
+struct ppu_scroll_reg {
+    unsigned char x;
+    unsigned char y;
+    unsigned int cnt :1;
+};
 /*ppu core register instances*/
 
 static struct ppu_ctrl_reg1 ctrl_reg1;
 static struct ppu_ctrl_reg2 ctrl_reg2;
 static struct ppu_status_reg status_reg;
 static struct ppu_vram_addr_reg vram_addr_reg;
+static struct ppu_scroll_reg scroll_reg;
+static struct ppu_sprite_reg sprite_reg;
 
 static unsigned char sprite_ram_addr_reg;
-static unsigned char sprite_ram_data_reg;
 static unsigned char sprite_ram_dma_reg;
 static unsigned char vram_data_reg;
-static unsigned char scroll_reg;
 static unsigned char vram_dma_reg;
 
+//value set by the ctrl_reg1.
+static unsigned char    sprite_size_type;
+static unsigned char    vram_addr_inc;
+static unsigned int     vram_read_cnt;
 
-static pthread_t ppucore_thread_id;
-static int ppucore_end_loop;
+#define SPR_STYPE_8x8   0 
+#define SPR_STYPE_8x16  1
 
-/*
- * ppucore main loop.
- * periodically update the display buffer.
- * */
-static void *ppucore_loop(void* arg) {
-    //struct timespec ts = {CPU_CLOCK_SEC, CPU_CLOCK_NSEC / 10};
-    struct timespec begin, end;
-    struct timespec slp;
-    long sec, nsec;
-#define NANOMAX (1000000000 - 1)
+#define SCAN_LINE       (V_SCREEN_TILE_SIZE * TILE_DOT_SIZE)
+
+static int scan_x;
+static int scan_y;
+
+static int scan_recovery(void) {
+    if (scan_y == VSCREEN_HEIGHT)
+        return FALSE;
+
+    /*
+     * Sprite reg:
+     * Stores the y-coordinate of the top left of the sprite minus 1
+     * */
+    if (scan_x == HSCAN_MAX - 1 && scan_y < VSCREEN_HEIGHT) {
+        int next_line = (scan_y == VSCAN_MAX - 1 ? 0 : scan_y + 1);
+        sprite_prefetch1(next_line);
+        sprite_prefetch2(next_line);
+        return TRUE;
+    }
+    return FALSE;
+}
 
-    while (!ppucore_end_loop) {
-        int updated = FALSE;
+static int clock_ppu(void) {
+//    dprint("ppu x:%d, y:%d...\n", scan_x, scan_y);
+
+    if (scan_x < VSCREEN_WIDTH && scan_y < VSCREEN_HEIGHT) {
+        if (scan_x == 0) {
+            status_reg.sprite0_hit = 0;
+            status_reg.sprite_overflow = 0;
+            if (scan_y == 0) {
+                //start displaying
+                status_reg.vblank = 0;
+            }
+        }
 
-        clock_gettime(CLOCK_REALTIME, &begin);
+        set_vscreen_pos(scan_x, scan_y);
         if (ctrl_reg2.show_sprite) {
             //sprite in the back
-            ;
+            load_sprite(TRUE, scan_x, scan_y);
         }
-        if (ctrl_reg2.show_bg) {
-            //back ground image
-            updated |= show_background();
+        if (ctrl_reg2.show_bg/**/) {
+            //back ground image is pre-loaded. load 1 line ahead of drawline.
+            load_background(scan_x + scroll_reg.x, scan_y + scroll_reg.y);
         }
         if (ctrl_reg2.show_sprite) {
             //foreground sprite
-            ;
+            load_sprite(FALSE, scan_x, scan_y);
         }
-        if (updated) 
-            vga_xfer();
-        clock_gettime(CLOCK_REALTIME, &end);
-
-        //sleep rest of time...
-        if (end.tv_sec < begin.tv_sec )
-            sec = LONG_MAX - begin.tv_sec + end.tv_sec + 1;
-        else
-            sec = end.tv_sec - begin.tv_sec;
-
-        if (end.tv_nsec < begin.tv_nsec)
-            nsec = NANOMAX - begin.tv_nsec + end.tv_nsec + 1;
-        else
-            nsec = end.tv_nsec - begin.tv_nsec;
-
-        if (sec < NES_VIDEO_CLK_SEC || nsec < NES_VIDEO_CLK_NSEC) {
-            int ret;
-            slp.tv_sec = sec > NES_VIDEO_CLK_SEC ? 0 : NES_VIDEO_CLK_SEC - sec;
-            slp.tv_nsec = nsec > NES_VIDEO_CLK_NSEC ? 0 : NES_VIDEO_CLK_NSEC - nsec;
-
-            //dprint("%d.%09d sec sleep\n", slp.tv_sec, slp.tv_nsec);
-            ret = nanosleep(&slp, NULL);
+        vga_xfer(scan_x, scan_y);
+    }
+    else {
+        scan_recovery();
+        if (scan_x == 0 && scan_y == VSCREEN_HEIGHT) {
+            //printing display done.
+            status_reg.vblank = 1;
+            if (ctrl_reg1.nmi_vblank) {
+                //generate nmi interrupt to the cpu.
+                set_nmi_pin(TRUE);
+            }
         }
     }
-    return NULL;
+
+
+    scan_x++;
+    if (scan_x == HSCAN_MAX) {
+        scan_x = 0;
+        scan_y++;
+    }
+    if (scan_y == VSCAN_MAX) {
+        scan_y = 0;
+    }
+    return TRUE;
 }
 
 void ppu_ctrl1_set(unsigned char data) {
+    unsigned char old, diff_tmp; 
+    struct ppu_ctrl_reg1 diff; 
+
+    memcpy(&old, &ctrl_reg1, sizeof(ctrl_reg1));
     memcpy(&ctrl_reg1, &data, sizeof(ctrl_reg1));
+
+    diff_tmp = old ^ data;
+    memcpy(&diff, &diff_tmp, sizeof(ctrl_reg1));
+
+    //set sprite_size
+    //if (diff.sprite_size)
+    //    sprite_size = (ctrl_reg1.sprite_size == 0 ? 8 : 16);
+
+    //set bg base tbl addr
+    if (diff.bg_ptn_addr_sel)
+        set_bg_pattern_bank(ctrl_reg1.bg_ptn_addr_sel);
+    //set sprite base tbl addr
+    if (diff.sprite_ptn_sel) 
+        set_spr_pattern_bank(ctrl_reg1.sprite_ptn_sel);
+    //set vram address increase unit
+    if (diff.addr_inc_size) 
+        vram_addr_inc = (ctrl_reg1.addr_inc_size == 0 ? 1 : 32);
+    //set main screen addr
+    if (diff.name_tbl_sel)
+        set_bg_name_tbl_base(ctrl_reg1.name_tbl_sel);
+
+    //dprint("ctrl1: %x\n", data);
+    //dump_ppu_reg();
 }
 
 void ppu_ctrl2_set(unsigned char data) {
@@ -141,24 +219,58 @@ void ppu_ctrl2_set(unsigned char data) {
 
     if (old.color_mode != ctrl_reg2.color_mode)
         set_monocolor(ctrl_reg2.color_mode);
+
+    //dprint("ctrl2 %x:\n", data);
+    //dump_ppu_reg();
 }
 
 unsigned char ppu_status_get(void) {
     unsigned char ret;
     memcpy(&ret, &status_reg, sizeof(status_reg));
+
+    //if read status reg, vram addr register counter is reset
+    vram_addr_reg.cnt = 0;
+    //dprint("ppu_status:%02x\n", ret);
+
+    //reading status resets the vblank flg.
+    status_reg.vblank = 0;
     return ret;
 }
 
 void sprite_addr_set(unsigned char addr) {
+    //dprint("sprite_addr_set addr=%02x\n", addr);
     sprite_ram_addr_reg = addr;
+    sprite_reg.cnt = 0;
 }
 
 void sprite_data_set(unsigned char data) {
-    sprite_ram_data_reg = data;
+    //dprint("sprite_data_set addr=%02x, data=%02x, cnt:%d\n", 
+     //       sprite_ram_addr_reg, data, sprite_reg.cnt);
+    switch (sprite_reg.cnt) {
+        case 0:
+        default:
+            sprite_reg.y = data;
+            break;
+        case 1:
+            sprite_reg.index = data;
+            break;
+        case 2:
+            memcpy(&sprite_reg.sa, &data, sizeof(struct sprite_attr));
+            break;
+        case 3:
+            sprite_reg.x = data;
+            break;
+    }
+    spr_ram_tbl_set(sprite_ram_addr_reg + sprite_reg.cnt, data);
+    sprite_reg.cnt++;
 }
 
 void ppu_scroll_set(unsigned char data) {
-    scroll_reg = data;
+    //dprint("scroll set %s = %02x\n", (scroll_reg.cnt == 0 ? "x" : "y"), data);
+    if (scroll_reg.cnt++ == 0)
+        scroll_reg.x = data;
+    else
+        scroll_reg.y = data;
 }
 
 void ppu_vram_addr_set(unsigned char half_addr) {
@@ -167,79 +279,139 @@ void ppu_vram_addr_set(unsigned char half_addr) {
         vram_addr_reg.addr.b.hi = half_addr;
     else
         vram_addr_reg.addr.b.low = half_addr;
+
+    //when setting the addr, read cnt is reset.
+    vram_read_cnt = 0;
 }
 
 void ppu_vram_data_set(unsigned char data) {
+    //check vram_ignore bit on write.
+    /*
+     * TODO ignore bit set timing is unknown.
+    */
+    if (status_reg.vram_ignore)
+        return;
+
     //dprint("vram data:%04x\n", data);
     vram_data_reg = data;
 
     vram_data_set(vram_addr_reg.addr.s, data);
     //vram addr increment.
-    if (ctrl_reg1.addr_inc_size) {
-        vram_addr_reg.addr.s += 32;
-    }
-    else {
-        vram_addr_reg.addr.s += 1;
-    }
+    vram_addr_reg.addr.s += vram_addr_inc;
 }
 
 unsigned char ppu_vram_data_get(void) {
+    if (vram_read_cnt++ == 0) {
+        //first read always fail.
+        return 0;
+    }
+
+    vram_data_reg = vram_data_get(vram_addr_reg.addr.s);
+    //vram addr increment.
+    vram_addr_reg.addr.s += vram_addr_inc;
     return vram_data_reg;
 }
 
-int ppucore_init(void) {
+void sprite0_hit_set(void) {
+    //dprint("sprite0 set...\n");
+    status_reg.sprite0_hit = 1;
+}
+
+void sprite_overflow_set(void) {
+    //dprint("sprite0 set...\n");
+    status_reg.sprite_overflow = 1;
+}
+
+int init_ppucore(void) {
     int ret;
-    pthread_attr_t attr;
 
     memset(&ctrl_reg1, 0, sizeof(ctrl_reg1));
     memset(&ctrl_reg2, 0,  sizeof(ctrl_reg1));
     memset(&status_reg, 0, sizeof(status_reg));
     memset(&vram_addr_reg, 0, sizeof(vram_addr_reg));
+    memset(&scroll_reg, 0, sizeof(scroll_reg));
+    memset(&sprite_reg, 0, sizeof(sprite_reg));
 
     sprite_ram_addr_reg = 0;
-    sprite_ram_data_reg = 0;
     sprite_ram_dma_reg = 0;
     vram_data_reg = 0;
-    scroll_reg = 0;
     vram_dma_reg = 0;
+    vram_read_cnt = 0;
+
+    sprite_size_type = SPR_STYPE_8x8;
+    vram_addr_inc = 1;
 
-    ret = vram_init();
+    scan_x = 0;
+    scan_y = 0;
+
+    ret = init_vga_xfer();
     if (!ret)
         return FALSE;
 
-    ret = vga_xfer_init();
+    ret = init_sprite();
     if (!ret)
         return FALSE;
 
-    ret = vscreen_init();
+    ret = init_vscreen();
     if (!ret)
         return FALSE;
 
-    ret = palette_init();
+    ret = init_palette();
     if (!ret)
         return FALSE;
 
-    ppucore_end_loop = FALSE;
-    ret = pthread_attr_init(&attr);
-    if (ret != RT_OK) {
+    ret = init_vram();
+    if (!ret)
         return FALSE;
-    }
-    ppucore_thread_id = 0;
-    ret = pthread_create(&ppucore_thread_id, &attr, ppucore_loop, NULL);
-    if (ret != RT_OK) {
+    ret = register_clock_hander(clock_ppu, PPU_DEVIDER);
+    if (!ret) {
         return FALSE;
     }
 
+    //vga_posinit();
 
     return TRUE;
 }
 
 void clean_ppucore(void) {
-    void* ret;
-    ppucore_end_loop = TRUE;
-    pthread_join(ppucore_thread_id, &ret);
-    dprint("ppucore thread joined.\n");
-
     clean_vram();
+    clean_sprite();
     clean_vscreen();
 }
+
+/*
+ * for debug.c
+ * */
+void dump_ppu_reg(void) {
+    printf("control reg1\n");
+    printf(" nmi_vblank:%d\n", ctrl_reg1.nmi_vblank);
+    printf(" sprite_size:%d\n", ctrl_reg1.sprite_size);
+    printf(" bg_ptn:%d\n", ctrl_reg1.bg_ptn_addr_sel);
+    printf(" spr_ptn:%d\n", ctrl_reg1.sprite_ptn_sel);
+    printf(" inc size:%d\n", ctrl_reg1.addr_inc_size);
+    printf(" name tbl:%d\n", ctrl_reg1.name_tbl_sel);
+
+    printf("\ncontrol reg2\n");
+    printf(" intense r:%d\n", ctrl_reg2.intense_r);
+    printf(" intense g:%d\n", ctrl_reg2.intense_g);
+    printf(" intense b:%d\n", ctrl_reg2.intense_b);
+
+    printf(" show spr:%d\n", ctrl_reg2.show_sprite);
+    printf(" show bg:%d\n", ctrl_reg2.show_bg);
+    printf(" left 8 pix spr:%d\n", ctrl_reg2.show_left_8sprite);
+    printf(" left 8 pix bg:%d\n", ctrl_reg2.show_left_8bg);
+    printf(" col mode:%d\n", ctrl_reg2.color_mode);
+
+    printf("\nstatus reg\n");
+    printf(" vram_ignore:%d\n", status_reg.vram_ignore);
+    printf(" sprite_overflow:%d\n", status_reg.sprite_overflow);
+    printf(" sprite0_hit:%d\n", status_reg.sprite0_hit);
+    printf(" vblank:%d\n", status_reg.vblank);
+
+    printf("\nvram_addr_reg:%04x\n", vram_addr_reg.addr.s);
+    printf("vram_data_reg:%02x\n", vram_data_reg);
+    printf("\nscroll_reg:\n");
+    printf(" x:%d\n", scroll_reg.x);
+    printf(" y:%d\n", scroll_reg.y);
+}
+