OSDN Git Service

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