summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/snes.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/snes.c')
-rw-r--r--src/mame/machine/snes.c1911
1 files changed, 1911 insertions, 0 deletions
diff --git a/src/mame/machine/snes.c b/src/mame/machine/snes.c
new file mode 100644
index 00000000000..feca580085b
--- /dev/null
+++ b/src/mame/machine/snes.c
@@ -0,0 +1,1911 @@
+/***************************************************************************
+
+ snes.c
+
+ Machine file to handle emulation of the Nintendo Super NES
+
+ R. Belmont
+ Anthony Kruize
+ Based on the original code by Lee Hammerton (aka Savoury Snax)
+ Thanks to Anomie for invaluable technical information.
+
+***************************************************************************/
+#define __MACHINE_SNES_C
+
+#include <math.h>
+#include "driver.h"
+#include "includes/snes.h"
+#include "cpu/g65816/g65816.h"
+#ifdef MESS
+#include "image.h"
+#endif
+
+// add-on chip emulators
+#include "machine/snesdsp1.c"
+
+/* -- Globals -- */
+UINT8 *snes_ram = NULL; /* 65816 ram */
+UINT8 *spc_ram = NULL; /* spc700 ram */
+UINT8 *snes_vram = NULL; /* Video RAM (Should be 16-bit, but it's easier this way) */
+UINT16 *snes_cgram = NULL; /* Colour RAM */
+UINT16 *snes_oam = NULL; /* Object Attribute Memory */
+static UINT16 cgram_address; /* CGRAM address */
+static UINT8 vram_read_offset; /* VRAM read offset */
+UINT8 spc_port_in[4]; /* Port for sending data to the SPC700 */
+UINT8 spc_port_out[4]; /* Port for receiving data from the SPC700 */
+static UINT8 ppu_last_scroll; /* as per Theme Park, this is shared by all scroll regs */
+static UINT8 snes_hdma_chnl; /* channels enabled for HDMA */
+static UINT8 joy1l, joy1h, joy2l, joy2h, joy3l, joy3h, joy4l, joy4h;
+static UINT8 read_ophct = 0, read_opvct = 0;
+static emu_timer *snes_scanline_timer;
+static emu_timer *snes_hblank_timer;
+static emu_timer *snes_nmi_timer;
+static emu_timer *snes_hirq_timer;
+static UINT16 hblank_offset;
+static UINT16 snes_htmult; /* in 512 wide, we run HTOTAL double and halve it on latching */
+
+// full graphic variables
+static UINT16 vram_fgr_high, vram_fgr_increment, vram_fgr_count, vram_fgr_mask, vram_fgr_shift, vram_read_buffer;
+static UINT16 vram_fgr_inctab[4] = { 1, 32, 128, 128 };
+static UINT16 vram_fgr_inccnts[4] = { 0, 32, 64, 128 };
+static UINT16 vram_fgr_shiftab[4] = { 0, 5, 6, 7 };
+
+struct snes_cart_info snes_cart = { SNES_MODE_20, 0x40000, 0x40000 };
+
+static struct
+{
+ UINT8 low;
+ UINT8 high;
+ UINT32 value;
+ UINT8 oldrol;
+} joypad[4];
+
+// quick extern to fix missing header
+extern attotime video_screen_get_time_until_pos(int scrnum, int vpos, int hpos);
+
+// utility function - latches the H/V counters. Used by IRQ, writes to WRIO, etc.
+static void snes_latch_counters(void)
+{
+ snes_ppu.beam.current_horz = video_screen_get_hpos(0) / snes_htmult;
+ snes_ppu.beam.latch_vert = video_screen_get_vpos(0);
+ snes_ppu.beam.latch_horz = snes_ppu.beam.current_horz;
+ snes_ram[STAT78] |= 0x40; // indicate we latched
+ read_ophct = read_opvct = 0; // clear read flags
+
+// printf("latched @ H %d V %d\n", snes_ppu.beam.latch_horz, snes_ppu.beam.latch_vert);
+}
+
+static TIMER_CALLBACK( snes_nmi_tick )
+{
+ // pull NMI
+ cpunum_set_input_line( 0, G65816_LINE_NMI, HOLD_LINE );
+
+ // don't happen again
+ timer_adjust(snes_nmi_timer, attotime_never, 0, attotime_never);
+}
+
+static void snes_hirq_tick(void)
+{
+ // latch the counters and pull IRQ
+ // (don't need to switch to the 65816 context, we don't do anything dependant on it)
+ snes_latch_counters();
+ snes_ram[TIMEUP] = 0x80; /* Indicate that irq occured */
+ cpunum_set_input_line( 0, G65816_LINE_IRQ, HOLD_LINE );
+
+ // don't happen again
+ timer_adjust(snes_hirq_timer, attotime_never, 0, attotime_never);
+}
+
+static TIMER_CALLBACK( snes_hirq_tick_callback )
+{
+ snes_hirq_tick();
+}
+
+static TIMER_CALLBACK( snes_scanline_tick )
+{
+ // make sure we're in the 65816's context since we're messing with the OAM and stuff
+ cpuintrf_push_context(0);
+
+ /* Increase current line - we want to latch on this line during it, not after it */
+ snes_ppu.beam.current_vert = video_screen_get_vpos(0);
+
+ // not in hblank
+ snes_ram[HVBJOY] &= ~0x40;
+
+ /* Vertical IRQ timer - only if horizontal isn't also enabled! */
+ if ((snes_ram[NMITIMEN] & 0x20) && !(snes_ram[NMITIMEN] & 0x10))
+ {
+ if (snes_ppu.beam.current_vert == (((snes_ram[VTIMEH] << 8) | snes_ram[VTIMEL]) & 0x1ff))
+ {
+ snes_ram[TIMEUP] = 0x80; /* Indicate that irq occured */
+ // IRQ latches the counters, do it now
+ snes_latch_counters();
+ cpunum_set_input_line( 0, G65816_LINE_IRQ, HOLD_LINE );
+ }
+ }
+ /* Horizontal IRQ timer */
+ if( snes_ram[NMITIMEN] & 0x10 )
+ {
+ int setirq = 1;
+ int pixel = ((snes_ram[HTIMEH] << 8) | snes_ram[HTIMEL]) & 0x1ff;
+
+ // is the HIRQ on a specific scanline?
+ if (snes_ram[NMITIMEN] & 0x20)
+ {
+ if (snes_ppu.beam.current_vert != (((snes_ram[VTIMEH] << 8) | snes_ram[VTIMEL]) & 0x1ff) )
+ {
+ setirq = 0;
+ }
+ }
+
+ if (setirq)
+ {
+// printf("HIRQ @ %d, %d\n", pixel*snes_htmult, snes_ppu.beam.current_vert);
+ if (pixel == 0)
+ {
+ snes_hirq_tick();
+ }
+ else
+ {
+ timer_adjust(snes_hirq_timer, video_screen_get_time_until_pos(0, snes_ppu.beam.current_vert, pixel*snes_htmult), 0, attotime_never);
+ }
+ }
+ }
+
+ /* Start of VBlank */
+ if( snes_ppu.beam.current_vert == snes_ppu.beam.last_visible_line )
+ {
+ program_write_byte(OAMADDL, snes_ppu.oam.address_low ); /* Reset oam address */
+ program_write_byte(OAMADDH, snes_ppu.oam.address_high );
+ snes_ram[HVBJOY] |= 0x81; /* Set vblank bit to on & indicate controllers being read */
+ snes_ram[RDNMI] |= 0x80; /* Set NMI occured bit */
+
+ if( snes_ram[NMITIMEN] & 0x80 ) /* NMI only signaled if this bit set */
+ {
+ // NMI goes off about 12 cycles after this (otherwise Chrono Trigger, NFL QB Club, etc. lock up)
+ timer_adjust(snes_nmi_timer, ATTOTIME_IN_CYCLES(12, 0), 0, attotime_zero);
+ }
+ }
+
+ // hdma reset happens at scanline 0, H=~6
+ if (snes_ppu.beam.current_vert == 0)
+ {
+ snes_hdma_init();
+ }
+
+ /* three lines after start of vblank we update the controllers (value from snes9x) */
+ if (snes_ppu.beam.current_vert == snes_ppu.beam.last_visible_line + 3)
+ {
+ int i;
+
+ joypad[0].low = readinputport( 0 );
+ joypad[0].high = readinputport( 1 );
+ joypad[1].low = readinputport( 2 );
+ joypad[1].high = readinputport( 3 );
+ joypad[2].low = readinputport( 4 );
+ joypad[2].high = readinputport( 5 );
+ joypad[3].low = readinputport( 6 );
+ joypad[3].high = readinputport( 7 );
+
+ // avoid sending signals that could crash games
+ for (i = 0; i < 4; i++)
+ {
+ // if left, no right
+ if (joypad[i].high & 2) joypad[i].high &= ~1;
+ // if up, no down
+ if (joypad[i].high & 8) joypad[i].high &= ~4;
+ }
+
+ // is automatic reading on?
+ if (snes_ram[NMITIMEN] & 1)
+ {
+ joy1l = joypad[0].low;
+ joy1h = joypad[0].high;
+ joy2l = joypad[1].low;
+ joy2h = joypad[1].high;
+ joy3l = joypad[2].low;
+ joy3h = joypad[2].high;
+ joy4l = joypad[3].low;
+ joy4h = joypad[3].high;
+
+ // make sure oldrol starts returning all 1s because the auto-read reads it :-)
+ joypad[0].oldrol = 16;
+ joypad[1].oldrol = 16;
+ }
+
+ snes_ram[HVBJOY] &= 0xfe; /* Clear busy bit */
+ }
+
+ if( snes_ppu.beam.current_vert == 0 )
+ { /* VBlank is over, time for a new frame */
+ snes_ram[HVBJOY] &= 0x7f; /* Clear vblank bit */
+ snes_ram[RDNMI] &= 0x7f; /* Clear nmi occured bit */
+ snes_ram[STAT77] &= 0x3f; /* Clear Time Over and Range Over bits */
+ snes_ram[STAT78] ^= 0x80; /* Toggle field flag */
+
+ cpunum_set_input_line( 0, G65816_LINE_NMI, CLEAR_LINE );
+ }
+
+ cpuintrf_pop_context();
+
+ timer_adjust(snes_scanline_timer, attotime_never, 0, attotime_never);
+ timer_adjust(snes_hblank_timer, video_screen_get_time_until_pos(0, snes_ppu.beam.current_vert, hblank_offset*snes_htmult), 0, attotime_never);
+}
+
+/* This is called at the start of hblank *before* the scanline indicated in current_vert! */
+static TIMER_CALLBACK( snes_hblank_tick )
+{
+ int nextscan;
+
+ snes_ppu.beam.current_vert = video_screen_get_vpos(0);
+
+ /* make sure we halt */
+ timer_adjust(snes_hblank_timer, attotime_never, 0, attotime_never);
+
+ // we must guarantee the 65816's context for HDMA to work
+ cpuintrf_push_context(0);
+
+ /* draw a scanline */
+ if (snes_ppu.beam.current_vert <= snes_ppu.beam.last_visible_line)
+ {
+ if (video_screen_get_vpos(0) > 0)
+ {
+ /* Do HDMA */
+ if( snes_ram[HDMAEN] )
+ {
+ snes_hdma();
+ }
+
+ video_screen_update_partial(0, snes_ppu.beam.current_vert-1);
+ }
+ }
+
+ cpuintrf_pop_context();
+
+ // signal hblank
+ snes_ram[HVBJOY] |= 0x40;
+
+ /* kick off the start of scanline timer */
+ nextscan = snes_ppu.beam.current_vert + 1;
+ if (nextscan >= (((snes_ram[STAT78] & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC : SNES_VTOTAL_PAL))
+ {
+ nextscan = 0;
+ }
+
+ timer_adjust(snes_scanline_timer, video_screen_get_time_until_pos(0, nextscan, 0), 0, attotime_never);
+}
+
+static void snes_init_ram(void)
+{
+ int i;
+
+ /* Init DSP1 interface */
+ InitDSP1();
+
+ /* Init VRAM */
+ memset( snes_vram, 0, SNES_VRAM_SIZE );
+
+ /* Init Colour RAM */
+ memset( (UINT8 *)snes_cgram, 0, SNES_CGRAM_SIZE );
+
+ /* Init oam RAM */
+ memset( snes_oam, 0xff, SNES_OAM_SIZE );
+
+ /* Init work RAM - 0x55 isn't exactly right but it's close */
+ /* make sure it happens to the 65816 (CPU 0) */
+ cpuintrf_push_context(0);
+ for (i = 0; i < (128*1024); i++)
+ {
+ program_write_byte(0x7e0000 + i, 0x55);
+ }
+ cpuintrf_pop_context();
+
+ /* Inititialize registers/variables */
+ snes_ppu.update_windows = 1;
+ snes_ppu.update_palette = 1;
+ snes_ppu.beam.latch_vert = 0;
+ snes_ppu.beam.latch_horz = 0;
+ snes_ppu.beam.current_vert = 0;
+ snes_ppu.beam.current_horz = 0;
+ snes_ppu.beam.last_visible_line = 240;
+ snes_ppu.mode = 0;
+ cgram_address = 0;
+ vram_read_offset = 2;
+ joy1l = joy1h = joy2l = joy2h = joy3l = joy3h = 0;
+
+ // set up some known register power-up defaults
+ snes_ram[WRIO] = 0xff;
+ snes_ram[VMAIN] = 0x80;
+
+ /* init timers and stop them */
+ snes_scanline_timer = timer_alloc(snes_scanline_tick);
+ timer_adjust(snes_scanline_timer, attotime_never, 0, attotime_never);
+ snes_hblank_timer = timer_alloc(snes_hblank_tick);
+ timer_adjust(snes_hblank_timer, attotime_never, 0, attotime_never);
+ snes_nmi_timer = timer_alloc(snes_nmi_tick);
+ timer_adjust(snes_nmi_timer, attotime_never, 0, attotime_never);
+ snes_hirq_timer = timer_alloc(snes_hirq_tick_callback);
+ timer_adjust(snes_hirq_timer, attotime_never, 0, attotime_never);
+
+ // SNES hcounter has a 0-339 range. hblank starts at counter 260.
+ // clayfighter sets an HIRQ at 260, apparently it wants it to be before hdma kicks off, so we'll delay 2 pixels.
+ hblank_offset = 268;
+ timer_adjust(snes_hblank_timer, video_screen_get_time_until_pos(0, ((snes_ram[STAT78] & 0x10) == SNES_NTSC) ? SNES_VTOTAL_NTSC-1 : SNES_VTOTAL_PAL-1, hblank_offset), 0, attotime_never);
+
+ // check if DSP1 is present (maybe not 100%?)
+ has_dsp1 = ((snes_r_bank1(0xffd6) >= 3) && (snes_r_bank1(0xffd6) <= 5)) ? 1 : 0;
+
+ // init frame counter so first line is 0
+ if( ATTOSECONDS_TO_HZ(Machine->screen[0].refresh) >= 59 )
+ {
+ snes_ppu.beam.current_vert = SNES_VTOTAL_NTSC;
+ }
+ else
+ {
+ snes_ppu.beam.current_vert = SNES_VTOTAL_PAL;
+ }
+}
+
+/* should we treat this as nvram in MAME? */
+static OPBASE_HANDLER(spc_opbase)
+{
+ opcode_base = opcode_arg_base = spc_ram;
+ return ~0;
+}
+
+static OPBASE_HANDLER(snes_opbase)
+{
+ opcode_base = opcode_arg_base = snes_ram;
+ return ~0;
+}
+
+MACHINE_START( snes )
+{
+ snes_vram = auto_malloc(SNES_VRAM_SIZE);
+ snes_cgram = auto_malloc(SNES_CGRAM_SIZE);
+ snes_oam = auto_malloc(SNES_OAM_SIZE);
+ memory_set_opbase_handler(0, snes_opbase);
+ memory_set_opbase_handler(1, spc_opbase);
+
+ // power-on sets these registers like this
+ snes_ram[WRIO] = 0xff;
+ snes_ram[WRMPYA] = 0xff;
+ snes_ram[WRDIVL] = 0xff;
+ snes_ram[WRDIVH] = 0xff;
+}
+
+MACHINE_RESET( snes )
+{
+ snes_init_ram();
+
+ /* Set STAT78 to NTSC or PAL */
+ if( ATTOSECONDS_TO_HZ(machine->screen[0].refresh) >= 59.0f )
+ snes_ram[STAT78] = SNES_NTSC;
+ else /* if( machine->screen[0].refresh == 50 ) */
+ snes_ram[STAT78] = SNES_PAL;
+
+ // reset does this to these registers
+ snes_ram[NMITIMEN] = 0;
+ snes_ram[HTIMEL] = 0xff;
+ snes_ram[HTIMEH] = 0x1;
+ snes_ram[VTIMEL] = 0xff;
+ snes_ram[VTIMEH] = 0x1;
+
+ snes_htmult = 1;
+}
+
+/* Handle reading of Mode 20 SRAM */
+/* 0x700000 - 0x77ffff */
+READ8_HANDLER( snes_r_sram )
+{
+ UINT8 value = 0xff;
+ int mask;
+
+ // limit SRAM size to what's actually present
+ mask = (snes_cart.sram * 1024) - 1;
+ offset &= mask;
+
+ if( snes_cart.sram > 0 )
+ {
+ value = snes_ram[0x700000 + offset];
+ }
+
+ return value;
+}
+
+WRITE8_HANDLER( snes_w_sram )
+{
+ int mask;
+
+ // limit SRAM size to what's actually present
+ mask = (snes_cart.sram * 1024) - 1;
+ offset &= mask;
+
+ if( snes_cart.sram > 0 )
+ {
+ snes_ram[0x700000 + offset] = data;
+ }
+}
+
+/* 0x000000 - 0x2fffff */
+READ8_HANDLER( snes_r_bank1 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if ((snes_cart.mode == SNES_MODE_20) && (has_dsp1))
+ {
+ if ((address >= 0x8000) && (offset >= 0x200000))
+ {
+ return dsp1_read(address);
+ }
+ }
+
+ if( address <= 0x1fff ) /* Mirror of Low RAM */
+ return program_read_byte(0x7e0000 + address );
+ else if( address >= 0x2000 && address <= 0x5fff ) /* I/O */
+ return snes_r_io( address );
+ else if( address >= 0x6000 && address <= 0x7fff ) /* Reserved */
+ {
+ if( snes_cart.mode == SNES_MODE_20 )
+ return 0xff;
+ else
+ return dsp1_read(address);
+ }
+ else
+ {
+ if( snes_cart.mode == SNES_MODE_20 )
+ return snes_ram[offset];
+ else /* MODE_21 */
+ return snes_ram[0xc00000 + offset];
+ }
+
+ return 0xff;
+}
+
+/* 0x300000 - 0x3fffff */
+READ8_HANDLER( snes_r_bank2 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if ((snes_cart.mode == SNES_MODE_20) && (has_dsp1))
+ {
+ if (address >= 0x8000)
+ {
+ return dsp1_read(address);
+ }
+ }
+
+ if( address <= 0x1fff ) /* Mirror of Low RAM */
+ return program_read_byte(0x7e0000 + address );
+ else if( address >= 0x2000 && address <= 0x5fff ) /* I/O */
+ return snes_r_io( address );
+ else if( address >= 0x6000 && address <= 0x7fff )
+ {
+ if( snes_cart.mode == SNES_MODE_20 )
+ return 0xff; /* Reserved */
+ else /* MODE_21 */
+ {
+ int mask;
+
+ offset -= 0x6000;
+
+ // limit SRAM size to what's actually present
+ mask = (snes_cart.sram * 1024) - 1;
+ offset &= mask;
+ return snes_ram[0x306000 + offset]; /* sram */
+ }
+ }
+ else
+ {
+ if( snes_cart.mode == SNES_MODE_20 )
+ return snes_ram[0x300000 + offset];
+ else /* MODE_21 */
+ return snes_ram[0xf00000 + offset];
+ }
+
+ return 0xff;
+}
+
+/* 0x400000 - 0x5fffff */
+READ8_HANDLER( snes_r_bank3 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if( snes_cart.mode == SNES_MODE_20 )
+ {
+ if( address <= 0x7fff )
+ return 0xff; /* Reserved */
+ else
+ return snes_ram[0x400000 + offset];
+ }
+ else /* MODE_21 */
+ {
+ return snes_ram[0x400000 + offset];
+ }
+
+ return 0xff;
+}
+
+/* 0x600000 - 0x6fffff */
+READ8_HANDLER( snes_r_bank6 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if (address < 0x8000)
+ {
+ return dsp1_read(address);
+ }
+
+ return 0xff;
+}
+
+/* 0x800000 - 0xffffff */
+READ8_HANDLER( snes_r_bank4 )
+{
+ if( snes_cart.mode == SNES_MODE_20 )
+ {
+ if( offset <= 0x5fffff )
+ return program_read_byte(offset );
+ else
+ return 0xff;
+ }
+ else /* MODE_21 */
+ {
+ if( offset <= 0x3fffff )
+ return program_read_byte(offset );
+ else
+ return snes_ram[offset + 0x800000];
+ }
+
+ return 0xff;
+}
+
+/* 0x000000 - 0x2fffff */
+WRITE8_HANDLER( snes_w_bank1 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if ((address >= 0x8000) && (offset >= 0x200000))
+ {
+ dsp1_write(address, data);
+ return;
+ }
+
+ if( address <= 0x1fff ) /* Mirror of Low RAM */
+ program_write_byte(0x7e0000 + address, data );
+ else if( address >= 0x2000 && address <= 0x5fff ) /* I/O */
+ snes_w_io( address, data );
+ else if( address >= 0x6000 && address <= 0x7fff ) /* Reserved */
+ if( snes_cart.mode == SNES_MODE_20 )
+ logerror( "Attempt to write to reserved address: %X\n", offset );
+ else
+ {
+ dsp1_write(address, data);
+ return;
+ }
+ else
+ logerror( "Attempt to write to ROM address: %X\n", offset );
+}
+
+/* 0x300000 - 0x3fffff */
+WRITE8_HANDLER( snes_w_bank2 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if (address >= 0x8000)
+ {
+ dsp1_write(address, data);
+ return;
+ }
+
+ if( address <= 0x1fff ) /* Mirror of Low RAM */
+ program_write_byte(0x7e0000 + address, data );
+ else if( address >= 0x2000 && address <= 0x5fff ) /* I/O */
+ snes_w_io( address, data );
+ else if( address >= 0x6000 && address <= 0x7fff )
+ {
+ if( snes_cart.mode == SNES_MODE_20 ) /* Reserved */
+ logerror( "Attempt to write to reserved address: %X\n", offset );
+ else /* MODE_21 */
+ {
+ int mask;
+
+ offset -= 0x6000;
+
+ // limit SRAM size to what's actually present
+ mask = (snes_cart.sram * 1024) - 1;
+ offset &= mask;
+ snes_ram[0x306000 + offset] = data; /* sram */
+ }
+ }
+ else
+ logerror( "Attempt to write to ROM address: %X\n", offset );
+}
+
+/* 0x600000 - 0x6fffff */
+WRITE8_HANDLER( snes_w_bank6 )
+{
+ UINT16 address = offset & 0xffff;
+
+ if (address < 0x8000)
+ {
+ dsp1_write(address, data);
+ return;
+ }
+}
+
+/* 0x800000 - 0xffffff */
+WRITE8_HANDLER( snes_w_bank4 )
+{
+ if( snes_cart.mode == SNES_MODE_20 )
+ {
+ if( offset <= 0x2fffff )
+ snes_w_bank1( offset, data );
+ else if( offset >= 0x300000 && offset <= 0x3fffff )
+ snes_w_bank2( offset - 0x300000, data );
+ }
+ else /* MODE_21 */
+ {
+ if( offset <= 0x2fffff )
+ snes_w_bank1( offset, data );
+ else if( offset >= 0x300000 && offset <= 0x3fffff )
+ snes_w_bank2( offset - 0x300000, data );
+ else
+ logerror( "Attempt to write to ROM address: %X\n", offset );
+ }
+}
+
+/*
+ * DR - Double read : address is read twice to return a 16bit value.
+ * low - This is the low byte of a 16 or 24 bit value
+ * mid - This is the middle byte of a 24 bit value
+ * high - This is the high byte of a 16 or 24 bit value
+ */
+READ8_HANDLER( snes_r_io )
+{
+ UINT8 value = 0;
+
+ // APU is mirrored from 2140 to 217f
+ if (offset >= APU00 && offset < WMDATA)
+ {
+ return spc_port_out[offset & 0x3];
+ }
+
+ /* offset is from 0x000000 */
+ switch( offset )
+ {
+ /* hacks for SimCity 2000 to boot - I presume openbus emulation will fix */
+ case 0x221a:
+ case 0x231c:
+ case 0x241e:
+ return 1;
+ case 0x2017:
+ case 0x221b:
+ case 0x231d:
+ case 0x241f:
+ return 0;
+ case 0x2016:
+ return 2;
+
+ case OAMADDL:
+ case OAMADDH:
+ case VMADDL:
+ case VMADDH:
+ case VMDATAL:
+ case VMDATAH:
+ case CGADD:
+ case CGDATA:
+ return snes_ram[offset];
+ case MPYL: /* Multiplication result (low) */
+ case MPYM: /* Multiplication result (mid) */
+ case MPYH: /* Multiplication result (high) */
+ {
+ /* Perform 16bit * 8bit multiply */
+ INT32 c = snes_ppu.mode7.matrix_a * (snes_ppu.mode7.matrix_b >> 8);
+ snes_ram[MPYL] = c & 0xff;
+ snes_ram[MPYM] = (c >> 8) & 0xff;
+ snes_ram[MPYH] = (c >> 16) & 0xff;
+ return snes_ram[offset];
+ }
+ case SLHV: /* Software latch for H/V counter */
+ snes_latch_counters();
+ return 0x0; /* Return value is meaningless */
+ case ROAMDATA: /* Read data from OAM (DR) */
+ {
+ int oam_addr = snes_ppu.oam.address;
+
+ if (oam_addr & 0x100)
+ {
+ oam_addr &= 0x10f;
+ }
+ else
+ {
+ oam_addr &= 0x1ff;
+ }
+
+ value = (snes_oam[oam_addr] >> (snes_ram[OAMDATA] << 3)) & 0xff;
+ snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
+ if( snes_ram[OAMDATA] == 0 )
+ {
+ snes_ppu.oam.address++;
+ snes_ppu.oam.address_low = snes_ram[OAMADDL] = snes_ppu.oam.address & 0xff;
+ snes_ppu.oam.address_high = snes_ram[OAMADDH] = (snes_ppu.oam.address >> 8) & 0x1;
+ }
+ return value;
+ }
+ case RVMDATAL: /* Read data from VRAM (low) */
+ {
+ UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
+
+ value = vram_read_buffer & 0xff;
+
+ if (!vram_fgr_high)
+ {
+ if (vram_fgr_count)
+ {
+ UINT32 rem = addr & vram_fgr_mask;
+ UINT32 faddr = (addr & ~vram_fgr_mask) + (rem >> vram_fgr_shift) +
+ ((rem & (vram_fgr_count - 1)) << 3);
+
+ vram_read_buffer = snes_vram[(faddr<<1)&0xffff] | snes_vram[((faddr<<1)+1) & 0xffff]<<8;
+ }
+ else
+ {
+ vram_read_buffer = snes_vram[(addr<<1)&0xffff] | snes_vram[((addr<<1)+1) & 0xffff]<<8;
+ }
+
+ addr += vram_fgr_increment;
+ snes_ram[VMADDL] = addr&0xff;
+ snes_ram[VMADDH] = (addr>>8)&0xff;
+ }
+ }
+ return value;
+ case RVMDATAH: /* Read data from VRAM (high) */
+ {
+ UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
+
+ value = (vram_read_buffer>>8) & 0xff;
+
+ if (vram_fgr_high)
+ {
+ if (vram_fgr_count)
+ {
+ UINT32 rem = addr & vram_fgr_mask;
+ UINT32 faddr = (addr & ~vram_fgr_mask) + (rem >> vram_fgr_shift) +
+ ((rem & (vram_fgr_count - 1)) << 3);
+
+ vram_read_buffer = snes_vram[(faddr<<1)&0xffff] | snes_vram[((faddr<<1)+1) & 0xffff]<<8;
+ }
+ else
+ {
+ vram_read_buffer = snes_vram[(addr<<1)&0xffff] | snes_vram[((addr<<1)+1) & 0xffff]<<8;
+ }
+
+ addr += vram_fgr_increment;
+ snes_ram[VMADDL] = addr&0xff;
+ snes_ram[VMADDH] = (addr>>8)&0xff;
+ }
+ }
+ return value;
+ case RCGDATA: /* Read data from CGRAM */
+ value = ((UINT8 *)snes_cgram)[cgram_address];
+ cgram_address = (cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
+ return value;
+ case OPHCT: /* Horizontal counter data by ext/soft latch */
+ {
+ /* FIXME: need to handle STAT78 reset */
+ if( read_ophct )
+ {
+ value = (snes_ppu.beam.latch_horz >> 8) & 0x1;
+ read_ophct = 0;
+ }
+ else
+ {
+ value = snes_ppu.beam.latch_horz & 0xff;
+ read_ophct = 1;
+ }
+ return value;
+ }
+ case OPVCT: /* Vertical counter data by ext/soft latch */
+ {
+ /* FIXME: need to handle STAT78 reset */
+ if( read_opvct )
+ {
+ value = (snes_ppu.beam.latch_vert >> 8) & 0x1;
+ read_opvct = 0;
+ }
+ else
+ {
+ value = snes_ppu.beam.latch_vert & 0xff;
+ read_opvct = 1;
+ }
+ return value;
+ }
+ case STAT77: /* PPU status flag and version number */
+ return snes_ram[offset];
+ case STAT78: /* PPU status flag and version number */
+ /* FIXME: need to reset OPHCT and OPVCT */
+ value = snes_ram[offset];
+ snes_ram[offset] &= ~0x40; // clear 'latched counters' flag
+ return value;
+ case WMDATA: /* Data to read from WRAM */
+ {
+ UINT32 addr = ((snes_ram[WMADDH] & 0x1) << 16) | (snes_ram[WMADDM] << 8) | snes_ram[WMADDL];
+
+ value = program_read_byte(0x7e0000 + addr++);
+ addr &= 0x1ffff;
+ snes_ram[WMADDH] = (addr >> 16) & 0x1;
+ snes_ram[WMADDM] = (addr >> 8) & 0xff;
+ snes_ram[WMADDL] = addr & 0xff;
+ return value;
+ }
+ case WMADDL: /* Address to read/write to wram (low) */
+ case WMADDM: /* Address to read/write to wram (mid) */
+ case WMADDH: /* Address to read/write to wram (high) */
+ return snes_ram[offset];
+ case OLDJOY1: /* Data for old NES controllers */
+ {
+ if( snes_ram[offset] & 0x1 )
+ {
+ return 0;
+ }
+ value = ((joypad[0].low | (joypad[0].high << 8) | 0x10000) >> (15 - (joypad[0].oldrol++ % 16))) & 0x1;
+ if( !(joypad[0].oldrol % 17) )
+ value = 0x1;
+ return value;
+ }
+ case OLDJOY2: /* Data for old NES controllers */
+ {
+ if( snes_ram[OLDJOY1] & 0x1 )
+ {
+ return 0;
+ }
+ value = ((joypad[1].low | (joypad[1].high << 8) | 0x10000) >> (15 - (joypad[1].oldrol++ % 16))) & 0x1;
+ if( !(joypad[1].oldrol % 17) )
+ value = 0x1;
+ value |= 0x1c; // bits 4, 3, and 2 are always set
+ return value;
+ }
+ case HTIMEL:
+ case HTIMEH:
+ case VTIMEL:
+ case VTIMEH:
+ return snes_ram[offset];
+ case MDMAEN: /* GDMA channel designation and trigger */
+ /* FIXME: Is this really read-only? - Villgust needs to read it */
+ return snes_ram[offset];
+ case RDNMI: /* NMI flag by v-blank and version number */
+ value = snes_ram[offset];
+ snes_ram[offset] &= 0x7f; /* NMI flag is reset on read */
+ return value;
+ case TIMEUP: /* IRQ flag by H/V count timer */
+ value = snes_ram[offset];
+ snes_ram[offset] = 0; /* Register is reset on read */
+ return value;
+ case HVBJOY: /* H/V blank and joypad controller enable */
+ // electronics test says hcounter 272 is start of hblank, which is beampos 363
+// if (video_screen_get_hpos(0) >= 363) snes_ram[offset] |= 0x40;
+// else snes_ram[offset] &= ~0x40;
+ return snes_ram[offset];
+ case RDIO: /* Programmable I/O port - echos back what's written to WRIO */
+ return snes_ram[WRIO];
+ break;
+ case RDDIVL: /* Quotient of divide result (low) */
+ case RDDIVH: /* Quotient of divide result (high) */
+ case RDMPYL: /* Product/Remainder of mult/div result (low) */
+ case RDMPYH: /* Product/Remainder of mult/div result (high) */
+ return snes_ram[offset];
+ case JOY1L: /* Joypad 1 status register (low) */
+ return joypad[0].low;
+ case JOY1H: /* Joypad 1 status register (high) */
+ return joypad[0].high;
+ case JOY2L: /* Joypad 2 status register (low) */
+ return joypad[1].low;
+ case JOY2H: /* Joypad 2 status register (high) */
+ return joypad[1].high;
+ case JOY3L: /* Joypad 3 status register (low) */
+ return joypad[2].low;
+ case JOY3H: /* Joypad 3 status register (high) */
+ return joypad[2].high;
+ case JOY4L: /* Joypad 4 status register (low) */
+ return joypad[3].low;
+ case JOY4H: /* Joypad 4 status register (high) */
+ return joypad[3].high;
+ case DMAP0: case BBAD0: case A1T0L: case A1T0H: case A1B0: case DAS0L:
+ case DAS0H: case DSAB0: case A2A0L: case A2A0H: case NTRL0:
+ case DMAP1: case BBAD1: case A1T1L: case A1T1H: case A1B1: case DAS1L:
+ case DAS1H: case DSAB1: case A2A1L: case A2A1H: case NTRL1:
+ case DMAP2: case BBAD2: case A1T2L: case A1T2H: case A1B2: case DAS2L:
+ case DAS2H: case DSAB2: case A2A2L: case A2A2H: case NTRL2:
+ case DMAP3: case BBAD3: case A1T3L: case A1T3H: case A1B3: case DAS3L:
+ case DAS3H: case DSAB3: case A2A3L: case A2A3H: case NTRL3:
+ case DMAP4: case BBAD4: case A1T4L: case A1T4H: case A1B4: case DAS4L:
+ case DAS4H: case DSAB4: case A2A4L: case A2A4H: case NTRL4:
+ case DMAP5: case BBAD5: case A1T5L: case A1T5H: case A1B5: case DAS5L:
+ case DAS5H: case DSAB5: case A2A5L: case A2A5H: case NTRL5:
+ case DMAP6: case BBAD6: case A1T6L: case A1T6H: case A1B6: case DAS6L:
+ case DAS6H: case DSAB6: case A2A6L: case A2A6H: case NTRL6:
+ case DMAP7: case BBAD7: case A1T7L: case A1T7H: case A1B7: case DAS7L:
+ case DAS7H: case DSAB7: case A2A7L: case A2A7H: case NTRL7:
+ return snes_ram[offset];
+
+#ifndef MESS
+ case 0x4100: /* NSS Dip-Switches */
+#ifdef MAME_DEBUG
+ return readinputport(12);
+#else
+ return readinputport(9);
+#endif /* MAME_DEBUG */
+// case 0x4101: //PC: a104 - a10e - a12a //only nss_actr
+// case 0x420c: //PC: 9c7d - 8fab //only nss_ssoc
+
+ default:
+ mame_printf_debug("snes_r: offset = %x pc = %x\n",offset,activecpu_get_pc());
+#endif /* MESS */
+
+ }
+
+ /* Unsupported reads return 0xff */
+ return 0xff;
+}
+
+/*
+ * DW - Double write : address is written twice to set a 16bit value.
+ * low - This is the low byte of a 16 or 24 bit value
+ * mid - This is the middle byte of a 24 bit value
+ * high - This is the high byte of a 16 or 24 bit value
+ */
+WRITE8_HANDLER( snes_w_io )
+{
+ // APU is mirrored from 2140 to 217f
+ if (offset >= APU00 && offset < WMDATA)
+ {
+// printf("816: %02x to APU @ %d\n", data, offset&3);
+ spc_port_in[offset & 0x3] = data;
+ cpu_boost_interleave(attotime_zero, ATTOTIME_IN_USEC(20));
+ return;
+ }
+
+ /* offset is from 0x000000 */
+ switch( offset )
+ {
+ case INIDISP: /* Initial settings for screen */
+ snes_ppu.update_palette = 1;
+ break;
+ case OBSEL: /* Object size and data area designation */
+ snes_ppu.layer[4].data = ((data & 0x3) * 0x2000) << 1;
+ snes_ppu.oam.name_select = (((data & 0x18)>>3) * 0x1000) << 1;
+ /* Determine object size */
+ switch( (data & 0xe0) >> 5 )
+ {
+ case 0: /* 8 & 16 */
+ snes_ppu.oam.size[0] = 1;
+ snes_ppu.oam.size[1] = 2;
+ break;
+ case 1: /* 8 & 32 */
+ snes_ppu.oam.size[0] = 1;
+ snes_ppu.oam.size[1] = 4;
+ break;
+ case 2: /* 8 & 64 */
+ snes_ppu.oam.size[0] = 1;
+ snes_ppu.oam.size[1] = 8;
+ break;
+ case 3: /* 16 & 32 */
+ snes_ppu.oam.size[0] = 2;
+ snes_ppu.oam.size[1] = 4;
+ break;
+ case 4: /* 16 & 64 */
+ snes_ppu.oam.size[0] = 2;
+ snes_ppu.oam.size[1] = 8;
+ break;
+ case 5: /* 32 & 64 */
+ snes_ppu.oam.size[0] = 4;
+ snes_ppu.oam.size[1] = 8;
+ break;
+ default:
+ /* Unknown size so default to 8 & 16 */
+ snes_ppu.oam.size[0] = 1;
+ snes_ppu.oam.size[1] = 2;
+#ifdef SNES_DBG_REG_W
+ mame_printf_debug( "Object size unsupported: %d\n", (data & 0xe0) >> 5 );
+#endif
+ }
+ break;
+ case OAMADDL: /* Address for accessing OAM (low) */
+ snes_ppu.oam.address_low = data;
+ snes_ppu.oam.address = ((snes_ppu.oam.address_high & 0x1) << 8) + data;
+ snes_ram[OAMDATA] = 0;
+ break;
+ case OAMADDH: /* Address for accessing OAM (high) */
+ snes_ppu.oam.address_high = data & 0x1;
+ snes_ppu.oam.address = ((data & 0x1) << 8) + snes_ppu.oam.address_low;
+ snes_ppu.oam.priority_rotation = (data & 0x80) ? 1 : 0;
+ snes_ram[OAMDATA] = 0;
+ break;
+ case OAMDATA: /* Data for OAM write (DW) */
+ {
+ int oam_addr = snes_ppu.oam.address;
+
+ if (oam_addr >= 0x100)
+ {
+ oam_addr &= 0x10f;
+ if (!(snes_ram[OAMDATA]))
+ {
+ snes_oam[oam_addr] &= 0xff00;
+ snes_oam[oam_addr] |= data;
+ }
+ else
+ {
+ snes_oam[oam_addr] &= 0x00ff;
+ snes_oam[oam_addr] |= (data<<8);
+ }
+ }
+ else
+ {
+ oam_addr &= 0x1ff;
+ if (!(snes_ram[OAMDATA]))
+ {
+ snes_ppu.oam.write_latch = data;
+ }
+ else
+ {
+ snes_oam[oam_addr] = (data<<8) | snes_ppu.oam.write_latch;
+ }
+ }
+ snes_ram[OAMDATA] = (snes_ram[OAMDATA] + 1) % 2;
+ if( snes_ram[OAMDATA] == 0 )
+ {
+ snes_ram[OAMDATA] = 0;
+ snes_ppu.oam.address++;
+ snes_ppu.oam.address_low = snes_ram[OAMADDL] = snes_ppu.oam.address & 0xff;
+ snes_ppu.oam.address_high = snes_ram[OAMADDH] = (snes_ppu.oam.address >> 8) & 0x1;
+ }
+ return;
+ }
+ case BGMODE: /* BG mode and character size settings */
+ snes_ppu.mode = data & 0x7;
+ {
+ screen_state *state = &Machine->screen[0];
+ rectangle visarea = state->visarea;
+
+ visarea.min_x = visarea.min_y = 0;
+ visarea.max_y = snes_ppu.beam.last_visible_line - 1;
+
+ // fixme: should compensate for SNES_DBG_video
+ if( snes_ppu.mode == 5 || snes_ppu.mode == 6 )
+ {
+ visarea.max_x = (SNES_SCR_WIDTH * 2) - 1;
+ snes_htmult = 2;
+ }
+ else
+ {
+ visarea.max_x = SNES_SCR_WIDTH - 1;
+ snes_htmult = 1;
+ }
+
+ if ((snes_ram[STAT78] & 0x10) == SNES_NTSC)
+ {
+ video_screen_configure(0, SNES_HTOTAL*snes_htmult, SNES_VTOTAL_NTSC, &visarea, Machine->screen[0].refresh);
+ }
+ else
+ {
+ video_screen_configure(0, SNES_HTOTAL*snes_htmult, SNES_VTOTAL_PAL, &visarea, Machine->screen[0].refresh);
+ }
+ }
+
+ snes_ppu.layer[0].tile_size = (data >> 4) & 0x1;
+ snes_ppu.layer[1].tile_size = (data >> 5) & 0x1;
+ snes_ppu.layer[2].tile_size = (data >> 6) & 0x1;
+ snes_ppu.layer[3].tile_size = (data >> 7) & 0x1;
+ snes_ppu.update_offsets = 1;
+ break;
+ case MOSAIC: /* Size and screen designation for mosaic */
+ /* FIXME: We don't support horizontal mosaic yet */
+ break;
+ case BG1SC: /* Address for storing SC data BG1 SC size designation */
+ case BG2SC: /* Address for storing SC data BG2 SC size designation */
+ case BG3SC: /* Address for storing SC data BG3 SC size designation */
+ case BG4SC: /* Address for storing SC data BG4 SC size designation */
+ snes_ppu.layer[offset - BG1SC].map = (data & 0xfc) << 9;
+ snes_ppu.layer[offset - BG1SC].map_size = data & 0x3;
+ break;
+ case BG12NBA: /* Address for BG 1 and 2 character data */
+ snes_ppu.layer[0].data = (data & 0xf) << 13;
+ snes_ppu.layer[1].data = (data & 0xf0) << 9;
+ break;
+ case BG34NBA: /* Address for BG 3 and 4 character data */
+ snes_ppu.layer[2].data = (data & 0xf) << 13;
+ snes_ppu.layer[3].data = (data & 0xf0) << 9;
+ break;
+
+ // Anomie says "Current = (Byte<<8) | (Prev&~7) | ((Current>>8)&7);"
+ case BG1HOFS: /* BG1 - horizontal scroll (DW) */
+ snes_ppu.layer[0].offset.horizontal = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[0].offset.horizontal>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG1VOFS: /* BG1 - vertical scroll (DW) */
+ snes_ppu.layer[0].offset.vertical = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[0].offset.vertical>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG2HOFS: /* BG2 - horizontal scroll (DW) */
+ snes_ppu.layer[1].offset.horizontal = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[1].offset.horizontal>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG2VOFS: /* BG2 - vertical scroll (DW) */
+ snes_ppu.layer[1].offset.vertical = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[1].offset.vertical>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG3HOFS: /* BG3 - horizontal scroll (DW) */
+ snes_ppu.layer[2].offset.horizontal = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[2].offset.horizontal>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG3VOFS: /* BG3 - vertical scroll (DW) */
+ snes_ppu.layer[2].offset.vertical = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[2].offset.vertical>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG4HOFS: /* BG4 - horizontal scroll (DW) */
+ snes_ppu.layer[3].offset.horizontal = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[3].offset.horizontal>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case BG4VOFS: /* BG4 - vertical scroll (DW) */
+ snes_ppu.layer[3].offset.vertical = (data<<8) | (ppu_last_scroll & ~7) | ((snes_ppu.layer[3].offset.vertical>>8) & 7);
+ ppu_last_scroll = data;
+ snes_ppu.update_offsets = 1;
+ return;
+ case VMAIN: /* VRAM address increment value designation */
+ vram_fgr_high = (data & 0x80);
+ vram_fgr_increment = vram_fgr_inctab[data & 3];
+
+ if (data & 0xc)
+ {
+ int md = (data & 0xc)>>2;
+
+ vram_fgr_count = vram_fgr_inccnts[md];
+ vram_fgr_mask = (vram_fgr_count*8)-1;
+ vram_fgr_shift = vram_fgr_shiftab[md];
+ }
+ else
+ {
+ vram_fgr_count = 0;
+ }
+// printf("VMAIN: high %x inc %x count %x mask %x shift %x\n", vram_fgr_high, vram_fgr_increment, vram_fgr_count, vram_fgr_mask, vram_fgr_shift);
+ break;
+ case VMADDL: /* Address for VRAM read/write (low) */
+ case VMADDH: /* Address for VRAM read/write (high) */
+ {
+ UINT32 addr;
+
+ snes_ram[offset] = data;
+ addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
+
+ if (vram_fgr_count)
+ {
+ UINT32 rem = addr & vram_fgr_mask;
+ UINT32 faddr = (addr & ~vram_fgr_mask) + (rem >> vram_fgr_shift) +
+ ((rem & (vram_fgr_count - 1)) << 3);
+
+ vram_read_buffer = snes_vram[(faddr<<1)&0xffff] | snes_vram[((faddr<<1)+1) & 0xffff]<<8;
+ }
+ else
+ {
+ vram_read_buffer = snes_vram[(addr<<1)&0xffff] | snes_vram[((addr<<1)+1) & 0xffff]<<8;
+ }
+
+ }
+ break;
+ case VMDATAL: /* 2118: Data for VRAM write (low) */
+ {
+ UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
+
+ if (vram_fgr_count)
+ {
+ UINT32 rem = addr & vram_fgr_mask;
+ UINT32 faddr = (addr & ~vram_fgr_mask) + (rem >> vram_fgr_shift) +
+ ((rem & (vram_fgr_count - 1)) << 3);
+
+ snes_vram[(faddr<<1)&0xffff] = data;
+ }
+ else
+ {
+ snes_vram[(addr<<1)&0xffff] = data;
+ }
+
+ if (!vram_fgr_high)
+ {
+ addr += vram_fgr_increment;
+ snes_ram[VMADDL] = addr&0xff;
+ snes_ram[VMADDH] = (addr>>8)&0xff;
+ }
+ }
+ return;
+ case VMDATAH: /* 2119: Data for VRAM write (high) */
+ {
+ UINT32 addr = (snes_ram[VMADDH] << 8) | snes_ram[VMADDL];
+
+ if (vram_fgr_count)
+ {
+ UINT32 rem = addr & vram_fgr_mask;
+ UINT32 faddr = (addr & ~vram_fgr_mask) + (rem >> vram_fgr_shift) +
+ ((rem & (vram_fgr_count - 1)) << 3);
+
+ snes_vram[((faddr<<1)+1)&0xffff] = data;
+ }
+ else
+ {
+ snes_vram[((addr<<1)+1)&0xffff] = data;
+ }
+
+ if (vram_fgr_high)
+ {
+ addr += vram_fgr_increment;
+ snes_ram[VMADDL] = addr&0xff;
+ snes_ram[VMADDH] = (addr>>8)&0xff;
+ }
+ }
+ return;
+ case M7SEL: /* Mode 7 initial settings */
+ break;
+ case M7A: /* Mode 7 COS angle/x expansion (DW) */
+ snes_ppu.mode7.matrix_a = ((snes_ppu.mode7.matrix_a >> 8) & 0xff) + (data << 8);
+ break;
+ case M7B: /* Mode 7 SIN angle/ x expansion (DW) */
+ snes_ppu.mode7.matrix_b = ((snes_ppu.mode7.matrix_b >> 8) & 0xff) + (data << 8);
+ break;
+ case M7C: /* Mode 7 SIN angle/y expansion (DW) */
+ snes_ppu.mode7.matrix_c = ((snes_ppu.mode7.matrix_c >> 8) & 0xff) + (data << 8);
+ break;
+ case M7D: /* Mode 7 COS angle/y expansion (DW) */
+ snes_ppu.mode7.matrix_d = ((snes_ppu.mode7.matrix_d >> 8) & 0xff) + (data << 8);
+ break;
+ case M7X: /* Mode 7 x center position (DW) */
+ snes_ppu.mode7.origin_x = ((snes_ppu.mode7.origin_x >> 8) & 0xff) + (data << 8);
+ break;
+ case M7Y: /* Mode 7 y center position (DW) */
+ snes_ppu.mode7.origin_y = ((snes_ppu.mode7.origin_y >> 8) & 0xff) + (data << 8);
+ break;
+ case CGADD: /* Initial address for colour RAM writing */
+ /* CGRAM is 16-bit, but when reading/writing we treat it as
+ * 8-bit, so we need to double the address */
+ cgram_address = data << 1;
+ break;
+ case CGDATA: /* Data for colour RAM */
+ ((UINT8 *)snes_cgram)[cgram_address] = data;
+ cgram_address = (cgram_address + 1) % (SNES_CGRAM_SIZE - 2);
+ snes_ppu.update_palette = 1;
+ break;
+ case W12SEL: /* Window mask settings for BG1-2 */
+ case W34SEL: /* Window mask settings for BG3-4 */
+ case WOBJSEL: /* Window mask settings for objects */
+ case WH0: /* Window 1 left position */
+ case WH1: /* Window 1 right position */
+ case WH2: /* Window 2 left position */
+ case WH3: /* Window 2 right position */
+ case WBGLOG: /* Window mask logic for BG's */
+ case WOBJLOG: /* Window mask logic for objects */
+ if( data != snes_ram[offset] )
+ snes_ppu.update_windows = 1;
+ break;
+ case TM: /* Main screen designation */
+ case TS: /* Subscreen designation */
+ case TMW: /* Window mask for main screen designation */
+ case TSW: /* Window mask for subscreen designation */
+ break;
+ case CGWSEL: /* Initial settings for Fixed colour addition or screen addition */
+ /* FIXME: We don't support direct select for modes 3 & 4 or subscreen window stuff */
+#ifdef SNES_DBG_REG_W
+ if( (data & 0x2) != (snes_ram[CGWSEL] & 0x2) )
+ mame_printf_debug( "Add/Sub Layer: %s\n", ((data & 0x2) >> 1) ? "Subscreen" : "Fixed colour" );
+#endif
+ break;
+ case CGADSUB: /* Addition/Subtraction designation for each screen */
+ {
+ UINT8 sub = (data & 0x80) >> 7;
+ snes_ppu.layer[0].blend = (data & 0x1) << sub;
+ snes_ppu.layer[1].blend = ((data & 0x2) >> 1) << sub;
+ snes_ppu.layer[2].blend = ((data & 0x4) >> 2) << sub;
+ snes_ppu.layer[3].blend = ((data & 0x8) >> 3) << sub;
+ snes_ppu.layer[4].blend = ((data & 0x10) >> 4) << sub;
+ } break;
+ case COLDATA: /* Fixed colour data for fixed colour addition/subtraction */
+ {
+ /* Store it in the extra space we made in the CGRAM
+ * It doesn't really go there, but it's as good a place as any. */
+ UINT8 r,g,b,fade;
+
+ /* Get existing value. */
+ r = snes_cgram[FIXED_COLOUR] & 0x1f;
+ g = (snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5;
+ b = (snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10;
+ /* Set new value */
+ if( data & 0x20 )
+ r = data & 0x1f;
+ if( data & 0x40 )
+ g = data & 0x1f;
+ if( data & 0x80 )
+ b = data & 0x1f;
+ snes_cgram[FIXED_COLOUR] = (r | (g << 5) | (b << 10));
+
+ /* set the palette entry, adjusting to the fade setting */
+ fade = (snes_ram[INIDISP] & 0xf) + 1;
+ r = (r * fade) >> 4;
+ g = (g * fade) >> 4;
+ b = (b * fade) >> 4;
+ /* palette hacks! */
+ ((pen_t *)Machine->remapped_colortable)[FIXED_COLOUR] = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10));
+ } break;
+ case SETINI: /* Screen mode/video select */
+ /* FIXME: We only support line count here */
+ snes_ppu.beam.last_visible_line = (data & 0x4) ? 240 : 225;
+#ifdef SNES_DBG_REG_W
+ if( (data & 0x8) != (snes_ram[SETINI] & 0x8) )
+ mame_printf_debug( "Pseudo 512 mode: %s\n", (data & 0x8) ? "on" : "off" );
+#endif
+ break;
+ case WMDATA: /* Data to write to WRAM */
+ {
+ UINT32 addr = ((snes_ram[WMADDH] & 0x1) << 16) | (snes_ram[WMADDM] << 8) | snes_ram[WMADDL];
+ program_write_byte(0x7e0000 + addr++, data );
+ addr &= 0x1ffff;
+ snes_ram[WMADDH] = (addr >> 16) & 0x1;
+ snes_ram[WMADDM] = (addr >> 8) & 0xff;
+ snes_ram[WMADDL] = addr & 0xff;
+ return;
+ }
+ case WMADDL: /* Address to read/write to wram (low) */
+ case WMADDM: /* Address to read/write to wram (mid) */
+ case WMADDH: /* Address to read/write to wram (high) */
+ break;
+ case OLDJOY1: /* Old NES joystick support */
+ if( (data & 0x1) && !(snes_ram[offset] & 0x1) )
+ {
+ joypad[0].oldrol = 0;
+ joypad[1].oldrol = 0;
+ joypad[2].oldrol = 0;
+ joypad[3].oldrol = 0;
+ }
+ break;
+ case NMITIMEN: /* Flag for v-blank, timer int. and joy read */
+ case OLDJOY2: /* Old NES joystick support */
+ break;
+ case WRIO: /* Programmable I/O port - latches H/V counters on a 1->0 transition */
+ if (!(snes_ram[WRIO] & 0x80) && (data & 0x80))
+ {
+ // external latch
+ snes_latch_counters();
+ }
+ case WRMPYA: /* Multiplier A */
+ break;
+ case WRMPYB: /* Multiplier B */
+ {
+ UINT32 c = snes_ram[WRMPYA] * data;
+ snes_ram[RDMPYL] = c & 0xff;
+ snes_ram[RDMPYH] = (c >> 8) & 0xff;
+ } break;
+ case WRDIVL: /* Dividend (low) */
+ case WRDIVH: /* Dividend (high) */
+ break;
+ case WRDVDD: /* Divisor */
+ {
+ UINT16 value, dividend, remainder;
+ dividend = remainder = 0;
+ value = (snes_ram[WRDIVH] << 8) + snes_ram[WRDIVL];
+ if( data > 0 )
+ {
+ dividend = value / data;
+ remainder = value % data;
+ }
+ else
+ {
+ dividend = 0xffff;
+ remainder = value;
+ }
+ snes_ram[RDDIVL] = dividend & 0xff;
+ snes_ram[RDDIVH] = (dividend >> 8) & 0xff;
+ snes_ram[RDMPYL] = remainder & 0xff;
+ snes_ram[RDMPYH] = (remainder >> 8) & 0xff;
+ } break;
+ case HTIMEL: /* H-Count timer settings (low) */
+ case HTIMEH: /* H-Count timer settings (high) */
+ case VTIMEL: /* V-Count timer settings (low) */
+ case VTIMEH: /* V-Count timer settings (high) */
+ break;
+ case MDMAEN: /* GDMA channel designation and trigger */
+ snes_gdma( data );
+ data = 0; /* Once DMA is done we need to reset all bits to 0 */
+ break;
+ case HDMAEN: /* HDMA channel designation */
+ break;
+ case MEMSEL: /* Access cycle designation in memory (2) area */
+ /* FIXME: Need to adjust the speed only during access of banks 0x80+
+ * Currently we are just increasing it no matter what */
+// cpunum_set_clockscale( 0, (data & 0x1) ? 1.335820896 : 1.0 );
+#ifdef SNES_DBG_REG_W
+ if( (data & 0x1) != (snes_ram[MEMSEL] & 0x1) )
+ mame_printf_debug( "CPU speed: %f Mhz\n", (data & 0x1) ? 3.58 : 2.68 );
+#endif
+ break;
+ /* Following are read-only */
+ case HVBJOY: /* H/V blank and joypad enable */
+ case MPYL: /* Multiplication result (low) */
+ case MPYM: /* Multiplication result (mid) */
+ case MPYH: /* Multiplication result (high) */
+ case RDIO:
+ case RDDIVL:
+ case RDDIVH:
+ case RDMPYL:
+ case RDMPYH:
+ case JOY1L:
+ case JOY1H:
+ case JOY2L:
+ case JOY2H:
+ case JOY3L:
+ case JOY3H:
+ case JOY4L:
+ case JOY4H:
+#ifdef MAME_DEBUG
+ logerror( "Write to read-only register: %X value: %X", offset, data );
+#endif /* MAME_DEBUG */
+ return;
+ /* Below is all DMA related */
+ case DMAP0: case BBAD0: case A1T0L: case A1T0H: case A1B0: case DAS0L:
+ case DAS0H: case DSAB0: case A2A0L: case A2A0H: case NTRL0:
+ case DMAP1: case BBAD1: case A1T1L: case A1T1H: case A1B1: case DAS1L:
+ case DAS1H: case DSAB1: case A2A1L: case A2A1H: case NTRL1:
+ case DMAP2: case BBAD2: case A1T2L: case A1T2H: case A1B2: case DAS2L:
+ case DAS2H: case DSAB2: case A2A2L: case A2A2H: case NTRL2:
+ case DMAP3: case BBAD3: case A1T3L: case A1T3H: case A1B3: case DAS3L:
+ case DAS3H: case DSAB3: case A2A3L: case A2A3H: case NTRL3:
+ case DMAP4: case BBAD4: case A1T4L: case A1T4H: case A1B4: case DAS4L:
+ case DAS4H: case DSAB4: case A2A4L: case A2A4H: case NTRL4:
+ case DMAP5: case BBAD5: case A1T5L: case A1T5H: case A1B5: case DAS5L:
+ case DAS5H: case DSAB5: case A2A5L: case A2A5H: case NTRL5:
+ case DMAP6: case BBAD6: case A1T6L: case A1T6H: case A1B6: case DAS6L:
+ case DAS6H: case DSAB6: case A2A6L: case A2A6H: case NTRL6:
+ case DMAP7: case BBAD7: case A1T7L: case A1T7H: case A1B7: case DAS7L:
+ case DAS7H: case DSAB7: case A2A7L: case A2A7H: case NTRL7:
+ break;
+ }
+
+ snes_ram[offset] = data;
+}
+
+/* This function checks everything is in a valid range and returns how
+ * 'valid' this section is as an information block. */
+int snes_validate_infoblock( UINT8 *infoblock, UINT16 offset )
+{
+ INT8 valid = 6;
+
+ /* Check the CRC and inverse CRC */
+ if( ((infoblock[offset + 0x1c] + (infoblock[offset + 0x1d] << 8)) |
+ (infoblock[offset + 0x1e] + (infoblock[offset + 0x1f] << 8))) != 0xffff )
+ {
+ valid -= 3;
+ }
+ /* Check the ROM Size is in a valid range */
+ if( infoblock[offset + 0x17] > 13 )
+ {
+ valid--;
+ }
+ /* Check the SRAM size */
+ if( infoblock[offset + 0x18] > 8 )
+ {
+ valid--;
+ }
+ /* Check the Country is in a valid range */
+ if( infoblock[offset + 0x19] > 13 )
+ {
+ valid--;
+ }
+ /* Check the game version */
+ if( infoblock[offset + 0x1b] >= 128 )
+ {
+ valid--;
+ }
+
+ if( valid < 0 )
+ {
+ valid = 0;
+ }
+
+ return valid;
+}
+
+#ifndef MESS
+/* for mame we use an init, maybe we will need more for the different games */
+DRIVER_INIT( snes )
+{
+ int i;
+ UINT16 totalblocks, readblocks;
+ UINT8 *rom;
+
+ rom = memory_region( REGION_USER3 );
+ snes_ram = auto_malloc(0x1000000);
+ memset( snes_ram, 0, 0x1000000 );
+
+ /* all NSS games seem to use MODE 20 */
+ snes_cart.mode = SNES_MODE_20;
+ snes_cart.sram_max = 0x40000;
+
+ /* Find the number of blocks in this ROM */
+ //totalblocks = ((mame_fsize(file) - offset) >> (snes_cart.mode == MODE_20 ? 15 : 16));
+ totalblocks = (memory_region_length(REGION_USER3) / 0x8000) - 1;
+
+ /* FIXME: Insert crc check here */
+
+ readblocks = 0;
+ {
+ /* In mode 20, all blocks are 32kb. There are upto 96 blocks, giving a
+ * total of 24mbit(3mb) of ROM.
+ * The first 48 blocks are located in banks 0x00 to 0x2f at address
+ * 0x8000. They are mirrored in banks 0x80 to 0xaf.
+ * The next 16 blocks are located in banks 0x30 to 0x3f at address
+ * 0x8000. They are mirrored in banks 0xb0 to 0xbf.
+ * The final 32 blocks are located in banks 0x40 - 0x5f at address
+ * 0x8000. They are mirrored in banks 0xc0 to 0xdf.
+ */
+ i = 0;
+ while( i < 96 && readblocks <= totalblocks )
+ {
+ //mame_fread( file, &snes_ram[(i++ * 0x10000) + 0x8000], 0x8000);
+ memcpy(&snes_ram[(i * 0x10000) + 0x8000], &rom[i * 0x8000], 0x8000);
+ i++;
+ readblocks++;
+ }
+ }
+
+ /* Find the amount of sram */
+ snes_cart.sram = snes_r_bank1(0x00ffd8);
+ if( snes_cart.sram > 0 )
+ {
+ snes_cart.sram = ((1 << (snes_cart.sram + 3)) / 8);
+ if( snes_cart.sram > snes_cart.sram_max )
+ snes_cart.sram = snes_cart.sram_max;
+ }
+}
+
+DRIVER_INIT( snes_hirom )
+{
+ int i;
+ UINT16 totalblocks, readblocks;
+ UINT8 *rom;
+
+ rom = memory_region( REGION_USER3 );
+ snes_ram = auto_malloc(0x1000000);
+ memset( snes_ram, 0, 0x1000000 );
+
+ snes_cart.mode = SNES_MODE_21;
+ snes_cart.sram_max = 0x40000;
+
+ /* Find the number of blocks in this ROM */
+ //totalblocks = ((mame_fsize(file) - offset) >> (snes_cart.mode == MODE_20 ? 15 : 16));
+ totalblocks = (memory_region_length(REGION_USER3) / 0x10000) - 1;
+
+ /* FIXME: Insert crc check here */
+
+ readblocks = 0;
+ {
+ i = 0;
+ while( i < 64 && readblocks <= totalblocks )
+ {
+ memcpy( &snes_ram[0xc00000 + (i * 0x10000)], &rom[i * 0x10000], 0x10000);
+ i++;
+ readblocks++;
+ }
+ }
+
+ /* Find the amount of sram */
+ snes_cart.sram = snes_r_bank1(0x00ffd8);
+ if( snes_cart.sram > 0 )
+ {
+ snes_cart.sram = ((1 << (snes_cart.sram + 3)) / 8);
+ if( snes_cart.sram > snes_cart.sram_max )
+ snes_cart.sram = snes_cart.sram_max;
+ }
+}
+#endif /* MESS */
+
+void snes_hdma_init()
+{
+ UINT8 mask = 1, dma = 0, i;
+
+ snes_hdma_chnl = snes_ram[HDMAEN];
+ for( i = 0; i < 8; i++ )
+ {
+ if( snes_ram[HDMAEN] & mask )
+ {
+ snes_ram[SNES_DMA_BASE + dma + 8] = snes_ram[SNES_DMA_BASE + dma + 2];
+ snes_ram[SNES_DMA_BASE + dma + 9] = snes_ram[SNES_DMA_BASE + dma + 3];
+ snes_ram[SNES_DMA_BASE + dma + 0xa] = 0;
+ }
+ dma += 0x10;
+ mask <<= 1;
+ }
+}
+
+void snes_hdma()
+{
+ UINT8 mask = 1, dma = 0, i, contmode;
+ UINT16 bbus;
+ UINT32 abus;
+
+ /* Assume priority of the 8 DMA channels is 0-7 */
+ for( i = 0; i < 8; i++ )
+ {
+ if( snes_hdma_chnl & mask )
+ {
+ /* Check if we need to read a new line from the table */
+ if( !(snes_ram[SNES_DMA_BASE + dma + 0xa] & 0x7f ) )
+ {
+ abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 9] << 8) + snes_ram[SNES_DMA_BASE + dma + 8];
+
+ /* Get the number of lines */
+ snes_ram[SNES_DMA_BASE + dma + 0xa] = program_read_byte(abus);
+ if( !snes_ram[SNES_DMA_BASE + dma + 0xa] )
+ {
+ /* No more lines so clear HDMA */
+ snes_hdma_chnl &= ~mask;
+ continue;
+ }
+ abus++;
+ snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x40 )
+ {
+ snes_ram[SNES_DMA_BASE + dma + 5] = program_read_byte(abus++);
+ snes_ram[SNES_DMA_BASE + dma + 6] = program_read_byte(abus++);
+ snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
+ }
+ }
+
+ contmode = (--snes_ram[SNES_DMA_BASE + dma + 0xa]) & 0x80;
+
+ /* Transfer addresses */
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x40 ) /* Indirect */
+ abus = (snes_ram[SNES_DMA_BASE + dma + 7] << 16) + (snes_ram[SNES_DMA_BASE + dma + 6] << 8) + snes_ram[SNES_DMA_BASE + dma + 5];
+ else /* Absolute */
+ abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 9] << 8) + snes_ram[SNES_DMA_BASE + dma + 8];
+ bbus = 0x2100 + snes_ram[SNES_DMA_BASE + dma + 1];
+
+#ifdef SNES_DBG_HDMA
+ mame_printf_debug( "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] );
+#endif
+
+ switch( snes_ram[SNES_DMA_BASE + dma] & 0x7 )
+ {
+ case 0: /* 1 address */
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ } break;
+ case 5: /* 4 bytes to 2 addresses (l,h,l,h) */
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ } break;
+ case 1: /* 2 addresses (l,h) */
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ } break;
+ case 2: /* Write twice (l,l) */
+ case 6:
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus, program_read_byte(abus++));
+ } break;
+ case 3: /* 2 addresses/Write twice (l,l,h,h) */
+ case 7:
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ } break;
+ case 4: /* 4 addresses (l,h,l,h) */
+ {
+ program_write_byte(bbus, program_read_byte(abus++));
+ program_write_byte(bbus + 1, program_read_byte(abus++));
+ program_write_byte(bbus + 2, program_read_byte(abus++));
+ program_write_byte(bbus + 3, program_read_byte(abus++));
+ } break;
+ default:
+#ifdef MAME_DEBUG
+ mame_printf_debug( " HDMA of unsupported type: %d\n", snes_ram[SNES_DMA_BASE + dma] & 0x7 );
+#endif
+ break;
+ }
+
+ /* Update address */
+ if( contmode )
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x40 ) /* Indirect */
+ {
+ snes_ram[SNES_DMA_BASE + dma + 5] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 6] = (abus >> 8) & 0xff;
+ }
+ else /* Absolute */
+ {
+ snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
+ }
+ }
+
+ if( !(snes_ram[SNES_DMA_BASE + dma + 0xa] & 0x7f) )
+ {
+ if( !(snes_ram[SNES_DMA_BASE + dma] & 0x40) ) /* Absolute */
+ {
+ if( !contmode )
+ {
+ snes_ram[SNES_DMA_BASE + dma + 8] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 9] = (abus >> 8) & 0xff;
+ }
+ }
+ }
+ }
+ dma += 0x10;
+ mask <<= 1;
+ }
+}
+
+void snes_gdma( UINT8 channels )
+{
+ UINT8 mask = 1, dma = 0, i;
+ INT8 increment;
+ UINT16 bbus;
+ UINT32 abus, length;
+
+ /* Assume priority of the 8 DMA channels is 0-7 */
+ for( i = 0; i < 8; i++ )
+ {
+ if( channels & mask )
+ {
+ /* Find transfer addresses */
+ abus = (snes_ram[SNES_DMA_BASE + dma + 4] << 16) + (snes_ram[SNES_DMA_BASE + dma + 3] << 8) + snes_ram[SNES_DMA_BASE + dma + 2];
+ bbus = 0x2100 + snes_ram[SNES_DMA_BASE + dma + 1];
+
+ /* Auto increment */
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x8 )
+ {
+ increment = 0;
+ }
+ else
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x10 )
+ increment = -1;
+ else
+ increment = 1;
+ }
+
+ /* Number of bytes to transfer */
+ length = (snes_ram[SNES_DMA_BASE + dma + 6] << 8) + snes_ram[SNES_DMA_BASE + dma + 5];
+ if( !length )
+ length = 0x10000; /* 0x0000 really means 0x10000 */
+
+#ifdef SNES_DBG_GDMA
+ mame_printf_debug( "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 );
+#endif
+
+ switch( snes_ram[SNES_DMA_BASE + dma] & 0x7 )
+ {
+ case 0: /* 1 address */
+ case 2: /* 1 address ?? */
+ case 6:
+ {
+ while( length-- )
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus) );
+ else /* CPU->PPU */
+ program_write_byte(bbus, program_read_byte(abus) );
+ abus += increment;
+ }
+ } break;
+ case 1: /* 2 addresses (l,h) */
+ case 5:
+ {
+ while( length-- )
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus) );
+ else /* CPU->PPU */
+ program_write_byte(bbus, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 1) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 1, program_read_byte(abus) );
+ abus += increment;
+ }
+ } break;
+ case 3: /* 2 addresses/write twice (l,l,h,h) */
+ case 7:
+ {
+ while( length-- )
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus) );
+ else /* CPU->PPU */
+ program_write_byte(bbus, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus) );
+ else /* CPU->PPU */
+ program_write_byte(bbus, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 1) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 1, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 1) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 1, program_read_byte(abus) );
+ abus += increment;
+ }
+ } break;
+ case 4: /* 4 addresses (l,h,l,h) */
+ {
+ while( length-- )
+ {
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus) );
+ else /* CPU->PPU */
+ program_write_byte(bbus, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 1) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 1, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 2) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 2, program_read_byte(abus) );
+ abus += increment;
+ if( !(length--) )
+ break;
+ if( snes_ram[SNES_DMA_BASE + dma] & 0x80 ) /* PPU->CPU */
+ program_write_byte(abus, program_read_byte(bbus + 3) );
+ else /* CPU->PPU */
+ program_write_byte(bbus + 3, program_read_byte(abus) );
+ abus += increment;
+ }
+ } break;
+ default:
+#ifdef MAME_DEBUG
+ mame_printf_debug( " GDMA of unsupported type: %d\n", snes_ram[SNES_DMA_BASE + dma] & 0x7 );
+#endif
+ break;
+ }
+ /* We're done so write the new abus back to the registers */
+ snes_ram[SNES_DMA_BASE + dma + 2] = abus & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 3] = (abus >> 8) & 0xff;
+ snes_ram[SNES_DMA_BASE + dma + 5] = 0;
+ snes_ram[SNES_DMA_BASE + dma + 6] = 0;
+ }
+ dma += 0x10;
+ mask <<= 1;
+ }
+}