13 int vscreen_init(void);
14 void clean_vscreen(void);
15 int palette_init(void);
16 void set_monocolor (int mono);
17 void set_nmi_pin(int val);
18 void set_bg_pattern_bank(unsigned char bank);
19 void set_spr_pattern_bank(unsigned char bank);
20 void set_bg_name_tbl_base(unsigned char sw);
21 void spr_ram_tbl_set(unsigned short offset, unsigned char data);
23 int load_sprite(int foreground, int scanline);
24 int load_background(int scanline);
25 void vga_xfer(int scanline);
27 void dump_ppu_reg(void);
31 * hi bit > low bit order
32 * but gcc generates low > hi order for bit field
34 struct ppu_ctrl_reg1 {
35 unsigned int name_tbl_sel :2;
36 unsigned int addr_inc_size :1;
37 unsigned int sprite_ptn_sel :1;
38 unsigned int bg_ptn_addr_sel :1;
39 unsigned int sprite_size :1;
40 unsigned int ppu_mode :1;
41 unsigned int nmi_vblank :1;
42 } __attribute__ ((packed));
44 struct ppu_ctrl_reg2 {
45 unsigned int color_mode :1;
46 unsigned int show_left_8bg :1;
47 unsigned int show_left_8sprite :1;
48 unsigned int show_bg :1;
49 unsigned int show_sprite :1;
50 unsigned int intense_b :1;
51 unsigned int intense_g :1;
52 unsigned int intense_r :1;
53 } __attribute__ ((packed));
57 * but gcc generates low > hi order when bit field is used??
59 struct ppu_status_reg {
60 unsigned int nouse :4;
61 unsigned int vram_ignore :1;
62 unsigned int sprite_overflow :1;
63 unsigned int sprite0_hit :1;
64 unsigned int vblank :1;
65 } __attribute__ ((packed));
67 struct ppu_vram_addr_reg {
78 struct ppu_scroll_reg {
83 /*ppu core register instances*/
85 static struct ppu_ctrl_reg1 ctrl_reg1;
86 static struct ppu_ctrl_reg2 ctrl_reg2;
87 static struct ppu_status_reg status_reg;
88 static struct ppu_vram_addr_reg vram_addr_reg;
89 static struct ppu_scroll_reg scroll_reg;
90 static struct ppu_sprite_reg sprite_reg;
92 static unsigned char sprite_ram_addr_reg;
93 static unsigned char sprite_ram_dma_reg;
94 static unsigned char vram_data_reg;
95 static unsigned char vram_dma_reg;
97 //value set by the ctrl_reg1.
98 static unsigned char sprite_size_type;
99 static unsigned char vram_addr_inc;
100 static unsigned int vram_read_cnt;
102 #define SPR_STYPE_8x8 0
103 #define SPR_STYPE_8x16 1
105 #define SCAN_LINE (V_SCREEN_TILE_SIZE * TILE_DOT_SIZE)
111 static pthread_t ppucore_thread_id;
112 static int ppucore_end_loop;
116 * periodically update the display buffer.
118 static void *ppucore_loop(void* arg) {
119 //struct timespec ts = {CPU_CLOCK_SEC, CPU_CLOCK_NSEC / 10};
120 struct timespec begin, end;
124 #define NANOMAX (1000000000 - 1)
126 while (!ppucore_end_loop) {
129 status_reg.vblank = 0;
130 status_reg.vram_ignore = 1;
131 status_reg.sprite0_hit = 0;
133 clock_gettime(CLOCK_REALTIME, &begin);
134 for (scanline = 0; scanline < SCAN_LINE; scanline++) {
135 if (ctrl_reg2.show_sprite) {
137 load_sprite_old(FALSE, scanline);
139 if (ctrl_reg2.show_bg/**/) {
140 //back ground image is pre-loaded. load 1 line ahead of drawline.
142 load_background_old(scanline);
143 if (scanline < SCAN_LINE - 1)
144 load_background(scanline + 8);
146 if (ctrl_reg2.show_sprite) {
148 load_sprite_old(TRUE, scanline);
153 //printing display done.
154 status_reg.vblank = 1;
155 status_reg.vram_ignore = 0;
156 if (ctrl_reg1.nmi_vblank) {
157 //generate nmi interrupt to the cpu.
161 clock_gettime(CLOCK_REALTIME, &end);
163 //sleep rest of time...
164 if (end.tv_sec < begin.tv_sec )
165 sec = LONG_MAX - begin.tv_sec + end.tv_sec + 1;
167 sec = end.tv_sec - begin.tv_sec;
169 if (end.tv_nsec < begin.tv_nsec)
170 nsec = NANOMAX - begin.tv_nsec + end.tv_nsec + 1;
172 nsec = end.tv_nsec - begin.tv_nsec;
174 if (sec < NES_VIDEO_CLK_SEC || nsec < NES_VIDEO_CLK_NSEC) {
176 slp.tv_sec = sec > NES_VIDEO_CLK_SEC ? 0 : NES_VIDEO_CLK_SEC - sec;
177 slp.tv_nsec = nsec > NES_VIDEO_CLK_NSEC ? 0 : NES_VIDEO_CLK_NSEC - nsec;
179 //dprint("%d.%09d sec sleep\n", slp.tv_sec, slp.tv_nsec);
180 ret = nanosleep(&slp, NULL);
189 static int clock_ppu(void) {
190 // dprint("ppu x:%d, y:%d...\n", scan_x, scan_y);
192 if (scan_x < VSCREEN_WIDTH && scan_y < VSCREEN_HEIGHT) {
193 int scanline = scan_y;
194 if (scan_x == 0 && scan_y == 0) {
196 status_reg.vblank = 0;
197 status_reg.vram_ignore = 1;
201 status_reg.sprite0_hit = 0;
203 if (ctrl_reg2.show_sprite) {
205 load_sprite_old(FALSE, scanline);
207 if (ctrl_reg2.show_bg/**/) {
208 //back ground image is pre-loaded. load 1 line ahead of drawline.
210 load_background_old(scanline);
211 if (scanline < SCAN_LINE - 1)
212 load_background_old(scanline + 8);
214 if (ctrl_reg2.show_sprite) {
216 load_sprite_old(TRUE, scanline);
218 vga_xfer_old(scanline);
222 if (scan_x == 0 && scan_y == VSCREEN_HEIGHT) {
223 //printing display done.
224 status_reg.vblank = 1;
225 status_reg.vram_ignore = 0;
226 if (ctrl_reg1.nmi_vblank) {
227 //generate nmi interrupt to the cpu.
235 if (scan_x == HSCAN_MAX) {
239 if (scan_y == VSCAN_MAX) {
245 void ppu_ctrl1_set(unsigned char data) {
246 unsigned char old, diff_tmp;
247 struct ppu_ctrl_reg1 diff;
249 memcpy(&old, &ctrl_reg1, sizeof(ctrl_reg1));
250 memcpy(&ctrl_reg1, &data, sizeof(ctrl_reg1));
252 diff_tmp = old ^ data;
253 memcpy(&diff, &diff_tmp, sizeof(ctrl_reg1));
256 //if (diff.sprite_size)
257 // sprite_size = (ctrl_reg1.sprite_size == 0 ? 8 : 16);
259 //set bg base tbl addr
260 if (diff.bg_ptn_addr_sel)
261 set_bg_pattern_bank(ctrl_reg1.bg_ptn_addr_sel);
262 //set sprite base tbl addr
263 if (diff.sprite_ptn_sel)
264 set_spr_pattern_bank(ctrl_reg1.sprite_ptn_sel);
265 //set vram address increase unit
266 if (diff.addr_inc_size)
267 vram_addr_inc = (ctrl_reg1.addr_inc_size == 0 ? 1 : 32);
268 //set main screen addr
269 if (diff.name_tbl_sel)
270 set_bg_name_tbl_base(ctrl_reg1.name_tbl_sel);
272 //dprint("ctrl1: %x\n", data);
276 void ppu_ctrl2_set(unsigned char data) {
277 struct ppu_ctrl_reg2 old = ctrl_reg2;
279 memcpy(&ctrl_reg2, &data, sizeof(ctrl_reg2));
280 //dprint("ppu_ctrl2_set %d, show_bg:%d\n", data, ctrl_reg2.show_bg);
282 if (old.color_mode != ctrl_reg2.color_mode)
283 set_monocolor(ctrl_reg2.color_mode);
285 //dprint("ctrl2 %x:\n", data);
289 unsigned char ppu_status_get(void) {
291 memcpy(&ret, &status_reg, sizeof(status_reg));
293 //if read status reg, vram addr register counter is reset
294 vram_addr_reg.cnt = 0;
295 //dprint("ppu_status:%02x\n", ret);
299 void sprite_addr_set(unsigned char addr) {
300 //dprint("sprite_addr_set addr=%02x\n", addr);
301 sprite_ram_addr_reg = addr;
305 void sprite_data_set(unsigned char data) {
306 //dprint("sprite_data_set addr=%02x, data=%02x, cnt:%d\n",
307 // sprite_ram_addr_reg, data, sprite_reg.cnt);
308 switch (sprite_reg.cnt) {
314 sprite_reg.index = data;
317 memcpy(&sprite_reg.sa, &data, sizeof(struct sprite_attr));
323 spr_ram_tbl_set(sprite_ram_addr_reg + sprite_reg.cnt, data);
327 void ppu_scroll_set(unsigned char data) {
328 //dprint("scroll set %s = %02x\n", (scroll_reg.cnt == 0 ? "x" : "y"), data);
329 if (scroll_reg.cnt++ == 0)
335 void ppu_vram_addr_set(unsigned char half_addr) {
336 //dprint("vram addr:%04x\n", half_addr);
337 if (vram_addr_reg.cnt++ == 0)
338 vram_addr_reg.addr.b.hi = half_addr;
340 vram_addr_reg.addr.b.low = half_addr;
342 //when setting the addr, read cnt is reset.
346 void ppu_vram_data_set(unsigned char data) {
347 //check vram_ignore bit on write.
350 if (status_reg.vram_ignore)
353 //dprint("vram data:%04x\n", data);
354 vram_data_reg = data;
356 vram_data_set(vram_addr_reg.addr.s, data);
357 //vram addr increment.
358 vram_addr_reg.addr.s += vram_addr_inc;
361 unsigned char ppu_vram_data_get(void) {
362 if (vram_read_cnt++ == 0) {
363 //first read always fail.
367 vram_data_reg = vram_data_get(vram_addr_reg.addr.s);
368 //vram addr increment.
369 vram_addr_reg.addr.s += vram_addr_inc;
370 return vram_data_reg;
373 void sprite0_hit_set(void) {
374 //dprint("sprite0 set...\n");
375 status_reg.sprite0_hit = 1;
378 int ppucore_init(void) {
381 memset(&ctrl_reg1, 0, sizeof(ctrl_reg1));
382 memset(&ctrl_reg2, 0, sizeof(ctrl_reg1));
383 memset(&status_reg, 0, sizeof(status_reg));
384 memset(&vram_addr_reg, 0, sizeof(vram_addr_reg));
385 memset(&scroll_reg, 0, sizeof(scroll_reg));
386 memset(&sprite_reg, 0, sizeof(sprite_reg));
388 sprite_ram_addr_reg = 0;
389 sprite_ram_dma_reg = 0;
394 sprite_size_type = SPR_STYPE_8x8;
400 ret = vga_xfer_init();
408 ret = vscreen_init();
412 ret = palette_init();
422 ppucore_end_loop = FALSE;
423 ret = pthread_attr_init(&attr);
427 ppucore_thread_id = 0;
428 ret = pthread_create(&ppucore_thread_id, &attr, ppucore_loop, NULL);
433 ret = register_clock_hander(clock_ppu, PPU_DEVIDER);
443 void clean_ppucore(void) {
446 ppucore_end_loop = TRUE;
447 pthread_join(ppucore_thread_id, &ret);
448 dprint("ppucore thread joined.\n");
459 void dump_ppu_reg(void) {
460 printf("control reg1\n");
461 printf(" nmi_vblank:%d\n", ctrl_reg1.nmi_vblank);
462 printf(" sprite_size:%d\n", ctrl_reg1.sprite_size);
463 printf(" bg_ptn:%d\n", ctrl_reg1.bg_ptn_addr_sel);
464 printf(" spr_ptn:%d\n", ctrl_reg1.sprite_ptn_sel);
465 printf(" inc size:%d\n", ctrl_reg1.addr_inc_size);
466 printf(" name tbl:%d\n", ctrl_reg1.name_tbl_sel);
468 printf("\ncontrol reg2\n");
469 printf(" intense r:%d\n", ctrl_reg2.intense_r);
470 printf(" intense g:%d\n", ctrl_reg2.intense_g);
471 printf(" intense b:%d\n", ctrl_reg2.intense_b);
473 printf(" show spr:%d\n", ctrl_reg2.show_sprite);
474 printf(" show bg:%d\n", ctrl_reg2.show_bg);
475 printf(" left 8 pix spr:%d\n", ctrl_reg2.show_left_8sprite);
476 printf(" left 8 pix bg:%d\n", ctrl_reg2.show_left_8bg);
477 printf(" col mode:%d\n", ctrl_reg2.color_mode);