1 /***************************************************************************
2 
3   snes.c
4 
5   Machine file to handle emulation of the Nintendo Super NES
6 
7   Anthony Kruize
8   Based on the original code by Lee Hammerton (aka Savoury Snax)
9 
10 ***************************************************************************/
11 #define __MACHINE_SNES_C
12 
13 #include "driver.h"
14 #include "includes/snes.h"
15 #include "cpu/g65816/g65816.h"
16 
17 /* -- Globals -- */
18 UINT8  *snes_ram = NULL;		/* 65816 ram */
19 UINT8  *spc_ram = NULL;			/* spc700 ram */
20 UINT8  *snes_vram = NULL;		/* Video RAM (Should be 16-bit, but it's easier this way) */
21 UINT16 *snes_cgram = NULL;		/* Colour RAM */
22 UINT16 *snes_oam = NULL;		/* Object Attribute Memory */
23 static UINT16 cgram_address;	/* CGRAM address */
24 static UINT8  vram_read_offset;	/* VRAM read offset */
25 static UINT16 vram_fg_count;	/* Fullgraphic increase count */
26 static UINT16 vram_fg_incr;		/* Fullgraphic increase */
27 static UINT16 vram_fg_cntr;		/* Fullgraphic counter */
28 static INT16 vram_fg_offset;	/* Fullgraphic offset */
29 UINT8  spc_port_in[4];	/* Port for sending data to the SPC700 */
30 UINT8  spc_port_out[4];	/* Port for receiving data from the SPC700 */
31 static UINT8 snes_hdma_chnl;	/* channels enabled for HDMA */
32 static struct
33 {
34 	UINT8  mode;		/* ROM memory mode */
35 	UINT32 sram;		/* Amount of sram in cart */
36 	UINT32 sram_max;	/* Maximum amount sram in cart (based on ROM mode) */
37 } cart = { SNES_MODE_20, 0x40000, 0x40000 };
38 static struct
39 {
40 	UINT8 low;
41 	UINT8 high;
42 	UINT32 value;
43 	UINT8 oldrol;
44 } joypad[4];
45 
snes_init_ram(void)46 static void snes_init_ram(void)
47 {
48 	/* Init VRAM */
49 	snes_vram = (UINT8 *)memory_region( REGION_GFX1 );
50 	memset( snes_vram, 0, SNES_VRAM_SIZE );
51 
52 	/* Init Colour RAM */
53 	snes_cgram = (UINT16 *)memory_region( REGION_USER1 );
54 	memset( (UINT8 *)snes_cgram, 0, SNES_CGRAM_SIZE );
55 
56 	/* Init oam RAM */
57 	snes_oam = (UINT16 *)memory_region( REGION_USER2 );
58 	memset( snes_oam, 0xff, SNES_OAM_SIZE );
59 
60 	/* Inititialize registers/variables */
61 	snes_ppu.update_windows = 1;
62 	snes_ppu.update_palette = 1;
63 	snes_ppu.beam.latch_vert = 0;
64 	snes_ppu.beam.latch_horz = 0;
65 	snes_ppu.beam.current_vert = 0;
66 	snes_ppu.beam.current_horz = 0;
67 	snes_ppu.beam.last_visible_line = 240;
68 	snes_ppu.mode = 0;
69 	cgram_address = 0;
70 	vram_read_offset = 2;
71 	/* Force the use of the SPCSkipper for now.
72 	 * Once the two CPU's are running in sync. we should check that sound is
73 	 * enabled here and only use the SPCSkipper if it is. */
74 	spc_usefakeapu = 1;
75 }
76 
77 
MACHINE_INIT(snes)78 MACHINE_INIT( snes )
79 {
80 	snes_init_ram();
81 
82 	/* Set STAT78 to NTSC or PAL */
83 	if( Machine->drv->frames_per_second == 60 )
84 		snes_ram[STAT78] = SNES_NTSC;
85 	else /* if( Machine->drv->frames_per_second == 50 ) */
86 		snes_ram[STAT78] = SNES_PAL;
87 }
88 
89 
90 /* Handle reading of Mode 20 SRAM */
91 /* 0x700000 - 0x77ffff */
READ_HANDLER(snes_r_sram)92 READ_HANDLER( snes_r_sram )
93 {
94 	UINT8 value = 0xff;
95 
96 	if( cart.sram > 0 )
97 	{
98 		value = snes_ram[0x700000 + offset];
99 	}
100 
101 	return value;
102 }
103 
104 /* 0x000000 - 0x2fffff */
READ_HANDLER(snes_r_bank1)105 READ_HANDLER( snes_r_bank1 )
106 {
107 	UINT16 address = offset & 0xffff;
108 
109 	if( address <= 0x1fff )								/* Mirror of Low RAM */
110 		return cpu_readmem24( 0x7e0000 + address );
111 	else if( address >= 0x2000 && address <= 0x5fff )	/* I/O */
112 		return snes_r_io( address );
113 	else if( address >= 0x6000 && address <= 0x7fff )	/* Reserved */
114 		return 0xff;
115 	else
116 	{
117 		if( cart.mode == SNES_MODE_20 )
118 			return snes_ram[offset];
119 		else	/* MODE_21 */
120 			return snes_ram[0xc00000 + offset];
121 	}
122 
123 	return 0xff;
124 }
125 
126 /* 0x300000 - 0x3fffff */
READ_HANDLER(snes_r_bank2)127 READ_HANDLER( snes_r_bank2 )
128 {
129 	UINT16 address = offset & 0xffff;
130 
131 	if( address <= 0x1fff )								/* Mirror of Low RAM */
132 		return cpu_readmem24( 0x7e0000 + address );
133 	else if( address >= 0x2000 && address <= 0x5fff )	/* I/O */
134 		return snes_r_io( address );
135 	else if( address >= 0x6000 && address <= 0x7fff )
136 	{
137 		if( cart.mode == SNES_MODE_20 )
138 			return 0xff;						/* Reserved */
139 		else	/* MODE_21 */
140 			return snes_ram[0x300000 + offset];	/* sram */
141 	}
142 	else
143 	{
144 		if( cart.mode == SNES_MODE_20 )
145 			return snes_ram[0x300000 + offset];
146 		else	/* MODE_21 */
147 			return snes_ram[0xf00000 + offset];
148 	}
149 
150 	return 0xff;
151 }
152 
153 /* 0x400000 - 0x5fffff */
READ_HANDLER(snes_r_bank3)154 READ_HANDLER( snes_r_bank3 )
155 {
156 	UINT16 address = offset & 0xffff;
157 
158 	if( cart.mode == SNES_MODE_20 )
159 	{
160 		if( address <= 0x7fff )
161 			return 0xff;		/* Reserved */
162 		else
163 			return snes_ram[0x400000 + offset];
164 	}
165 	else	/* MODE_21 */
166 	{
167 		return snes_ram[0x400000 + offset];
168 	}
169 
170 	return 0xff;
171 }
172 
173 /* 0x800000 - 0xffffff */
READ_HANDLER(snes_r_bank4)174 READ_HANDLER( snes_r_bank4 )
175 {
176 	if( cart.mode == SNES_MODE_20 )
177 	{
178 		if( offset <= 0x5fffff )
179 			return cpu_readmem24( offset );
180 		else
181 			return 0xff;
182 	}
183 	else	/* MODE_21 */
184 	{
185 		if( offset <= 0x3fffff )
186 			return cpu_readmem24( offset );
187 		else
188 			return snes_ram[offset + 0x800000];
189 	}
190 
191 	return 0xff;
192 }
193 
194 /* 0x000000 - 0x2fffff */
WRITE_HANDLER(snes_w_bank1)195 WRITE_HANDLER( snes_w_bank1 )
196 {
197 	UINT16 address = offset & 0xffff;
198 
199 	if( address <= 0x1fff )								/* Mirror of Low RAM */
200 		cpu_writemem24( 0x7e0000 + address, data );
201 	else if( address >= 0x2000 && address <= 0x5fff )	/* I/O */
202 		snes_w_io( address, data );
203 	else if( address >= 0x6000 && address <= 0x7fff )	/* Reserved */
204 		log_cb(RETRO_LOG_DEBUG, LOGPRE  "Attempt to write to reserved address: %X\n", offset );
205 	else
206 		log_cb(RETRO_LOG_DEBUG, LOGPRE  "Attempt to write to ROM address: %X\n", offset );
207 }
208 
209 /* 0x300000 - 0x3fffff */
WRITE_HANDLER(snes_w_bank2)210 WRITE_HANDLER( snes_w_bank2 )
211 {
212 	UINT16 address = offset & 0xffff;
213 
214 	if( address <= 0x1fff )								/* Mirror of Low RAM */
215 		cpu_writemem24( 0x7e0000 + address, data );
216 	else if( address >= 0x2000 && address <= 0x5fff )	/* I/O */
217 		snes_w_io( address, data );
218 	else if( address >= 0x6000 && address <= 0x7fff )
219 	{
220 		if( cart.mode == SNES_MODE_20 )			/* Reserved */
221 			log_cb(RETRO_LOG_DEBUG, LOGPRE  "Attempt to write to reserved address: %X\n", offset );
222 		else /* MODE_21 */
223 			snes_ram[0x300000 + offset] = data;  /* sram */
224 	}
225 	else
226 		log_cb(RETRO_LOG_DEBUG, LOGPRE  "Attempt to write to ROM address: %X\n", offset );
227 }
228 
229 /* 0x800000 - 0xffffff */
WRITE_HANDLER(snes_w_bank4)230 WRITE_HANDLER( snes_w_bank4 )
231 {
232 	if( cart.mode == SNES_MODE_20 )
233 	{
234 		if( offset <= 0x2fffff )
235 			snes_w_bank1( offset, data );
236 		else if( offset >= 0x300000 && offset <= 0x3fffff )
237 			snes_w_bank2( offset - 0x300000, data );
238 	}
239 	else /* MODE_21 */
240 	{
241 		if( offset <= 0x2fffff )
242 			snes_w_bank1( offset, data );
243 		else if( offset >= 0x300000 && offset <= 0x3fffff )
244 			snes_w_bank2( offset - 0x300000, data );
245 		else
246 			log_cb(RETRO_LOG_DEBUG, LOGPRE  "Attempt to write to ROM address: %X\n", offset );
247 	}
248 }
249 
250 /*
251  * DR   - Double read : address is read twice to return a 16bit value.
252  * low  - This is the low byte of a 16 or 24 bit value
253  * mid  - This is the middle byte of a 24 bit value
254  * high - This is the high byte of a 16 or 24 bit value
255  */
READ_HANDLER(snes_r_io)256 READ_HANDLER( snes_r_io )
257 {
258 	UINT8 value = 0;
259 
260 	/* offset is from 0x000000 */
261 	switch( offset )
262 	{
263 		case OAMADDL:
264 		case OAMADDH:
265 		case VMADDL:
266 		case VMADDH:
267 		case VMDATAL:
268 		case VMDATAH:
269 		case CGADD:
270 		case CGDATA:
271 			return snes_ram[offset];
272 		case MPYL:		/* Multiplication result (low) */
273 		case MPYM:		/* Multiplication result (mid) */
274 		case MPYH:		/* Multiplication result (high) */
275 			{
276 				/* Perform 16bit * 8bit multiply */
277 				INT32 c = snes_ppu.mode7.matrix_a * (snes_ppu.mode7.matrix_b >> 8);
278 				snes_ram[MPYL] = c & 0xff;
279 				snes_ram[MPYM] = (c >> 8) & 0xff;
280 				snes_ram[MPYH] = (c >> 16) & 0xff;
281 				return snes_ram[offset];
282 			}
283 		case SLHV:		/* Software latch for H/V counter */
284 			/* FIXME: horizontal latch is a major fudge!!! */
285 			snes_ppu.beam.latch_vert = snes_ppu.beam.current_vert;
286 			snes_ppu.beam.latch_horz = snes_ppu.beam.current_horz;
287 			snes_ppu.beam.current_horz = 0;
288 			return 0x0;		/* Return value is meaningless */
289 		case ROAMDATA:	/* Read data from OAM (DR) */
290 			{
291 				value = (snes_oam[snes_ppu.oam.address] >> (snes_ram[OAMDATA] << 3)) & 0xff;
292 				snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
293 				if( snes_ram[OAMDATA] == 0 )
294 				{
295 					snes_ppu.oam.address++;
296 					snes_ram[OAMADDL] = snes_ppu.oam.address & 0xff;
297 					snes_ram[OAMADDH] = (snes_ppu.oam.address >> 8) & 0x1;
298 				}
299 				return value;
300 			}
301 		case RVMDATAL:	/* Read data from VRAM (low) */
302 			{
303 				UINT16 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
304 				value = snes_vram[(addr << 1) - vram_read_offset];
305 				if( !(snes_ram[VMAIN] & 0x80) )
306 				{
307 					if( vram_read_offset == 0 )
308 					{
309 						vram_read_offset = 2;
310 					}
311 					/* Increase the address */
312 					if( snes_ram[VMAIN] & 0xc )
313 					{
314 						addr++;
315 						vram_fg_offset += 7;	/* addr increases by 1, plus 7 = 8 */
316 						vram_fg_count--;
317 						if( vram_fg_count == 0 )
318 						{
319 							vram_fg_cntr--;
320 							vram_fg_count = vram_fg_incr;
321 							if( vram_fg_cntr == 0 )
322 							{
323 								vram_fg_cntr = 8;
324 								vram_fg_offset -= 7;
325 							}
326 							else
327 							{
328 								vram_fg_offset -= (vram_fg_count * 8) - 1;
329 							}
330 						}
331 					}
332 					else
333 					{
334 						switch( snes_ram[VMAIN] & 0x03 )
335 						{
336 							case 0: addr++;      break;
337 							case 1: addr += 32;  break;
338 							case 2: addr += 128; break; /* Should be 64, but a bug in the snes means it's 128 */
339 							case 3: addr += 128; break;
340 						}
341 					}
342 					snes_ram[VMADDL] = addr & 0xff;
343 					snes_ram[VMADDH] = (addr >> 8) & 0xff;
344 				}
345 				return value;
346 			}
347 		case RVMDATAH:	/* Read data from VRAM (high) */
348 			{
349 				UINT16 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
350 
351 				value = snes_vram[(addr << 1) + 1 - vram_read_offset];
352 				if( snes_ram[VMAIN] & 0x80 )
353 				{
354 					if( vram_read_offset == 0 )
355 					{
356 						vram_read_offset = 2;
357 					}
358 					/* Increase the address */
359 					if( snes_ram[VMAIN] & 0xc )
360 					{
361 						addr++;
362 						vram_fg_offset += 7;	/* addr increases by 1, plus 7 = 8 */
363 						vram_fg_count--;
364 						if( vram_fg_count == 0 )
365 						{
366 							vram_fg_cntr--;
367 							vram_fg_count = vram_fg_incr;
368 							if( vram_fg_cntr == 0 )
369 							{
370 								vram_fg_cntr = 8;
371 								vram_fg_offset -= 7;
372 							}
373 							else
374 							{
375 								vram_fg_offset -= (vram_fg_count * 8) - 1;
376 							}
377 						}
378 					}
379 					else
380 					{
381 						switch( snes_ram[VMAIN] & 0x03 )
382 						{
383 							case 0: addr++;      break;
384 							case 1: addr += 32;  break;
385 							case 2: addr += 128; break; /* Should be 64, but a bug in the snes means it's 128 */
386 							case 3: addr += 128; break;
387 						}
388 					}
389 					snes_ram[VMADDL] = addr & 0xff;
390 					snes_ram[VMADDH] = (addr >> 8) & 0xff;
391 				}
392 				return value;
393 			}
394 		case RCGDATA:	/* Read data from CGRAM */
395 				value = ((UINT8 *)snes_cgram)[cgram_address];
396 				cgram_address = (cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
397 				return value;
398 		case OPHCT:		/* Horizontal counter data by ext/soft latch */
399 			{
400 				/* FIXME: need to handle STAT78 reset */
401 				static UINT8 read_ophct = 0;
402 				if( read_ophct )
403 				{
404 					value = (snes_ppu.beam.latch_horz >> 8) & 0x1;
405 					read_ophct = 0;
406 				}
407 				else
408 				{
409 					value = snes_ppu.beam.latch_horz & 0xff;
410 					read_ophct = 1;
411 				}
412 				return value;
413 			}
414 		case OPVCT:		/* Vertical counter data by ext/soft latch */
415 			{
416 				/* FIXME: need to handle STAT78 reset */
417 				static UINT8 read_opvct = 0;
418 				if( read_opvct )
419 				{
420 					value = (snes_ppu.beam.latch_vert >> 8) & 0x1;
421 					read_opvct = 0;
422 				}
423 				else
424 				{
425 					value = snes_ppu.beam.latch_vert & 0xff;
426 					read_opvct = 1;
427 				}
428 				return value;
429 			}
430 		case STAT77:	/* PPU status flag and version number */
431 			return snes_ram[offset];
432 		case STAT78:	/* PPU status flag and version number */
433 			/* FIXME: need to reset OPHCT and OPVCT */
434 			return snes_ram[offset];
435 		case APU00:		/* Audio port register */
436 		case APU01:		/* Audio port register */
437 		case APU02:		/* Audio port register */
438 		case APU03:		/* Audio port register */
439 			if( spc_usefakeapu )
440 				return fakespc_port_r( offset & 0x3 );
441 			else
442 			return spc_port_out[offset & 0x3];
443 		case WMDATA:	/* Data to read from WRAM */
444 			{
445 				UINT32 addr = ((snes_ram[WMADDH] & 0x1) << 16) | (snes_ram[WMADDM] << 8) | snes_ram[WMADDL];
446 
447 				value = cpu_readmem24(0x7e0000 + addr++);
448 				snes_ram[WMADDH] = (addr >> 16) & 0x1;
449 				snes_ram[WMADDM] = (addr >> 8) & 0xff;
450 				snes_ram[WMADDL] = addr & 0xff;
451 				return value;
452 			}
453 		case WMADDL:	/* Address to read/write to wram (low) */
454 		case WMADDM:	/* Address to read/write to wram (mid) */
455 		case WMADDH:	/* Address to read/write to wram (high) */
456 			return snes_ram[offset];
457 		case OLDJOY1:	/* Data for old NES controllers */
458 			{
459 				if( snes_ram[offset] & 0x1 )
460 				{
461 					return 0;
462 				}
463 				value = ((joypad[0].low | (joypad[0].high << 8) | 0x10000) >> (15 - (joypad[0].oldrol++ % 16))) & 0x1;
464 				if( !(joypad[0].oldrol % 17) )
465 					value = 0x1;
466 				return value;
467 			}
468 		case OLDJOY2:	/* Data for old NES controllers */
469 			{
470 				if( snes_ram[OLDJOY1] & 0x1 )
471 				{
472 					return 0;
473 				}
474 				value = ((joypad[1].low | (joypad[1].high << 8) | 0x10000) >> (15 - (joypad[1].oldrol++ % 16))) & 0x1;
475 				if( !(joypad[1].oldrol % 17) )
476 					value = 0x1;
477 				return value;
478 			}
479 		case HTIMEL:
480 		case HTIMEH:
481 		case VTIMEL:
482 		case VTIMEH:
483 			return snes_ram[offset];
484 		case MDMAEN:		/* GDMA channel designation and trigger */
485 			/* FIXME: Is this really read-only? - Villgust needs to read it */
486 			return snes_ram[offset];
487 		case RDNMI:			/* NMI flag by v-blank and version number */
488 			value = snes_ram[offset];
489 			snes_ram[offset] &= 0xf;	/* NMI flag is reset on read */
490 			return value;
491 		case TIMEUP:		/* IRQ flag by H/V count timer */
492 			value = snes_ram[offset];
493 			snes_ram[offset] = 0;	/* Register is reset on read */
494 			return value;
495 		case HVBJOY:		/* H/V blank and joypad controller enable */
496 			/* FIXME: JOYCONT and HBLANK are emulated wrong at present */
497 			value = snes_ram[offset] & 0xbe;
498 			snes_ram[offset] = ((snes_ram[offset]^0x41)&0x41)|value;
499 			return snes_ram[offset];
500 		case RDIO:			/* Programmable I/O port (in port ) */
501 			/* FIXME: do something here */
502 		case RDDIVL:		/* Quotient of divide result (low) */
503 		case RDDIVH:		/* Quotient of divide result (high) */
504 		case RDMPYL:		/* Product/Remainder of mult/div result (low) */
505 		case RDMPYH:		/* Product/Remainder of mult/div result (high) */
506 			return snes_ram[offset];
507 		case JOY1L:			/* Joypad 1 status register (low) */
508 			return joypad[0].low;
509 		case JOY1H:			/* Joypad 1 status register (high) */
510 			return joypad[0].high;
511 		case JOY2L:			/* Joypad 2 status register (low) */
512 			return joypad[1].low;
513 		case JOY2H:			/* Joypad 2 status register (high) */
514 			return joypad[1].high;
515 		case JOY3L:			/* Joypad 3 status register (low) */
516 			return joypad[2].low;
517 		case JOY3H:			/* Joypad 3 status register (high) */
518 			return joypad[2].high;
519 		case JOY4L:			/* Joypad 4 status register (low) */
520 			return joypad[3].low;
521 		case JOY4H:			/* Joypad 4 status register (high) */
522 			return joypad[3].high;
523 		case DMAP0: case BBAD0: case A1T0L: case A1T0H: case A1B0: case DAS0L:
524 		case DAS0H: case DSAB0: case A2A0L: case A2A0H: case NTRL0:
525 		case DMAP1: case BBAD1: case A1T1L: case A1T1H: case A1B1: case DAS1L:
526 		case DAS1H: case DSAB1: case A2A1L: case A2A1H: case NTRL1:
527 		case DMAP2: case BBAD2: case A1T2L: case A1T2H: case A1B2: case DAS2L:
528 		case DAS2H: case DSAB2: case A2A2L: case A2A2H: case NTRL2:
529 		case DMAP3: case BBAD3: case A1T3L: case A1T3H: case A1B3: case DAS3L:
530 		case DAS3H: case DSAB3: case A2A3L: case A2A3H: case NTRL3:
531 		case DMAP4: case BBAD4: case A1T4L: case A1T4H: case A1B4: case DAS4L:
532 		case DAS4H: case DSAB4: case A2A4L: case A2A4H: case NTRL4:
533 		case DMAP5: case BBAD5: case A1T5L: case A1T5H: case A1B5: case DAS5L:
534 		case DAS5H: case DSAB5: case A2A5L: case A2A5H: case NTRL5:
535 		case DMAP6: case BBAD6: case A1T6L: case A1T6H: case A1B6: case DAS6L:
536 		case DAS6H: case DSAB6: case A2A6L: case A2A6H: case NTRL6:
537 		case DMAP7: case BBAD7: case A1T7L: case A1T7H: case A1B7: case DAS7L:
538 		case DAS7H: case DSAB7: case A2A7L: case A2A7H: case NTRL7:
539 			return snes_ram[offset];
540 
541 		case 0x4100:		/* NSS Dip-Switches */
542 #ifdef MAME_DEBUG
543 			return readinputport(12);
544 #else
545 			return readinputport(9);
546 #endif	/* MAME_DEBUG */
547 /*		case 0x4101: // PC: a104 - a10e - a12a	*/ /*only nss_actr*/
548 /*		case 0x420c: // PC: 9c7d - 8fab			*/ /*only nss_ssoc*/
549 		default:
550 			log_cb(RETRO_LOG_DEBUG, LOGPRE "offset = %x pc = %x\n",offset,activecpu_get_pc());
551 	}
552 
553 	/* Unsupported reads return 0xff */
554 	return 0xff;
555 }
556 
557 /*
558  * DW   - Double write : address is written twice to set a 16bit value.
559  * low  - This is the low byte of a 16 or 24 bit value
560  * mid  - This is the middle byte of a 24 bit value
561  * high - This is the high byte of a 16 or 24 bit value
562  */
WRITE_HANDLER(snes_w_io)563 WRITE_HANDLER( snes_w_io )
564 {
565 	/* offset is from 0x000000 */
566 	switch( offset )
567 	{
568 		case INIDISP:	/* Initial settings for screen */
569 			snes_ppu.update_palette = 1;
570 			break;
571 		case OBSEL:		/* Object size and data area designation */
572 			snes_ppu.layer[4].data = ((data & 0x3) * 0x2000) << 1;
573 			snes_ppu.oam.name_select = (((data & 0x18)>>3) * 0x1000) << 1;
574 			/* Determine object size */
575 			switch( (data & 0xe0) >> 5 )
576 			{
577 				case 0:			/* 8 & 16 */
578 					snes_ppu.oam.size[0] = 1;
579 					snes_ppu.oam.size[1] = 2;
580 					break;
581 				case 1:			/* 8 & 32 */
582 					snes_ppu.oam.size[0] = 1;
583 					snes_ppu.oam.size[1] = 4;
584 					break;
585 				case 2:			/* 8 & 64 */
586 					snes_ppu.oam.size[0] = 1;
587 					snes_ppu.oam.size[1] = 8;
588 					break;
589 				case 3:			/* 16 & 32 */
590 					snes_ppu.oam.size[0] = 2;
591 					snes_ppu.oam.size[1] = 4;
592 					break;
593 				case 4:			/* 16 & 64 */
594 					snes_ppu.oam.size[0] = 2;
595 					snes_ppu.oam.size[1] = 8;
596 					break;
597 				case 5:			/* 32 & 64 */
598 					snes_ppu.oam.size[0] = 4;
599 					snes_ppu.oam.size[1] = 8;
600 					break;
601 				default:
602 					/* Unknown size so default to 8 & 16 */
603 					snes_ppu.oam.size[0] = 1;
604 					snes_ppu.oam.size[1] = 2;
605 #ifdef SNES_DBG_REG_W
606 					log_cb(RETRO_LOG_DEBUG, LOGPRE  "Object size unsupported: %d\n", (data & 0xe0) >> 5 );
607 #endif
608 			}
609 			break;
610 		case OAMADDL:	/* Address for accessing OAM (low) */
611 			snes_ppu.oam.address_low = data;
612 			snes_ppu.oam.address = ((snes_ppu.oam.address_high & 0x1) << 8) + data;
613 			snes_ram[OAMDATA] = 0;
614 			break;
615 		case OAMADDH:	/* Address for accessing OAM (high) */
616 			snes_ppu.oam.address_high = data & 0x1;
617 			snes_ppu.oam.address = ((data & 0x1) << 8) + snes_ppu.oam.address_low;
618 			if( data & 0x80 )
619 				snes_ppu.oam.high_priority = snes_ppu.oam.address;
620 			snes_ram[OAMDATA] = 0;
621 			break;
622 		case OAMDATA:	/* Data for OAM write (DW) */
623 			{
624 				snes_oam[snes_ppu.oam.address] = ((snes_oam[snes_ppu.oam.address] >> 8) & 0xff) + (data << 8);
625 				snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
626 				if( snes_ram[OAMDATA] == 0 )
627 				{
628 					snes_ram[OAMDATA] = 0;
629 					snes_ppu.oam.address++;
630 					snes_ram[OAMADDL] = snes_ppu.oam.address & 0xff;
631 					snes_ram[OAMADDH] = (snes_ppu.oam.address >> 8) & 0x1;
632 				}
633 				return;
634 			}
635 		case BGMODE:	/* BG mode and character size settings */
636 			snes_ppu.mode = data & 0x7;
637 #ifdef SNES_DBG_VIDHRDW
638 			if( snes_ppu.mode == 5 || snes_ppu.mode == 6 )
639 				set_visible_area(0, (SNES_SCR_WIDTH * 2 * 1.75) - 1, 0, snes_ppu.beam.last_visible_line  - 1);
640 			else
641 				set_visible_area(0, (SNES_SCR_WIDTH * 2 * 1.75) - 1, 0, snes_ppu.beam.last_visible_line - 1 );
642 #else
643 			if( snes_ppu.mode == 5 || snes_ppu.mode == 6 )
644 				set_visible_area(0, (SNES_SCR_WIDTH * 2) - 1, 0, snes_ppu.beam.last_visible_line - 1 );
645 			else
646 				set_visible_area(0, SNES_SCR_WIDTH - 1, 0, snes_ppu.beam.last_visible_line - 1 );
647 #endif
648 
649 			snes_ppu.layer[0].tile_size = (data >> 4) & 0x1;
650 			snes_ppu.layer[1].tile_size = (data >> 5) & 0x1;
651 			snes_ppu.layer[2].tile_size = (data >> 6) & 0x1;
652 			snes_ppu.layer[3].tile_size = (data >> 7) & 0x1;
653 			snes_ppu.update_offsets = 1;
654 			break;
655 		case MOSAIC:	/* Size and screen designation for mosaic */
656 			/* FIXME: We don't support horizontal mosaic yet */
657 			break;
658 		case BG1SC:		/* Address for storing SC data BG1 SC size designation */
659 		case BG2SC:		/* Address for storing SC data BG2 SC size designation  */
660 		case BG3SC:		/* Address for storing SC data BG3 SC size designation  */
661 		case BG4SC:		/* Address for storing SC data BG4 SC size designation  */
662 			snes_ppu.layer[offset - BG1SC].map = (data & 0xfc) << 9;
663 			snes_ppu.layer[offset - BG1SC].map_size = data & 0x3;
664 			break;
665 		case BG12NBA:	/* Address for BG 1 and 2 character data */
666 			snes_ppu.layer[0].data = (data & 0xf) << 13;
667 			snes_ppu.layer[1].data = (data & 0xf0) << 9;
668 			break;
669 		case BG34NBA:	/* Address for BG 3 and 4 character data */
670 			snes_ppu.layer[2].data = (data & 0xf) << 13;
671 			snes_ppu.layer[3].data = (data & 0xf0) << 9;
672 			break;
673 		case BG1HOFS:	/* BG1 - horizontal scroll (DW) */
674 			snes_ppu.layer[0].offset.horizontal = ((snes_ppu.layer[0].offset.horizontal >> 8) & 0xff) + (data << 8);
675 			snes_ppu.update_offsets = 1;
676 			return;
677 		case BG1VOFS:	/* BG1 - vertical scroll (DW) */
678 			snes_ppu.layer[0].offset.vertical = ((snes_ppu.layer[0].offset.vertical >> 8) & 0xff) + (data << 8);
679 			snes_ppu.update_offsets = 1;
680 			return;
681 		case BG2HOFS:	/* BG2 - horizontal scroll (DW) */
682 			snes_ppu.layer[1].offset.horizontal = ((snes_ppu.layer[1].offset.horizontal >> 8) & 0xff) + (data << 8);
683 			snes_ppu.update_offsets = 1;
684 			return;
685 		case BG2VOFS:	/* BG2 - vertical scroll (DW) */
686 			snes_ppu.layer[1].offset.vertical = ((snes_ppu.layer[1].offset.vertical >> 8) & 0xff) + (data << 8);
687 			snes_ppu.update_offsets = 1;
688 			return;
689 		case BG3HOFS:	/* BG3 - horizontal scroll (DW) */
690 			snes_ppu.layer[2].offset.horizontal = ((snes_ppu.layer[2].offset.horizontal >> 8) & 0xff) + (data << 8);
691 			snes_ppu.update_offsets = 1;
692 			return;
693 		case BG3VOFS:	/* BG3 - vertical scroll (DW) */
694 			snes_ppu.layer[2].offset.vertical = ((snes_ppu.layer[2].offset.vertical >> 8) & 0xff) + (data << 8);
695 			snes_ppu.update_offsets = 1;
696 			return;
697 		case BG4HOFS:	/* BG4 - horizontal scroll (DW) */
698 			snes_ppu.layer[3].offset.horizontal = ((snes_ppu.layer[3].offset.horizontal >> 8) & 0xff) + (data << 8);
699 			snes_ppu.update_offsets = 1;
700 			return;
701 		case BG4VOFS:	/* BG4 - vertical scroll (DW) */
702 			snes_ppu.layer[3].offset.vertical = ((snes_ppu.layer[3].offset.vertical >> 8) & 0xff) + (data << 8);
703 			snes_ppu.update_offsets = 1;
704 			return;
705 		case VMAIN:		/* VRAM address increment value designation */
706 			{
707 				/* FIXME: We don't support full graphic properly yet */
708 				if( data & 0xc )
709 				{
710 					vram_fg_incr = vram_fg_count = 0x10 << ((data & 0xc) >> 2);
711 					vram_fg_cntr = 8;
712 					vram_fg_offset = 0;
713 				}
714 #ifdef SNES_DBG_REG_W
715 				if( (data & 0x80) != (snes_ram[VMAIN] & 0x80) )
716 					log_cb(RETRO_LOG_DEBUG, LOGPRE  "VRAM access: %s(%d)\n", (data & 0x80) >> 7?"Word":"Byte", (data & 0x80) >> 7 );
717 				if( (data & 0xc) && (data & 0xc) != (snes_ram[VMAIN] & 0xc) )
718 					log_cb(RETRO_LOG_DEBUG, LOGPRE  "Full graphic specified: %d, incr: %d\n", (data & 0xc) >> 2, vram_fg_incr );
719 #endif
720 			}
721 			break;
722 		case VMADDL:	/* Address for VRAM read/write (low) */
723 		case VMADDH:	/* Address for VRAM read/write (high) */
724 			vram_read_offset = 0;
725 			vram_fg_count = vram_fg_incr;
726 			vram_fg_cntr = 8;
727 			break;
728 		case VMDATAL:	/* Data for VRAM write (low) */
729 			{
730 				UINT16 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
731 				if( snes_ram[VMAIN] & 0xc )
732 					snes_vram[(addr + vram_fg_offset) << 1] = data;
733 				else
734 					snes_vram[addr << 1] = data;
735 
736 				vram_read_offset = 0;
737 				if( !(snes_ram[VMAIN] & 0x80) )
738 				{
739 					if( snes_ram[VMAIN] & 0xc )
740 					{
741 						addr++;
742 						vram_fg_offset += 7;	/* addr increases by 1, plus 7 = 8 */
743 						vram_fg_count--;
744 						if( vram_fg_count == 0 )
745 						{
746 							vram_fg_cntr--;
747 							vram_fg_count = vram_fg_incr;
748 							if( vram_fg_cntr == 0 )
749 							{
750 								vram_fg_cntr = 8;
751 								vram_fg_offset -= 7;
752 							}
753 							else
754 							{
755 								vram_fg_offset -= (vram_fg_count * 8) - 1;
756 							}
757 						}
758 					}
759 					else
760 					{
761 						switch( snes_ram[VMAIN] & 0x03 )
762 						{
763 							case 0: addr++;      break;
764 							case 1: addr += 32;  break;
765 							case 2: addr += 128; break; /* Should be 64, but a bug in the snes means it's 128 */
766 							case 3: addr += 128; break;
767 						}
768 					}
769 					snes_ram[VMADDL] = addr & 0xff;
770 					snes_ram[VMADDH] = (addr >> 8) & 0xff;
771 				}
772 			} return;
773 		case VMDATAH:	/* Data for VRAM write (high) */
774 			{
775 				UINT16 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
776 				if( snes_ram[VMAIN] & 0xc )
777 					snes_vram[((addr + vram_fg_offset) << 1) + 1] = data;
778 				else
779 					snes_vram[(addr << 1) + 1] = data;
780 
781 				vram_read_offset = 0;
782 				if( (snes_ram[VMAIN] & 0x80) )
783 				{
784 					if( snes_ram[VMAIN] & 0xc )
785 					{
786 						addr++;
787 						vram_fg_offset += 7;	/* addr increases by 1, plus 7 = 8 */
788 						vram_fg_count--;
789 						if( vram_fg_count == 0 )
790 						{
791 							vram_fg_cntr--;
792 							vram_fg_count = vram_fg_incr;
793 							if( vram_fg_cntr == 0 )
794 							{
795 								vram_fg_cntr = 8;
796 								vram_fg_offset -= 7;
797 							}
798 							else
799 							{
800 								vram_fg_offset -= (vram_fg_count * 8) - 1;
801 							}
802 						}
803 					}
804 					else
805 					{
806 						switch( snes_ram[VMAIN] & 0x03 )
807 						{
808 							case 0: addr++;      break;
809 							case 1: addr += 32;  break;
810 							case 2: addr += 128; break; /* Should be 64, but a bug in the snes means it's 128 */
811 							case 3: addr += 128; break;
812 						}
813 					}
814 					snes_ram[VMADDL] = addr & 0xff;
815 					snes_ram[VMADDH] = (addr >> 8) & 0xff;
816 				}
817 			} return;
818 		case M7SEL:		/* Mode 7 initial settings */
819 			break;
820 		case M7A:		/* Mode 7 COS angle/x expansion (DW) */
821 			snes_ppu.mode7.matrix_a = ((snes_ppu.mode7.matrix_a >> 8) & 0xff) + (data << 8);
822 			break;
823 		case M7B:		/* Mode 7 SIN angle/ x expansion (DW) */
824 			snes_ppu.mode7.matrix_b = ((snes_ppu.mode7.matrix_b >> 8) & 0xff) + (data << 8);
825 			break;
826 		case M7C:		/* Mode 7 SIN angle/y expansion (DW) */
827 			snes_ppu.mode7.matrix_c = ((snes_ppu.mode7.matrix_c >> 8) & 0xff) + (data << 8);
828 			break;
829 		case M7D:		/* Mode 7 COS angle/y expansion (DW) */
830 			snes_ppu.mode7.matrix_d = ((snes_ppu.mode7.matrix_d >> 8) & 0xff) + (data << 8);
831 			break;
832 		case M7X:		/* Mode 7 x center position (DW) */
833 			snes_ppu.mode7.origin_x = ((snes_ppu.mode7.origin_x >> 8) & 0xff) + (data << 8);
834 			break;
835 		case M7Y:		/* Mode 7 y center position (DW) */
836 			snes_ppu.mode7.origin_y = ((snes_ppu.mode7.origin_y >> 8) & 0xff) + (data << 8);
837 			break;
838 		case CGADD:		/* Initial address for colour RAM writing */
839 			/* CGRAM is 16-bit, but when reading/writing we treat it as
840 			 * 8-bit, so we need to double the address */
841 			cgram_address = data << 1;
842 			break;
843 		case CGDATA:	/* Data for colour RAM */
844 			((UINT8 *)snes_cgram)[cgram_address] = data;
845 			cgram_address = (cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
846 			snes_ppu.update_palette = 1;
847 			break;
848 		case W12SEL:	/* Window mask settings for BG1-2 */
849 		case W34SEL:	/* Window mask settings for BG3-4 */
850 		case WOBJSEL:	/* Window mask settings for objects */
851 		case WH0:		/* Window 1 left position */
852 		case WH1:		/* Window 1 right position */
853 		case WH2:		/* Window 2 left position */
854 		case WH3:		/* Window 2 right position */
855 		case WBGLOG:	/* Window mask logic for BG's */
856 		case WOBJLOG:	/* Window mask logic for objects */
857 			if( data != snes_ram[offset] )
858 				snes_ppu.update_windows = 1;
859 			break;
860 		case TM:		/* Main screen designation */
861 		case TS:		/* Subscreen designation */
862 		case TMW:		/* Window mask for main screen designation */
863 		case TSW:		/* Window mask for subscreen designation */
864 			break;
865 		case CGWSEL:	/* Initial settings for Fixed colour addition or screen addition */
866 			/* FIXME: We don't support direct select for modes 3 & 4 or subscreen window stuff */
867 #ifdef SNES_DBG_REG_W
868 			if( (data & 0x2) != (snes_ram[CGWSEL] & 0x2) )
869 				log_cb(RETRO_LOG_DEBUG, LOGPRE  "Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour" );
870 #endif
871 			break;
872 		case CGADSUB:	/* Addition/Subtraction designation for each screen */
873 			{
874 				UINT8 sub = (data & 0x80) >> 7;
875 				snes_ppu.layer[0].blend = (data & 0x1) << sub;
876 				snes_ppu.layer[1].blend = ((data & 0x2) >> 1) << sub;
877 				snes_ppu.layer[2].blend = ((data & 0x4) >> 2) << sub;
878 				snes_ppu.layer[3].blend = ((data & 0x8) >> 3) << sub;
879 				snes_ppu.layer[4].blend = ((data & 0x10) >> 4) << sub;
880 			} break;
881 		case COLDATA:	/* Fixed colour data for fixed colour addition/subtraction */
882 			{
883 				/* Store it in the extra space we made in the CGRAM
884 				 * It doesn't really go there, but it's as good a place as any. */
885 				UINT8 r,g,b,fade;
886 
887 				/* Get existing value. */
888 				r = snes_cgram[FIXED_COLOUR] & 0x1f;
889 				g = (snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
890 				b = (snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
891 				/* Set new value */
892 				if( data & 0x20 )
893 					r = data & 0x1f;
894 				if( data & 0x40 )
895 					g = data & 0x1f;
896 				if( data & 0x80 )
897 					b = data & 0x1f;
898 				snes_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
899 
900 				/* set the palette entry, adjusting to the fade setting */
901 				fade = (snes_ram[INIDISP] & 0xf) + 1;
902 				r = (r * fade) >> 4;
903 				g = (g * fade) >> 4;
904 				b = (b * fade) >> 4;
905 				Machine->remapped_colortable[FIXED_COLOUR] = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
906 			} break;
907 		case SETINI:	/* Screen mode/video select */
908 			/* FIXME: We only support line count here */
909 			snes_ppu.beam.last_visible_line = (data & 0x4) ? 240 : 225;
910 #ifdef SNES_DBG_REG_W
911 			if( (data & 0x8) != (snes_ram[SETINI] & 0x8) )
912 				log_cb(RETRO_LOG_DEBUG, LOGPRE  "Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off" );
913 #endif
914 			break;
915 		case APU00:		/* Audio port register */
916 		case APU01:		/* Audio port register */
917 		case APU02:		/* Audio port register */
918 		case APU03:		/* Audio port register */
919 			if( spc_usefakeapu )
920 				fakespc_port_w( offset & 0x3, data );
921 			else
922 			{
923 				cpu_boost_interleave(0, TIME_IN_USEC(20));
924 			spc_port_in[offset & 0x3] = data;
925 			}
926 			return;
927 		case WMDATA:	/* Data to write to WRAM */
928 			{
929 				UINT32 addr = ((snes_ram[WMADDH] & 0x1) << 16) | (snes_ram[WMADDM] << 8) | snes_ram[WMADDL];
930 
931 				cpu_writemem24( 0x7e0000 + addr++, data );
932 				snes_ram[WMADDH] = (addr >> 16) & 0x1;
933 				snes_ram[WMADDM] = (addr >> 8) & 0xff;
934 				snes_ram[WMADDL] = addr & 0xff;
935 				return;
936 			}
937 		case WMADDL:	/* Address to read/write to wram (low) */
938 		case WMADDM:	/* Address to read/write to wram (mid) */
939 		case WMADDH:	/* Address to read/write to wram (high) */
940 			break;
941 		case OLDJOY1:	/* Old NES joystick support */
942 			if( (data & 0x1) && !(snes_ram[offset] & 0x1) )
943 			{
944 				joypad[0].oldrol = 0;
945 				joypad[1].oldrol = 0;
946 				joypad[2].oldrol = 0;
947 				joypad[3].oldrol = 0;
948 			}
949 			break;
950 		case OLDJOY2:	/* Old NES joystick support */
951 		case NMITIMEN:	/* Flag for v-blank, timer int. and joy read */
952 		case WRIO:		/* Programmable I/O port */
953 		case WRMPYA:	/* Multiplier A */
954 			break;
955 		case WRMPYB:	/* Multiplier B */
956 			{
957 				UINT32 c = snes_ram[WRMPYA] * data;
958 				snes_ram[RDMPYL] = c & 0xff;
959 				snes_ram[RDMPYH] = (c >> 8) & 0xff;
960 			} break;
961 		case WRDIVL:	/* Dividend (low) */
962 		case WRDIVH:	/* Dividend (high) */
963 			break;
964 		case WRDVDD:	/* Divisor */
965 			{
966 				UINT16 value, dividend, remainder;
967 				dividend = remainder = 0;
968 				value = (snes_ram[WRDIVH] << 8) + snes_ram[WRDIVL];
969 				if( data > 0 )
970 				{
971 					dividend = value / data;
972 					remainder = value % data;
973 				}
974 				else
975 				{
976 					dividend = 0xffff;
977 					remainder = value;
978 				}
979 				snes_ram[RDDIVL] = dividend & 0xff;
980 				snes_ram[RDDIVH] = (dividend >> 8) & 0xff;
981 				snes_ram[RDMPYL] = remainder & 0xff;
982 				snes_ram[RDMPYH] = (remainder >> 8) & 0xff;
983 			} break;
984 		case HTIMEL:	/* H-Count timer settings (low)  */
985 		case HTIMEH:	/* H-Count timer settings (high) */
986 		case VTIMEL:	/* V-Count timer settings (low)  */
987 		case VTIMEH:	/* V-Count timer settings (high) */
988 			break;
989 		case MDMAEN:	/* GDMA channel designation and trigger */
990 			snes_gdma( data );
991 			data = 0;	/* Once DMA is done we need to reset all bits to 0 */
992 			break;
993 		case HDMAEN:	/* HDMA channel designation */
994 			break;
995 		case MEMSEL:	/* Access cycle designation in memory (2) area */
996 			/* FIXME: Need to adjust the speed only during access of banks 0x80+
997 			 * Currently we are just increasing it no matter what */
998 			cpunum_set_clockscale( 0, (data & 0x1) ? 1.335820896 : 1.0 );
999 #ifdef SNES_DBG_REG_W
1000 			if( (data & 0x1) != (snes_ram[MEMSEL] & 0x1) )
1001 				log_cb(RETRO_LOG_DEBUG, LOGPRE  "CPU speed: %f Mhz\n", (data & 0x1) ? 3.58 : 2.68 );
1002 #endif
1003 			break;
1004 	/* Following are read-only */
1005 		case HVBJOY:	/* H/V blank and joypad enable */
1006 		case MPYL:		/* Multiplication result (low) */
1007 		case MPYM:		/* Multiplication result (mid) */
1008 		case MPYH:		/* Multiplication result (high) */
1009 		case RDIO:
1010 		case RDDIVL:
1011 		case RDDIVH:
1012 		case RDMPYL:
1013 		case RDMPYH:
1014 		case JOY1L:
1015 		case JOY1H:
1016 		case JOY2L:
1017 		case JOY2H:
1018 		case JOY3L:
1019 		case JOY3H:
1020 		case JOY4L:
1021 		case JOY4H:
1022 #ifdef MAME_DEBUG
1023 			log_cb(RETRO_LOG_DEBUG, LOGPRE  "Write to read-only register: %X value: %X", offset, data );
1024 #endif /* MAME_DEBUG */
1025 			return;
1026 	/* Below is all DMA related */
1027 		case DMAP0: case BBAD0: case A1T0L: case A1T0H: case A1B0: case DAS0L:
1028 		case DAS0H: case DSAB0: case A2A0L: case A2A0H: case NTRL0:
1029 		case DMAP1: case BBAD1: case A1T1L: case A1T1H: case A1B1: case DAS1L:
1030 		case DAS1H: case DSAB1: case A2A1L: case A2A1H: case NTRL1:
1031 		case DMAP2: case BBAD2: case A1T2L: case A1T2H: case A1B2: case DAS2L:
1032 		case DAS2H: case DSAB2: case A2A2L: case A2A2H: case NTRL2:
1033 		case DMAP3: case BBAD3: case A1T3L: case A1T3H: case A1B3: case DAS3L:
1034 		case DAS3H: case DSAB3: case A2A3L: case A2A3H: case NTRL3:
1035 		case DMAP4: case BBAD4: case A1T4L: case A1T4H: case A1B4: case DAS4L:
1036 		case DAS4H: case DSAB4: case A2A4L: case A2A4H: case NTRL4:
1037 		case DMAP5: case BBAD5: case A1T5L: case A1T5H: case A1B5: case DAS5L:
1038 		case DAS5H: case DSAB5: case A2A5L: case A2A5H: case NTRL5:
1039 		case DMAP6: case BBAD6: case A1T6L: case A1T6H: case A1B6: case DAS6L:
1040 		case DAS6H: case DSAB6: case A2A6L: case A2A6H: case NTRL6:
1041 		case DMAP7: case BBAD7: case A1T7L: case A1T7H: case A1B7: case DAS7L:
1042 		case DAS7H: case DSAB7: case A2A7L: case A2A7H: case NTRL7:
1043 			break;
1044 	}
1045 
1046 	snes_ram[offset] = data;
1047 }
1048 
1049 /* This function checks everything is in a valid range and returns how
1050  * 'valid' this section is as an information block. */
snes_validate_infoblock(UINT8 * infoblock,UINT16 offset)1051 static int snes_validate_infoblock( UINT8 *infoblock, UINT16 offset )
1052 {
1053 	INT8 valid = 6;
1054 
1055 	/* Check the CRC and inverse CRC */
1056 	if( ((infoblock[offset + 0x1c] + (infoblock[offset + 0x1d] << 8)) |
1057 		(infoblock[offset + 0x1e] + (infoblock[offset + 0x1f] << 8))) != 0xffff )
1058 	{
1059 		valid -= 3;
1060 	}
1061 	/* Check the ROM Size is in a valid range */
1062 	if( infoblock[offset + 0x17] > 13 )
1063 	{
1064 		valid--;
1065 	}
1066 	/* Check the SRAM size */
1067 	if( infoblock[offset + 0x18] > 8 )
1068 	{
1069 		valid--;
1070 	}
1071 	/* Check the Country is in a valid range */
1072 	if( infoblock[offset + 0x19] > 13 )
1073 	{
1074 		valid--;
1075 	}
1076 	/* Check the game version */
1077 	if( infoblock[offset + 0x1b] >= 128 )
1078 	{
1079 		valid--;
1080 	}
1081 
1082 	if( valid < 0 )
1083 	{
1084 		valid = 0;
1085 	}
1086 
1087 	return valid;
1088 }
1089 
DRIVER_INIT(snes)1090 DRIVER_INIT( snes )
1091 {
1092 	int i;
1093 	UINT16 totalblocks, readblocks;
1094 	UINT8  *rom;
1095 
1096 	rom = memory_region( REGION_USER3 );
1097 	snes_ram = memory_region( REGION_CPU1 );
1098 	memset( snes_ram, 0, 0x1000000 );
1099 
1100 	/* all NSS games seem to use MODE 20 */
1101 	cart.mode = SNES_MODE_20;
1102 	cart.sram_max = 0x40000;
1103 
1104 	/* Find the number of blocks in this ROM */
1105 	/*totalblocks = ((mame_fsize(file) - offset) >> (cart.mode == MODE_20 ? 15 : 16));*/
1106 	totalblocks = (memory_region_length(REGION_USER3) / 0x8000) - 1;
1107 
1108 	/* FIXME: Insert crc check here */
1109 
1110 	readblocks = 0;
1111 	{
1112 		/* In mode 20, all blocks are 32kb. There are upto 96 blocks, giving a
1113 		 * total of 24mbit(3mb) of ROM.
1114 		 * The first 48 blocks are located in banks 0x00 to 0x2f at address
1115 		 * 0x8000.  They are mirrored in banks 0x80 to 0xaf.
1116 		 * The next 16 blocks are located in banks 0x30 to 0x3f at address
1117 		 * 0x8000.  They are mirrored in banks 0xb0 to 0xbf.
1118 		 * The final 32 blocks are located in banks 0x40 - 0x5f at address
1119 		 * 0x8000.  They are mirrored in banks 0xc0 to 0xdf.
1120 		 */
1121 		i = 0;
1122 		while( i < 96 && readblocks <= totalblocks )
1123 		{
1124 			/*mame_fread( file, &snes_ram[(i++ * 0x10000) + 0x8000], 0x8000);*/
1125 			memcpy(&snes_ram[(i * 0x10000) + 0x8000], &rom[i * 0x8000], 0x8000);
1126 			i++;
1127 			readblocks++;
1128 		}
1129 	}
1130 
1131 	/* Find the amount of sram */
1132 	cart.sram = snes_r_bank1(0x00ffd8);
1133 	if( cart.sram > 0 )
1134 	{
1135 		cart.sram = ((1 << (cart.sram + 3)) / 8);
1136 		if( cart.sram > cart.sram_max )
1137 			cart.sram = cart.sram_max;
1138 	}
1139 
1140 	free_memory_region(REGION_USER3);
1141 }
1142 
1143 
INTERRUPT_GEN(snes_scanline_interrupt)1144 INTERRUPT_GEN(snes_scanline_interrupt)
1145 {
1146 	/* Start of VBlank */
1147 	if( snes_ppu.beam.current_vert == snes_ppu.beam.last_visible_line )
1148 	{
1149 		snes_ram[HVBJOY] |= 0x80;		/* Set vblank bit to on */
1150 		snes_ram[STAT77] &= 0x3f;		/* Clear Time Over and Range Over bits - done every nmi (presumably because no sprites drawn here) */
1151 		snes_ram[RDNMI] |= 0x80;		/* Set NMI occured bit */
1152 		if( snes_ram[NMITIMEN] & 0x80 )	/* NMI only signaled if this bit set */
1153 		{
1154 			cpu_set_irq_line( 0, G65816_LINE_NMI, HOLD_LINE );
1155 		}
1156 	}
1157 
1158 	/* Setup HDMA on start of new frame */
1159 	if( snes_ppu.beam.current_vert == 0 )
1160 		snes_hdma_init();
1161 
1162 	/* Let's draw the current line */
1163 	if( snes_ppu.beam.current_vert < snes_ppu.beam.last_visible_line )
1164 	{
1165 		/* Do HDMA */
1166 		if( snes_ram[HDMAEN] )
1167 			snes_hdma();
1168 
1169 		snes_refresh_scanline( snes_ppu.beam.current_vert );
1170 	}
1171 	else
1172 	{
1173 		joypad[0].low = readinputport( 0 );
1174 		joypad[0].high = readinputport( 1 );
1175 		joypad[1].low = readinputport( 2 );
1176 		joypad[1].high = readinputport( 3 );
1177 		joypad[2].low = readinputport( 4 );
1178 		joypad[2].high = readinputport( 5 );
1179 		joypad[3].low = readinputport( 6 );
1180 		joypad[3].high = readinputport( 7 );
1181 	}
1182 
1183 	/* Vertical IRQ timer */
1184 	if( snes_ram[NMITIMEN] & 0x20 )
1185 	{
1186 		if( snes_ppu.beam.current_vert == (((snes_ram[VTIMEH] << 8) | snes_ram[VTIMEL]) & 0x1ff) )
1187 		{
1188 			snes_ram[TIMEUP] = 0x80;	/* Indicate that irq occured */
1189 			cpu_set_irq_line( 0, G65816_LINE_IRQ, HOLD_LINE );
1190 		}
1191 	}
1192 	/* Horizontal IRQ timer */
1193 	/* FIXME: Commented out as it causes heaps of trouble right now */
1194 /*	if( snes_ram[NMITIMEN] & 0x10 )
1195 	{
1196 		if( (((snes_ram[HTIMEH] << 8) | snes_ram[HTIMEL]) & 0x1ff) )
1197 		{
1198 			snes_ram[TIMEUP] = 0x80;
1199 			cpu_set_irq_line( 0, G65816_LINE_IRQ, HOLD_LINE );
1200 		}
1201 	} */
1202 
1203 	/* Increase current line */
1204 	snes_ppu.beam.current_vert = (snes_ppu.beam.current_vert + 1) % (snes_ram[STAT78] == SNES_NTSC ? SNES_MAX_LINES_NTSC : SNES_MAX_LINES_PAL);
1205 	if( snes_ppu.beam.current_vert == 0 )
1206 	{	/* VBlank is over, time for a new frame */
1207 		cpu_writemem24( OAMADDL, snes_ppu.oam.address_low ); /* Reset oam address */
1208 		cpu_writemem24( OAMADDH, snes_ppu.oam.address_high );
1209 		snes_ram[HVBJOY] &= 0x7f;		/* Clear vblank bit */
1210 		snes_ram[RDNMI]  &= 0x7f;		/* Clear nmi occured bit */
1211 		cpu_set_irq_line( 0, G65816_LINE_NMI, CLEAR_LINE );
1212 	}
1213 }
1214 
snes_hdma_init()1215 void snes_hdma_init()
1216 {
1217 	UINT8 mask = 1, dma = 0, i;
1218 
1219 	snes_hdma_chnl = snes_ram[HDMAEN];
1220 	for( i = 0; i < 8; i++ )
1221 	{
1222 		if( snes_ram[HDMAEN] & mask )
1223 		{
1224 			snes_ram[SNES_DMA_BASE + dma + 8] = snes_ram[SNES_DMA_BASE + dma + 2];
1225 			snes_ram[SNES_DMA_BASE + dma + 9] = snes_ram[SNES_DMA_BASE + dma + 3];
1226 			snes_ram[SNES_DMA_BASE + dma + 0xa] = 0;
1227 		}
1228 		dma += 0x10;
1229 		mask <<= 1;
1230 	}
1231 }
1232 
snes_hdma()1233 void snes_hdma()
1234 {
1235 	UINT8 mask = 1, dma = 0, i, contmode;
1236 	UINT16 bbus;
1237 	UINT32 abus;
1238 
1239 	/* Assume priority of the 8 DMA channels is 0-7 */
1240 	for( i = 0; i < 8; i++ )
1241 	{
1242 		if( snes_hdma_chnl & mask )
1243 		{
1244 			/* Check if we need to read a new line from the table */
1245 			if( !(snes_ram[SNES_DMA_BASE + dma + 0xa] & 0x7f ) )
1246 			{
1247 				abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 9] << 8) + snes_ram[SNES_DMA_BASE + dma + 8];
1248 
1249 				/* Get the number of lines */
1250 				snes_ram[SNES_DMA_BASE + dma + 0xa] = cpu_readmem24(abus);
1251 				if( !snes_ram[SNES_DMA_BASE + dma + 0xa] )
1252 				{
1253 					/* No more lines so clear HDMA */
1254 					snes_hdma_chnl &= ~mask;
1255 					continue;
1256 				}
1257 				abus++;
1258 				snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
1259 				snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
1260 				if( snes_ram[SNES_DMA_BASE + dma] & 0x40 )
1261 				{
1262 					snes_ram[SNES_DMA_BASE + dma + 5] = cpu_readmem24(abus++);
1263 					snes_ram[SNES_DMA_BASE + dma + 6] = cpu_readmem24(abus++);
1264 					snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
1265 					snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
1266 				}
1267 			}
1268 
1269 			contmode = (--snes_ram[SNES_DMA_BASE + dma + 0xa]) & 0x80;
1270 
1271 			/* Transfer addresses */
1272 			if( snes_ram[SNES_DMA_BASE + dma] & 0x40 )	/* Indirect */
1273 				abus = (snes_ram[SNES_DMA_BASE + dma + 7] << 16) + (snes_ram[SNES_DMA_BASE + dma + 6] << 8) + snes_ram[SNES_DMA_BASE + dma + 5];
1274 			else									/* Absolute */
1275 				abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 9] << 8) + snes_ram[SNES_DMA_BASE + dma + 8];
1276 			bbus = 0x2100 + snes_ram[SNES_DMA_BASE + dma + 1];
1277 
1278 #ifdef SNES_DBG_HDMA
1279 			log_cb(RETRO_LOG_DEBUG, LOGPRE  "HDMA-Ch: %d(%s) abus: %X bbus: %X type: %d(%X %X)\n", i, snes_ram[SNES_DMA_BASE + dma] & 0x40 ? "Indirect" : "Absolute", abus, bbus, snes_ram[SNES_DMA_BASE + dma] & 0x7, snes_ram[SNES_DMA_BASE + dma + 8],snes_ram[SNES_DMA_BASE + dma + 9] );
1280 #endif
1281 
1282 			switch( snes_ram[SNES_DMA_BASE + dma] & 0x7 )
1283 			{
1284 				case 0:		/* 1 address */
1285 				{
1286 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1287 				} break;
1288 				case 1:		/* 2 addresses (l,h) */
1289 				{
1290 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1291 					cpu_writemem24( bbus + 1, cpu_readmem24(abus++));
1292 				} break;
1293 				case 2:		/* Write twice (l,l) */
1294 				{
1295 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1296 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1297 				} break;
1298 				case 3:		/* 2 addresses/Write twice (l,l,h,h) */
1299 				{
1300 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1301 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1302 					cpu_writemem24( bbus + 1, cpu_readmem24(abus++));
1303 					cpu_writemem24( bbus + 1, cpu_readmem24(abus++));
1304 				} break;
1305 				case 4:		/* 4 addresses (l,h,l,h) */
1306 				{
1307 					cpu_writemem24( bbus, cpu_readmem24(abus++));
1308 					cpu_writemem24( bbus + 1, cpu_readmem24(abus++));
1309 					cpu_writemem24( bbus + 2, cpu_readmem24(abus++));
1310 					cpu_writemem24( bbus + 3, cpu_readmem24(abus++));
1311 				} break;
1312 				default:
1313 #ifdef MAME_DEBUG
1314 					log_cb(RETRO_LOG_DEBUG, LOGPRE  "  HDMA of unsupported type: %d\n", snes_ram[SNES_DMA_BASE + dma] & 0x7 );
1315 #endif
1316 					break;
1317 			}
1318 
1319 			/* Update address */
1320 			if( contmode )
1321 			{
1322 				if( snes_ram[SNES_DMA_BASE + dma] & 0x40 )	/* Indirect */
1323 				{
1324 					snes_ram[SNES_DMA_BASE + dma + 5] = abus & 0xff;
1325 					snes_ram[SNES_DMA_BASE + dma + 6] = (abus >> 8) & 0xff;
1326 				}
1327 				else									/* Absolute */
1328 				{
1329 					snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
1330 					snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
1331 				}
1332 			}
1333 
1334 			if( !(snes_ram[SNES_DMA_BASE + dma + 0xa] & 0x7f) )
1335 			{
1336 				if( !(snes_ram[SNES_DMA_BASE + dma] & 0x40) )	/* Absolute */
1337 				{
1338 					if( !contmode )
1339 					{
1340 						snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
1341 						snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
1342 					}
1343 				}
1344 			}
1345 		}
1346 		dma += 0x10;
1347 		mask <<= 1;
1348 	}
1349 }
1350 
snes_gdma(UINT8 channels)1351 void snes_gdma( UINT8 channels )
1352 {
1353 	UINT8 mask = 1, dma = 0, i;
1354 	INT8 increment;
1355 	UINT16 bbus;
1356 	UINT32 abus, length;
1357 
1358 	/* Assume priority of the 8 DMA channels is 0-7 */
1359 	for( i = 0; i < 8; i++ )
1360 	{
1361 		if( channels & mask )
1362 		{
1363 			/* Find transfer addresses */
1364 			abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 3] << 8) + snes_ram[SNES_DMA_BASE + dma + 2];
1365 			bbus = 0x2100 + snes_ram[SNES_DMA_BASE + dma + 1];
1366 
1367 			/* Auto increment */
1368 			if( snes_ram[SNES_DMA_BASE + dma] & 0x8 )
1369 			{
1370 				increment = 0;
1371 			}
1372 			else
1373 			{
1374 				if( snes_ram[SNES_DMA_BASE + dma] & 0x10 )
1375 					increment = -1;
1376 				else
1377 					increment = 1;
1378 			}
1379 
1380 			/* Number of bytes to transfer */
1381 			length = (snes_ram[SNES_DMA_BASE + dma + 6] << 8) + snes_ram[SNES_DMA_BASE + dma + 5];
1382 			if( !length )
1383 				length = 0x10000;	/* 0x0000 really means 0x10000 */
1384 
1385 #ifdef SNES_DBG_GDMA
1386 			log_cb(RETRO_LOG_DEBUG, LOGPRE  "GDMA-Ch %d: len: %X, abus: %X, bbus: %X, incr: %d, dir: %s, type: %d\n", i, length, abus, bbus, increment, snes_ram[SNES_DMA_BASE + dma] & 0x80 ? "PPU->CPU" : "CPU->PPU", snes_ram[SNES_DMA_BASE + dma] & 0x7 );
1387 #endif
1388 
1389 			switch( snes_ram[SNES_DMA_BASE + dma] & 0x7 )
1390 			{
1391 				case 0:		/* 1 address */
1392 				case 2:		/* 1 address ?? */
1393 				{
1394 					while( length-- )
1395 					{
1396 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1397 							cpu_writemem24( abus, cpu_readmem24(bbus) );
1398 						else									/* CPU->PPU */
1399 							cpu_writemem24( bbus, cpu_readmem24(abus) );
1400 						abus += increment;
1401 					}
1402 				} break;
1403 				case 1:		/* 2 addresses (l,h) */
1404 				{
1405 					while( length-- )
1406 					{
1407 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1408 							cpu_writemem24( abus, cpu_readmem24(bbus) );
1409 						else									/* CPU->PPU */
1410 							cpu_writemem24( bbus, cpu_readmem24(abus) );
1411 						abus += increment;
1412 						if( !(length--) )
1413 							break;
1414 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1415 							cpu_writemem24( abus, cpu_readmem24(bbus + 1) );
1416 						else									/* CPU->PPU */
1417 							cpu_writemem24( bbus + 1, cpu_readmem24(abus) );
1418 						abus += increment;
1419 					}
1420 				} break;
1421 				case 3:		/* 2 addresses/write twice (l,l,h,h) */
1422 				{
1423 					while( length-- )
1424 					{
1425 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1426 							cpu_writemem24( abus, cpu_readmem24(bbus) );
1427 						else									/* CPU->PPU */
1428 							cpu_writemem24( bbus, cpu_readmem24(abus) );
1429 						abus += increment;
1430 						if( !(length--) )
1431 							break;
1432 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1433 							cpu_writemem24( abus, cpu_readmem24(bbus) );
1434 						else									/* CPU->PPU */
1435 							cpu_writemem24( bbus, cpu_readmem24(abus) );
1436 						abus += increment;
1437 						if( !(length--) )
1438 							break;
1439 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1440 							cpu_writemem24( abus, cpu_readmem24(bbus + 1) );
1441 						else									/* CPU->PPU */
1442 							cpu_writemem24( bbus + 1, cpu_readmem24(abus) );
1443 						abus += increment;
1444 						if( !(length--) )
1445 							break;
1446 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1447 							cpu_writemem24( abus, cpu_readmem24(bbus + 1) );
1448 						else									/* CPU->PPU */
1449 							cpu_writemem24( bbus + 1, cpu_readmem24(abus) );
1450 						abus += increment;
1451 					}
1452 				} break;
1453 				case 4:		/* 4 addresses (l,h,l,h) */
1454 				{
1455 					while( length-- )
1456 					{
1457 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1458 							cpu_writemem24( abus, cpu_readmem24(bbus) );
1459 						else									/* CPU->PPU */
1460 							cpu_writemem24( bbus, cpu_readmem24(abus) );
1461 						abus += increment;
1462 						if( !(length--) )
1463 							break;
1464 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1465 							cpu_writemem24( abus, cpu_readmem24(bbus + 1) );
1466 						else									/* CPU->PPU */
1467 							cpu_writemem24( bbus + 1, cpu_readmem24(abus) );
1468 						abus += increment;
1469 						if( !(length--) )
1470 							break;
1471 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1472 							cpu_writemem24( abus, cpu_readmem24(bbus + 2) );
1473 						else									/* CPU->PPU */
1474 							cpu_writemem24( bbus + 2, cpu_readmem24(abus) );
1475 						abus += increment;
1476 						if( !(length--) )
1477 							break;
1478 						if( snes_ram[SNES_DMA_BASE + dma] & 0x80 )	/* PPU->CPU */
1479 							cpu_writemem24( abus, cpu_readmem24(bbus + 3) );
1480 						else									/* CPU->PPU */
1481 							cpu_writemem24( bbus + 3, cpu_readmem24(abus) );
1482 						abus += increment;
1483 					}
1484 				} break;
1485 				default:
1486 #ifdef MAME_DEBUG
1487 					log_cb(RETRO_LOG_DEBUG, LOGPRE  "  GDMA of unsupported type: %d\n", snes_ram[SNES_DMA_BASE + dma] & 0x7 );
1488 #endif
1489 					break;
1490 			}
1491 			/* We're done so write the new abus back to the registers */
1492 			snes_ram[SNES_DMA_BASE + dma + 2] = abus & 0xff;
1493 			snes_ram[SNES_DMA_BASE + dma + 3] = (abus >> 8) & 0xff;
1494 			snes_ram[SNES_DMA_BASE + dma + 5] = 0;
1495 			snes_ram[SNES_DMA_BASE + dma + 6] = 0;
1496 		}
1497 		dma += 0x10;
1498 		mask <<= 1;
1499 	}
1500 }
1501