OSDN Git Service

- ppu debug dump func updated
[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 init_vscreen(void);
14 void clean_vscreen(void);
15 int init_palette(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 void set_vscreen_pos(int x, int y);
24 int sprite_prefetch1(int srch_line);
25 int sprite_prefetch2(int srch_line);
26 int load_sprite(int background, int x, int y);
27 int load_background(int x, int y);
28 void vga_xfer(int x, int y);
29 void vga_posinit(void);
30
31 void dump_ppu_reg(void);
32
33 /*
34  * 6502 little endian
35  * hi bit > low bit order
36  * but gcc generates low > hi order for bit field
37  * */
38 struct ppu_ctrl_reg1 {
39     unsigned int name_tbl_sel   :2;
40     unsigned int addr_inc_size  :1;
41     unsigned int sprite_ptn_sel :1;
42     unsigned int bg_ptn_addr_sel :1;
43     unsigned int sprite_size    :1;
44     unsigned int ppu_mode       :1;
45     unsigned int nmi_vblank     :1;
46 } __attribute__ ((packed));
47
48 struct ppu_ctrl_reg2 {
49     unsigned int color_mode     :1;
50     unsigned int show_left_8bg  :1;
51     unsigned int show_left_8sprite  :1;
52     unsigned int show_bg        :1;
53     unsigned int show_sprite    :1;
54     unsigned int intense_b      :1;
55     unsigned int intense_g      :1;
56     unsigned int intense_r      :1;
57 } __attribute__ ((packed));
58
59 /*
60  * 6502 little endian
61  * but gcc generates low > hi order when bit field is used??
62  * */
63 struct ppu_status_reg {
64     unsigned int nouse          :4;
65     unsigned int vram_ignore    :1;
66     unsigned int sprite_overflow    :1;
67     unsigned int sprite0_hit   :1;
68     unsigned int vblank         :1;
69 } __attribute__ ((packed));
70
71 struct ppu_vram_addr_reg {
72     union {
73         struct {
74             unsigned char low;
75             unsigned char hi;
76         } b;
77         unsigned short s;
78     } addr;
79     unsigned int cnt :1;
80 };
81
82 struct ppu_scroll_reg {
83     unsigned char x;
84     unsigned char y;
85     unsigned int cnt :1;
86 };
87 /*ppu core register instances*/
88
89 static struct ppu_ctrl_reg1 ctrl_reg1;
90 static struct ppu_ctrl_reg2 ctrl_reg2;
91 static struct ppu_status_reg status_reg;
92 static struct ppu_vram_addr_reg vram_addr_reg;
93 static struct ppu_scroll_reg scroll_reg;
94 static struct ppu_sprite_reg sprite_reg;
95
96 static unsigned char sprite_ram_addr_reg;
97 static unsigned char sprite_ram_dma_reg;
98 static unsigned char vram_data_reg;
99 static unsigned char vram_dma_reg;
100
101 //value set by the ctrl_reg1.
102 static unsigned char    sprite_size_type;
103 static unsigned char    vram_addr_inc;
104 static unsigned int     vram_read_cnt;
105
106 #define SPR_STYPE_8x8   0 
107 #define SPR_STYPE_8x16  1
108
109 #define SCAN_LINE       (V_SCREEN_TILE_SIZE * TILE_DOT_SIZE)
110
111 static int scan_x;
112 static int scan_y;
113
114 static int scan_recovery(void) {
115     if (scan_y == VSCREEN_HEIGHT)
116         return FALSE;
117
118     /*
119      * Sprite reg:
120      * Stores the y-coordinate of the top left of the sprite minus 1
121      * */
122     if (scan_x == HSCAN_MAX - 1 && scan_y < VSCREEN_HEIGHT) {
123         int next_line = (scan_y == VSCAN_MAX - 1 ? 0 : scan_y + 1);
124         sprite_prefetch1(next_line);
125         sprite_prefetch2(next_line);
126         return TRUE;
127     }
128     return FALSE;
129 }
130
131 static int clock_ppu(void) {
132 //    dprint("ppu x:%d, y:%d...\n", scan_x, scan_y);
133
134     if (scan_x < VSCREEN_WIDTH && scan_y < VSCREEN_HEIGHT) {
135         if (scan_x == 0) {
136             status_reg.sprite0_hit = 0;
137             status_reg.sprite_overflow = 0;
138             if (scan_y == 0) {
139                 //start displaying
140                 status_reg.vblank = 0;
141             }
142         }
143
144         set_vscreen_pos(scan_x, scan_y);
145         if (ctrl_reg2.show_sprite) {
146             //sprite in the back
147             load_sprite(TRUE, scan_x, scan_y);
148         }
149         if (ctrl_reg2.show_bg/**/) {
150             //back ground image is pre-loaded. load 1 line ahead of drawline.
151             load_background(scan_x + scroll_reg.x, scan_y + scroll_reg.y);
152         }
153         if (ctrl_reg2.show_sprite) {
154             //foreground sprite
155             load_sprite(FALSE, scan_x, scan_y);
156         }
157         vga_xfer(scan_x, scan_y);
158     }
159     else {
160         scan_recovery();
161         if (scan_x == 0 && scan_y == VSCREEN_HEIGHT) {
162             //printing display done.
163             status_reg.vblank = 1;
164             if (ctrl_reg1.nmi_vblank) {
165                 //generate nmi interrupt to the cpu.
166                 set_nmi_pin(TRUE);
167             }
168         }
169     }
170
171
172     scan_x++;
173     if (scan_x == HSCAN_MAX) {
174         scan_x = 0;
175         scan_y++;
176     }
177     if (scan_y == VSCAN_MAX) {
178         scan_y = 0;
179     }
180     return TRUE;
181 }
182
183 void ppu_ctrl1_set(unsigned char data) {
184     unsigned char old, diff_tmp; 
185     struct ppu_ctrl_reg1 diff; 
186
187     memcpy(&old, &ctrl_reg1, sizeof(ctrl_reg1));
188     memcpy(&ctrl_reg1, &data, sizeof(ctrl_reg1));
189
190     diff_tmp = old ^ data;
191     memcpy(&diff, &diff_tmp, sizeof(ctrl_reg1));
192
193     //set sprite_size
194     //if (diff.sprite_size)
195     //    sprite_size = (ctrl_reg1.sprite_size == 0 ? 8 : 16);
196
197     //set bg base tbl addr
198     if (diff.bg_ptn_addr_sel)
199         set_bg_pattern_bank(ctrl_reg1.bg_ptn_addr_sel);
200     //set sprite base tbl addr
201     if (diff.sprite_ptn_sel) 
202         set_spr_pattern_bank(ctrl_reg1.sprite_ptn_sel);
203     //set vram address increase unit
204     if (diff.addr_inc_size) 
205         vram_addr_inc = (ctrl_reg1.addr_inc_size == 0 ? 1 : 32);
206     //set main screen addr
207     if (diff.name_tbl_sel)
208         set_bg_name_tbl_base(ctrl_reg1.name_tbl_sel);
209
210     //dprint("ctrl1: %x\n", data);
211     //dump_ppu_reg();
212 }
213
214 void ppu_ctrl2_set(unsigned char data) {
215     struct ppu_ctrl_reg2 old = ctrl_reg2;
216
217     memcpy(&ctrl_reg2, &data, sizeof(ctrl_reg2));
218     //dprint("ppu_ctrl2_set %d, show_bg:%d\n", data, ctrl_reg2.show_bg);
219
220     if (old.color_mode != ctrl_reg2.color_mode)
221         set_monocolor(ctrl_reg2.color_mode);
222
223     //dprint("ctrl2 %x:\n", data);
224     //dump_ppu_reg();
225 }
226
227 unsigned char ppu_status_get(void) {
228     unsigned char ret;
229     memcpy(&ret, &status_reg, sizeof(status_reg));
230
231     //if read status reg, vram addr register counter is reset
232     vram_addr_reg.cnt = 0;
233     //dprint("ppu_status:%02x\n", ret);
234
235     //reading status resets the vblank flg.
236     status_reg.vblank = 0;
237     return ret;
238 }
239
240 void sprite_addr_set(unsigned char addr) {
241     //dprint("sprite_addr_set addr=%02x\n", addr);
242     sprite_ram_addr_reg = addr;
243     sprite_reg.cnt = 0;
244 }
245
246 void sprite_data_set(unsigned char data) {
247     //dprint("sprite_data_set addr=%02x, data=%02x, cnt:%d\n", 
248      //       sprite_ram_addr_reg, data, sprite_reg.cnt);
249     switch (sprite_reg.cnt) {
250         case 0:
251         default:
252             sprite_reg.y = data;
253             break;
254         case 1:
255             sprite_reg.index = data;
256             break;
257         case 2:
258             memcpy(&sprite_reg.sa, &data, sizeof(struct sprite_attr));
259             break;
260         case 3:
261             sprite_reg.x = data;
262             break;
263     }
264     spr_ram_tbl_set(sprite_ram_addr_reg + sprite_reg.cnt, data);
265     sprite_reg.cnt++;
266 }
267
268 void ppu_scroll_set(unsigned char data) {
269     //dprint("scroll set %s = %02x\n", (scroll_reg.cnt == 0 ? "x" : "y"), data);
270     if (scroll_reg.cnt++ == 0)
271         scroll_reg.x = data;
272     else
273         scroll_reg.y = data;
274 }
275
276 void ppu_vram_addr_set(unsigned char half_addr) {
277     //dprint("vram addr:%04x\n", half_addr);
278     if (vram_addr_reg.cnt++ == 0)
279         vram_addr_reg.addr.b.hi = half_addr;
280     else
281         vram_addr_reg.addr.b.low = half_addr;
282
283     //when setting the addr, read cnt is reset.
284     vram_read_cnt = 0;
285 }
286
287 void ppu_vram_data_set(unsigned char data) {
288     //check vram_ignore bit on write.
289     /*
290      * TODO ignore bit set timing is unknown.
291     */
292     if (status_reg.vram_ignore)
293         return;
294
295     //dprint("vram data:%04x\n", data);
296     vram_data_reg = data;
297
298     vram_data_set(vram_addr_reg.addr.s, data);
299     //vram addr increment.
300     vram_addr_reg.addr.s += vram_addr_inc;
301 }
302
303 unsigned char ppu_vram_data_get(void) {
304     if (vram_read_cnt++ == 0) {
305         //first read always fail.
306         return 0;
307     }
308
309     vram_data_reg = vram_data_get(vram_addr_reg.addr.s);
310     //vram addr increment.
311     vram_addr_reg.addr.s += vram_addr_inc;
312     return vram_data_reg;
313 }
314
315 void sprite0_hit_set(void) {
316     //dprint("sprite0 set...\n");
317     status_reg.sprite0_hit = 1;
318 }
319
320 void sprite_overflow_set(void) {
321     //dprint("sprite0 set...\n");
322     status_reg.sprite_overflow = 1;
323 }
324
325 int init_ppucore(void) {
326     int ret;
327
328     memset(&ctrl_reg1, 0, sizeof(ctrl_reg1));
329     memset(&ctrl_reg2, 0,  sizeof(ctrl_reg1));
330     memset(&status_reg, 0, sizeof(status_reg));
331     memset(&vram_addr_reg, 0, sizeof(vram_addr_reg));
332     memset(&scroll_reg, 0, sizeof(scroll_reg));
333     memset(&sprite_reg, 0, sizeof(sprite_reg));
334
335     sprite_ram_addr_reg = 0;
336     sprite_ram_dma_reg = 0;
337     vram_data_reg = 0;
338     vram_dma_reg = 0;
339     vram_read_cnt = 0;
340
341     sprite_size_type = SPR_STYPE_8x8;
342     vram_addr_inc = 1;
343
344     scan_x = 0;
345     scan_y = 0;
346
347     ret = init_vga_xfer();
348     if (!ret)
349         return FALSE;
350
351     ret = init_sprite();
352     if (!ret)
353         return FALSE;
354
355     ret = init_vscreen();
356     if (!ret)
357         return FALSE;
358
359     ret = init_palette();
360     if (!ret)
361         return FALSE;
362
363     ret = init_vram();
364     if (!ret)
365         return FALSE;
366     ret = register_clock_hander(clock_ppu, PPU_DEVIDER);
367     if (!ret) {
368         return FALSE;
369     }
370
371     //vga_posinit();
372
373     return TRUE;
374 }
375
376 void clean_ppucore(void) {
377     clean_vram();
378     clean_sprite();
379     clean_vscreen();
380 }
381
382 /*
383  * for debug.c
384  * */
385 void dump_ppu_reg(void) {
386     printf("control reg1\n");
387     printf(" nmi_vblank:%d\n", ctrl_reg1.nmi_vblank);
388     printf(" sprite_size:%d\n", ctrl_reg1.sprite_size);
389     printf(" bg_ptn:%d\n", ctrl_reg1.bg_ptn_addr_sel);
390     printf(" spr_ptn:%d\n", ctrl_reg1.sprite_ptn_sel);
391     printf(" inc size:%d\n", ctrl_reg1.addr_inc_size);
392     printf(" name tbl:%d\n", ctrl_reg1.name_tbl_sel);
393
394     printf("\ncontrol reg2\n");
395     printf(" intense r:%d\n", ctrl_reg2.intense_r);
396     printf(" intense g:%d\n", ctrl_reg2.intense_g);
397     printf(" intense b:%d\n", ctrl_reg2.intense_b);
398
399     printf(" show spr:%d\n", ctrl_reg2.show_sprite);
400     printf(" show bg:%d\n", ctrl_reg2.show_bg);
401     printf(" left 8 pix spr:%d\n", ctrl_reg2.show_left_8sprite);
402     printf(" left 8 pix bg:%d\n", ctrl_reg2.show_left_8bg);
403     printf(" col mode:%d\n", ctrl_reg2.color_mode);
404
405     printf("\nstatus reg\n");
406     printf(" vram_ignore:%d\n", status_reg.vram_ignore);
407     printf(" sprite_overflow:%d\n", status_reg.sprite_overflow);
408     printf(" sprite0_hit:%d\n", status_reg.sprite0_hit);
409     printf(" vblank:%d\n", status_reg.vblank);
410
411     printf("\nvram_addr_reg:%04x\n", vram_addr_reg.addr.s);
412     printf("vram_data_reg:%02x\n", vram_data_reg);
413     printf("\nscroll_reg:\n");
414     printf(" x:%d\n", scroll_reg.x);
415     printf(" y:%d\n", scroll_reg.y);
416 }
417