OSDN Git Service

load bg and stripe update, temporary commit...
[motonesemu/motonesemu.git] / emulator / ppucore / ppucore.c
1 #include <string.h>
2 #include <pthread.h>
3 #include <limits.h>
4 #include <stdio.h>
5
6 #include "tools.h"
7 #include "vram.h"
8 #include "ppucore.h"
9 #include "vga_xfer.h"
10 #include "sprite.h"
11 #include "clock.h"
12
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);
22
23 int load_sprite(int foreground, int scanline);
24 int load_background(int scanline);
25 void vga_xfer(int scanline);
26
27 void dump_ppu_reg(void);
28
29 /*
30  * 6502 little endian
31  * hi bit > low bit order
32  * but gcc generates low > hi order for bit field
33  * */
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));
43
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));
54
55 /*
56  * 6502 little endian
57  * but gcc generates low > hi order when bit field is used??
58  * */
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));
66
67 struct ppu_vram_addr_reg {
68     union {
69         struct {
70             unsigned char low;
71             unsigned char hi;
72         } b;
73         unsigned short s;
74     } addr;
75     unsigned int cnt :1;
76 };
77
78 struct ppu_scroll_reg {
79     unsigned char x;
80     unsigned char y;
81     unsigned int cnt :1;
82 };
83 /*ppu core register instances*/
84
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;
91
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;
96
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;
101
102 #define SPR_STYPE_8x8   0 
103 #define SPR_STYPE_8x16  1
104
105 #define SCAN_LINE       (V_SCREEN_TILE_SIZE * TILE_DOT_SIZE)
106
107 #define OLD_PPU_LOOP
108 #undef OLD_PPU_LOOP
109
110 #ifdef OLD_PPU_LOOP
111 static pthread_t ppucore_thread_id;
112 static int ppucore_end_loop;
113
114 /*
115  * ppucore main loop.
116  * periodically update the display buffer.
117  * */
118 static void *ppucore_loop(void* arg) {
119     //struct timespec ts = {CPU_CLOCK_SEC, CPU_CLOCK_NSEC / 10};
120     struct timespec begin, end;
121     struct timespec slp;
122     long sec, nsec;
123     int scanline;
124 #define NANOMAX (1000000000 - 1)
125
126     while (!ppucore_end_loop) {
127
128         //start displaying
129         status_reg.vblank = 0;
130         status_reg.vram_ignore = 1;
131         status_reg.sprite0_hit = 0;
132
133         clock_gettime(CLOCK_REALTIME, &begin);
134         for (scanline = 0; scanline < SCAN_LINE; scanline++) {
135             if (ctrl_reg2.show_sprite) {
136                 //sprite in the back
137                 load_sprite_old(FALSE, scanline);
138             }
139             if (ctrl_reg2.show_bg/**/) {
140                 //back ground image is pre-loaded. load 1 line ahead of drawline.
141                 if (scanline == 0)
142                     load_background_old(scanline);
143                 if (scanline < SCAN_LINE - 1)
144                     load_background(scanline + 8);
145             }
146             if (ctrl_reg2.show_sprite) {
147                 //foreground sprite
148                 load_sprite_old(TRUE, scanline);
149             }
150             vga_xfer(scanline);
151         }
152
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.
158             set_nmi_pin(TRUE);
159         }
160
161         clock_gettime(CLOCK_REALTIME, &end);
162
163         //sleep rest of time...
164         if (end.tv_sec < begin.tv_sec )
165             sec = LONG_MAX - begin.tv_sec + end.tv_sec + 1;
166         else
167             sec = end.tv_sec - begin.tv_sec;
168
169         if (end.tv_nsec < begin.tv_nsec)
170             nsec = NANOMAX - begin.tv_nsec + end.tv_nsec + 1;
171         else
172             nsec = end.tv_nsec - begin.tv_nsec;
173
174         if (sec < NES_VIDEO_CLK_SEC || nsec < NES_VIDEO_CLK_NSEC) {
175             int ret;
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;
178
179             //dprint("%d.%09d sec sleep\n", slp.tv_sec, slp.tv_nsec);
180             ret = nanosleep(&slp, NULL);
181         }
182     }
183     return NULL;
184 }
185 #endif
186
187 static int scan_x;
188 static int scan_y;
189 static int clock_ppu(void) {
190 //    dprint("ppu x:%d, y:%d...\n", scan_x, scan_y);
191
192     if (scan_x < VSCREEN_WIDTH && scan_y < VSCREEN_HEIGHT) {
193         int scanline = scan_y;
194         if (scan_x == 0 && scan_y == 0) {
195             //start displaying
196             status_reg.vblank = 0;
197             status_reg.vram_ignore = 1;
198         }
199
200         if (scan_x == 0) {
201             status_reg.sprite0_hit = 0;
202
203             if (ctrl_reg2.show_sprite) {
204                 //sprite in the back
205                 load_sprite_old(FALSE, scanline);
206             }
207             if (ctrl_reg2.show_bg/**/) {
208                 //back ground image is pre-loaded. load 1 line ahead of drawline.
209                 if (scanline == 0)
210                     load_background_old(scanline);
211                 if (scanline < SCAN_LINE - 1)
212                     load_background_old(scanline + 8);
213             }
214             if (ctrl_reg2.show_sprite) {
215                 //foreground sprite
216                 load_sprite_old(TRUE, scanline);
217             }
218             vga_xfer_old(scanline);
219         }
220     }
221     else {
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.
228                 set_nmi_pin(TRUE);
229             }
230         }
231     }
232
233
234     scan_x++;
235     if (scan_x == HSCAN_MAX) {
236         scan_x = 0;
237         scan_y++;
238     }
239     if (scan_y == VSCAN_MAX) {
240         scan_y = 0;
241     }
242     return TRUE;
243 }
244
245 void ppu_ctrl1_set(unsigned char data) {
246     unsigned char old, diff_tmp; 
247     struct ppu_ctrl_reg1 diff; 
248
249     memcpy(&old, &ctrl_reg1, sizeof(ctrl_reg1));
250     memcpy(&ctrl_reg1, &data, sizeof(ctrl_reg1));
251
252     diff_tmp = old ^ data;
253     memcpy(&diff, &diff_tmp, sizeof(ctrl_reg1));
254
255     //set sprite_size
256     //if (diff.sprite_size)
257     //    sprite_size = (ctrl_reg1.sprite_size == 0 ? 8 : 16);
258
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);
271
272     //dprint("ctrl1: %x\n", data);
273     //dump_ppu_reg();
274 }
275
276 void ppu_ctrl2_set(unsigned char data) {
277     struct ppu_ctrl_reg2 old = ctrl_reg2;
278
279     memcpy(&ctrl_reg2, &data, sizeof(ctrl_reg2));
280     //dprint("ppu_ctrl2_set %d, show_bg:%d\n", data, ctrl_reg2.show_bg);
281
282     if (old.color_mode != ctrl_reg2.color_mode)
283         set_monocolor(ctrl_reg2.color_mode);
284
285     //dprint("ctrl2 %x:\n", data);
286     //dump_ppu_reg();
287 }
288
289 unsigned char ppu_status_get(void) {
290     unsigned char ret;
291     memcpy(&ret, &status_reg, sizeof(status_reg));
292
293     //if read status reg, vram addr register counter is reset
294     vram_addr_reg.cnt = 0;
295     //dprint("ppu_status:%02x\n", ret);
296     return ret;
297 }
298
299 void sprite_addr_set(unsigned char addr) {
300     //dprint("sprite_addr_set addr=%02x\n", addr);
301     sprite_ram_addr_reg = addr;
302     sprite_reg.cnt = 0;
303 }
304
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) {
309         case 0:
310         default:
311             sprite_reg.y = data;
312             break;
313         case 1:
314             sprite_reg.index = data;
315             break;
316         case 2:
317             memcpy(&sprite_reg.sa, &data, sizeof(struct sprite_attr));
318             break;
319         case 3:
320             sprite_reg.x = data;
321             break;
322     }
323     spr_ram_tbl_set(sprite_ram_addr_reg + sprite_reg.cnt, data);
324     sprite_reg.cnt++;
325 }
326
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)
330         scroll_reg.x = data;
331     else
332         scroll_reg.y = data;
333 }
334
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;
339     else
340         vram_addr_reg.addr.b.low = half_addr;
341
342     //when setting the addr, read cnt is reset.
343     vram_read_cnt = 0;
344 }
345
346 void ppu_vram_data_set(unsigned char data) {
347     //check vram_ignore bit on write.
348     /*
349     */
350     if (status_reg.vram_ignore)
351         return;
352
353     //dprint("vram data:%04x\n", data);
354     vram_data_reg = data;
355
356     vram_data_set(vram_addr_reg.addr.s, data);
357     //vram addr increment.
358     vram_addr_reg.addr.s += vram_addr_inc;
359 }
360
361 unsigned char ppu_vram_data_get(void) {
362     if (vram_read_cnt++ == 0) {
363         //first read always fail.
364         return 0;
365     }
366
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;
371 }
372
373 void sprite0_hit_set(void) {
374     //dprint("sprite0 set...\n");
375     status_reg.sprite0_hit = 1;
376 }
377
378 int ppucore_init(void) {
379     int ret;
380
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));
387
388     sprite_ram_addr_reg = 0;
389     sprite_ram_dma_reg = 0;
390     vram_data_reg = 0;
391     vram_dma_reg = 0;
392     vram_read_cnt = 0;
393
394     sprite_size_type = SPR_STYPE_8x8;
395     vram_addr_inc = 1;
396
397     scan_x = 0;
398     scan_y = 0;
399
400     ret = vga_xfer_init();
401     if (!ret)
402         return FALSE;
403
404     ret = sprite_init();
405     if (!ret)
406         return FALSE;
407
408     ret = vscreen_init();
409     if (!ret)
410         return FALSE;
411
412     ret = palette_init();
413     if (!ret)
414         return FALSE;
415
416     ret = vram_init();
417     if (!ret)
418         return FALSE;
419 #ifdef OLD_PPU_LOOP
420     pthread_attr_t attr;
421
422     ppucore_end_loop = FALSE;
423     ret = pthread_attr_init(&attr);
424     if (ret != RT_OK) {
425         return FALSE;
426     }
427     ppucore_thread_id = 0;
428     ret = pthread_create(&ppucore_thread_id, &attr, ppucore_loop, NULL);
429     if (ret != RT_OK) {
430         return FALSE;
431     }
432 #else
433     ret = register_clock_hander(clock_ppu, PPU_DEVIDER);
434     if (!ret) {
435         return FALSE;
436     }
437 #endif
438
439
440     return TRUE;
441 }
442
443 void clean_ppucore(void) {
444 #ifdef OLD_PPU_LOOP
445     void* ret;
446     ppucore_end_loop = TRUE;
447     pthread_join(ppucore_thread_id, &ret);
448     dprint("ppucore thread joined.\n");
449 #endif
450
451     clean_vram();
452     clean_sprite();
453     clean_vscreen();
454 }
455
456 /*
457  * for debug.c
458  * */
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);
467
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);
472
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);
478
479 }
480