summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/snes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/snes.cpp')
-rw-r--r--src/mame/machine/snes.cpp1530
1 files changed, 0 insertions, 1530 deletions
diff --git a/src/mame/machine/snes.cpp b/src/mame/machine/snes.cpp
deleted file mode 100644
index 5afd2019388..00000000000
--- a/src/mame/machine/snes.cpp
+++ /dev/null
@@ -1,1530 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Angelo Salese, R. Belmont, Anthony Kruize, Fabio Priuli, Ryan Holtz
-/***************************************************************************
-
- snes.cpp
-
- Machine file to handle emulation of the Nintendo Super NES
-
- R. Belmont
- Anthony Kruize
- Angelo Salese
- Fabio Priuli
- Ryan Holtz
- Based on the original code by Lee Hammerton (aka Savoury Snax)
- Thanks to Anomie for invaluable technical information.
- Thanks to byuu for invaluable technical information.
-
- TODO:
- - DMA takes some Master CPU clock cycles that aren't taken into account
- for now.
-
-***************************************************************************/
-#define __MACHINE_SNES_C
-
-#include "emu.h"
-#include "includes/snes.h"
-
-
-#define DMA_REG(a) m_dma_regs[a - 0x4300] // regs 0x4300-0x437f
-
-uint32_t snes_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
-{
- /* NTSC SNES draw range is 1-225. */
- for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
- m_ppu->refresh_scanline(bitmap, y + 1);
-
- return 0;
-}
-
-
-/*************************************
-
- Timers
-
-*************************************/
-void snes_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
-{
- switch (id)
- {
- case TIMER_NMI_TICK:
- snes_nmi_tick(ptr, param);
- break;
- case TIMER_HIRQ_TICK:
- snes_hirq_tick_callback(ptr, param);
- break;
- case TIMER_RESET_OAM_ADDRESS:
- snes_reset_oam_address(ptr, param);
- break;
- case TIMER_RESET_HDMA:
- snes_reset_hdma(ptr, param);
- break;
- case TIMER_UPDATE_IO:
- snes_update_io(ptr, param);
- break;
- case TIMER_SCANLINE_TICK:
- snes_scanline_tick(ptr, param);
- break;
- case TIMER_HBLANK_TICK:
- snes_hblank_tick(ptr, param);
- break;
- default:
- throw emu_fatalerror("Unknown id in snes_state::device_timer");
- }
-}
-
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_nmi_tick)
-{
- // pull NMI
- m_maincpu->set_input_line(G65816_LINE_NMI, ASSERT_LINE);
-
- // don't happen again
- m_nmi_timer->adjust(attotime::never);
-}
-
-void snes_state::hirq_tick()
-{
- // latch the counters and pull IRQ
- // (don't need to switch to the 65816 context, we don't do anything dependant on it)
- m_ppu->set_latch_hv(m_ppu->current_x(), m_ppu->current_y());
- SNES_CPU_REG(TIMEUP) = 0x80; /* Indicate that irq occurred */
- m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE);
-
- // don't happen again
- m_hirq_timer->adjust(attotime::never);
-}
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_hirq_tick_callback)
-{
- hirq_tick();
-}
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_reset_oam_address)
-{
- if (!m_ppu->screen_disabled()) //Reset OAM address, byuu says it happens at H=10
- m_ppu->oam_address_reset();
-}
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_reset_hdma)
-{
- address_space &cpu0space = m_maincpu->space(AS_PROGRAM);
- hdma_init(cpu0space);
-}
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_update_io)
-{
- io_read();
- SNES_CPU_REG(HVBJOY) &= 0xfe; /* Clear busy bit */
-
- m_io_timer->adjust(attotime::never);
-}
-
-TIMER_CALLBACK_MEMBER(snes_state::snes_scanline_tick)
-{
- /* Increase current line - we want to latch on this line during it, not after it */
- m_ppu->set_current_vert(m_screen->vpos());
-
- // not in hblank
- SNES_CPU_REG(HVBJOY) &= ~0x40;
-
- /* Vertical IRQ timer - only if horizontal isn't also enabled! */
- if ((SNES_CPU_REG(NMITIMEN) & 0x20) && !(SNES_CPU_REG(NMITIMEN) & 0x10))
- {
- if (m_ppu->current_vert() == m_vtime)
- {
- SNES_CPU_REG(TIMEUP) = 0x80; /* Indicate that irq occurred */
- // IRQ latches the counters, do it now
- m_ppu->set_latch_hv(m_ppu->current_x(), m_ppu->current_y());
- m_maincpu->set_input_line(G65816_LINE_IRQ, ASSERT_LINE );
- }
- }
- /* Horizontal IRQ timer */
- if (SNES_CPU_REG(NMITIMEN) & 0x10)
- {
- int setirq = 1;
- int pixel = m_htime;
-
- // is the HIRQ on a specific scanline?
- if (SNES_CPU_REG(NMITIMEN) & 0x20)
- {
- if (m_ppu->current_vert() != m_vtime)
- {
- setirq = 0;
- }
- }
-
- if (setirq)
- {
-// printf("HIRQ @ %d, %d\n", pixel * m_ppu->htmult(), m_ppu->current_vert());
- if (pixel == 0)
- {
- hirq_tick();
- }
- else
- {
- m_hirq_timer->adjust(m_screen->time_until_pos(m_ppu->current_vert(), pixel * m_ppu->htmult()));
- }
- }
- }
-
- /* Start of VBlank */
- if (m_ppu->current_vert() == m_ppu->last_visible_line())
- {
- timer_set(m_screen->time_until_pos(m_ppu->current_vert(), 10), TIMER_RESET_OAM_ADDRESS);
-
- SNES_CPU_REG(HVBJOY) |= 0x81; /* Set vblank bit to on & indicate controllers being read */
- SNES_CPU_REG(RDNMI) |= 0x80; /* Set NMI occurred bit */
-
- if (SNES_CPU_REG(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)
- m_nmi_timer->adjust(m_maincpu->cycles_to_attotime(12));
- }
-
- /* three lines after start of vblank we update the controllers (value from snes9x) */
- m_io_timer->adjust(m_screen->time_until_pos(m_ppu->current_vert() + 2, m_hblank_offset * m_ppu->htmult()));
- }
-
- // hdma reset happens at scanline 0, H=~6
- if (m_ppu->current_vert() == 0)
- {
- address_space &cpu0space = m_maincpu->space(AS_PROGRAM);
- hdma_init(cpu0space);
- }
-
- if (m_ppu->current_vert() == 0)
- { /* VBlank is over, time for a new frame */
- SNES_CPU_REG(HVBJOY) &= 0x7f; /* Clear vblank bit */
- SNES_CPU_REG(RDNMI) &= 0x7f; /* Clear nmi occurred bit */
- m_ppu->toggle_field();
- m_ppu->clear_time_range_over();
-
- m_maincpu->set_input_line(G65816_LINE_NMI, CLEAR_LINE );
- }
-
- m_scanline_timer->adjust(attotime::never);
- m_hblank_timer->adjust(m_screen->time_until_pos(m_ppu->current_vert(), m_hblank_offset * m_ppu->htmult()));
-
-// printf("%02x %d\n",SNES_CPU_REG(HVBJOY),m_ppu->current_vert());
-}
-
-/* This is called at the start of hblank *before* the scanline indicated in current_vert! */
-TIMER_CALLBACK_MEMBER(snes_state::snes_hblank_tick)
-{
- address_space &cpu0space = m_maincpu->space(AS_PROGRAM);
- int nextscan;
-
- m_ppu->set_current_vert(m_screen->vpos());
-
- /* make sure we halt */
- m_hblank_timer->adjust(attotime::never);
-
- /* draw a scanline */
- if (m_ppu->current_vert() <= m_ppu->last_visible_line())
- {
- /* Do HDMA */
- if (SNES_CPU_REG(HDMAEN))
- hdma(cpu0space);
-
- if (m_screen->vpos() > 0)
- m_screen->update_partial((m_ppu->interlace() == 2) ? (m_ppu->current_vert() * m_ppu->interlace()) : m_ppu->current_vert() - 1);
- }
-
- // signal hblank
- SNES_CPU_REG(HVBJOY) |= 0x40;
-
- /* kick off the start of scanline timer */
- nextscan = m_ppu->current_vert() + 1;
- if (nextscan >= m_ppu->vtotal())
- {
- nextscan = 0;
- }
-
- m_scanline_timer->adjust(m_screen->time_until_pos(nextscan));
-}
-
-
-/*************************************
-
- Input Handlers
-
-*************************************/
-
-uint8_t snes_state::snes_open_bus_r()
-{
- static uint8_t recurse = 0;
- uint16_t result;
-
- /* prevent recursion */
- if (recurse)
- return 0xff;
-
- recurse = 1;
- result = m_maincpu->space(AS_PROGRAM).read_byte(m_maincpu->pc() - 1); //LAST opcode that's fetched on the bus
- recurse = 0;
- return result;
-}
-
-/* read & write to DMA addresses are defined separately, to be called by snessdd1 handlers */
-uint8_t snes_state::snes_io_dma_r(offs_t offset)
-{
- switch (offset)
- {
- case DMAP0: case DMAP1: case DMAP2: case DMAP3: /*0x43n0*/
- case DMAP4: case DMAP5: case DMAP6: case DMAP7:
- return m_dma_channel[(offset >> 4) & 0x07].dmap;
- case BBAD0: case BBAD1: case BBAD2: case BBAD3: /*0x43n1*/
- case BBAD4: case BBAD5: case BBAD6: case BBAD7:
- return m_dma_channel[(offset >> 4) & 0x07].dest_addr;
- case A1T0L: case A1T1L: case A1T2L: case A1T3L: /*0x43n2*/
- case A1T4L: case A1T5L: case A1T6L: case A1T7L:
- return m_dma_channel[(offset >> 4) & 0x07].src_addr & 0xff;
- case A1T0H: case A1T1H: case A1T2H: case A1T3H: /*0x43n3*/
- case A1T4H: case A1T5H: case A1T6H: case A1T7H:
- return (m_dma_channel[(offset >> 4) & 0x07].src_addr >> 8) & 0xff;
- case A1B0: case A1B1: case A1B2: case A1B3: /*0x43n4*/
- case A1B4: case A1B5: case A1B6: case A1B7:
- return m_dma_channel[(offset >> 4) & 0x07].bank;
- case DAS0L: case DAS1L: case DAS2L: case DAS3L: /*0x43n5*/
- case DAS4L: case DAS5L: case DAS6L: case DAS7L:
- return m_dma_channel[(offset >> 4) & 0x07].trans_size & 0xff;
- case DAS0H: case DAS1H: case DAS2H: case DAS3H: /*0x43n6*/
- case DAS4H: case DAS5H: case DAS6H: case DAS7H:
- return (m_dma_channel[(offset >> 4) & 0x07].trans_size >> 8) & 0xff;
- case DSAB0: case DSAB1: case DSAB2: case DSAB3: /*0x43n7*/
- case DSAB4: case DSAB5: case DSAB6: case DSAB7:
- return m_dma_channel[(offset >> 4) & 0x07].ibank;
- case A2A0L: case A2A1L: case A2A2L: case A2A3L: /*0x43n8*/
- case A2A4L: case A2A5L: case A2A6L: case A2A7L:
- return m_dma_channel[(offset >> 4) & 0x07].hdma_addr & 0xff;
- case A2A0H: case A2A1H: case A2A2H: case A2A3H: /*0x43n9*/
- case A2A4H: case A2A5H: case A2A6H: case A2A7H:
- return (m_dma_channel[(offset >> 4) & 0x07].hdma_addr >> 8) & 0xff;
- case NTRL0: case NTRL1: case NTRL2: case NTRL3: /*0x43na*/
- case NTRL4: case NTRL5: case NTRL6: case NTRL7:
- return m_dma_channel[(offset >> 4) & 0x07].hdma_line_counter;
- case 0x430b: case 0x431b: case 0x432b: case 0x433b: /* according to bsnes, this does not return open_bus (even if its precise effect is unknown) */
- case 0x434b: case 0x435b: case 0x436b: case 0x437b:
- return m_dma_channel[(offset >> 4) & 0x07].unk;
- }
-
- /* we should never arrive here */
- return snes_open_bus_r();
-}
-
-void snes_state::snes_io_dma_w(offs_t offset, uint8_t data)
-{
- switch (offset)
- {
- /* Below is all DMA related */
- case DMAP0: case DMAP1: case DMAP2: case DMAP3: /*0x43n0*/
- case DMAP4: case DMAP5: case DMAP6: case DMAP7:
- m_dma_channel[(offset >> 4) & 0x07].dmap = data;
- break;
- case BBAD0: case BBAD1: case BBAD2: case BBAD3: /*0x43n1*/
- case BBAD4: case BBAD5: case BBAD6: case BBAD7:
- m_dma_channel[(offset >> 4) & 0x07].dest_addr = data;
- break;
- case A1T0L: case A1T1L: case A1T2L: case A1T3L: /*0x43n2*/
- case A1T4L: case A1T5L: case A1T6L: case A1T7L:
- m_dma_channel[(offset >> 4) & 0x07].src_addr = (m_dma_channel[(offset >> 4) & 0x07].src_addr & 0xff00) | (data << 0);
- break;
- case A1T0H: case A1T1H: case A1T2H: case A1T3H: /*0x43n3*/
- case A1T4H: case A1T5H: case A1T6H: case A1T7H:
- m_dma_channel[(offset >> 4) & 0x07].src_addr = (m_dma_channel[(offset >> 4) & 0x07].src_addr & 0x00ff) | (data << 8);
- break;
- case A1B0: case A1B1: case A1B2: case A1B3: /*0x43n4*/
- case A1B4: case A1B5: case A1B6: case A1B7:
- m_dma_channel[(offset >> 4) & 0x07].bank = data;
- break;
- case DAS0L: case DAS1L: case DAS2L: case DAS3L: /*0x43n5*/
- case DAS4L: case DAS5L: case DAS6L: case DAS7L:
- m_dma_channel[(offset >> 4) & 0x07].trans_size = (m_dma_channel[(offset >> 4) & 0x07].trans_size & 0xff00) | (data << 0);
- break;
- case DAS0H: case DAS1H: case DAS2H: case DAS3H: /*0x43n6*/
- case DAS4H: case DAS5H: case DAS6H: case DAS7H:
- m_dma_channel[(offset >> 4) & 0x07].trans_size = (m_dma_channel[(offset >> 4) & 0x07].trans_size & 0x00ff) | (data << 8);
- break;
- case DSAB0: case DSAB1: case DSAB2: case DSAB3: /*0x43n7*/
- case DSAB4: case DSAB5: case DSAB6: case DSAB7:
- m_dma_channel[(offset >> 4) & 0x07].ibank = data;
- break;
- case A2A0L: case A2A1L: case A2A2L: case A2A3L: /*0x43n8*/
- case A2A4L: case A2A5L: case A2A6L: case A2A7L:
- m_dma_channel[(offset >> 4) & 0x07].hdma_addr = (m_dma_channel[(offset >> 4) & 0x07].hdma_addr & 0xff00) | (data << 0);
- break;
- case A2A0H: case A2A1H: case A2A2H: case A2A3H: /*0x43n9*/
- case A2A4H: case A2A5H: case A2A6H: case A2A7H:
- m_dma_channel[(offset >> 4) & 0x07].hdma_addr = (m_dma_channel[(offset >> 4) & 0x07].hdma_addr & 0x00ff) | (data << 8);
- break;
- case NTRL0: case NTRL1: case NTRL2: case NTRL3: /*0x43na*/
- case NTRL4: case NTRL5: case NTRL6: case NTRL7:
- m_dma_channel[(offset >> 4) & 0x07].hdma_line_counter = data;
- break;
- case 0x430b: case 0x431b: case 0x432b: case 0x433b:
- case 0x434b: case 0x435b: case 0x436b: case 0x437b:
- m_dma_channel[(offset >> 4) & 0x07].unk = data;
- break;
- }
-
- DMA_REG(offset) = data;
-}
-
-/*
- * 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
- */
-uint8_t snes_state::snes_r_io(offs_t offset)
-{
- uint8_t value = 0;
-
- // PPU accesses are from 2100 to 213f
- if (offset >= INIDISP && offset < APU00)
- {
- return m_ppu->read(offset, SNES_CPU_REG(WRIO) & 0x80);
- }
-
- // APU is mirrored from 2140 to 217f
- if (offset >= APU00 && offset < WMDATA)
- {
- return m_soundcpu->spc_port_out_r(offset & 0x3);
- }
-
- // DMA accesses are from 4300 to 437f
- if (offset >= DMAP0 && offset < 0x4380)
- {
- return snes_io_dma_r(offset);
- }
-
-
- // other accesses (notice that WRMPYA, WRMPYB, WRDIVL, WRDIVH, WRDVDD, MEMSEL, RDDIVL, RDDIVH, RDMPYL, RDMPYH are handled by the CPU directly)
- switch (offset) // offset is from 0x000000
- {
- case WMDATA: /* Data to read from WRAM */
- value = m_wram[m_wram_address++];
- m_wram_address &= 0x1ffff;
- return value;
-
- case OLDJOY1: /* Data for old NES controllers (JOYSER1) */
- return (oldjoy1_read(m_oldjoy1_latch & 0x1) & 0x03) | (snes_open_bus_r() & 0xfc);
-
- case OLDJOY2: /* Data for old NES controllers (JOYSER2) */
- return (oldjoy2_read(m_oldjoy1_latch & 0x1) & 0x03) | 0x1c | (snes_open_bus_r() & 0xe0);
-
- case RDNMI: /* NMI flag by v-blank and version number */
- value = (SNES_CPU_REG(RDNMI) & 0x80) | (snes_open_bus_r() & 0x70);
- SNES_CPU_REG(RDNMI) &= 0x70; /* NMI flag is reset on read */
- return value | 2; //CPU version number
- case TIMEUP: /* IRQ flag by H/V count timer */
- value = (snes_open_bus_r() & 0x7f) | (SNES_CPU_REG(TIMEUP) & 0x80);
- m_maincpu->set_input_line(G65816_LINE_IRQ, CLEAR_LINE );
- SNES_CPU_REG(TIMEUP) = 0; // flag is cleared on both read and write
- 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 (m_screen->hpos() >= 363) SNES_CPU_REG(HVBJOY) |= 0x40;
-// else SNES_CPU_REG(HVBJOY) &= ~0x40;
- return (SNES_CPU_REG(HVBJOY) & 0xc1) | (snes_open_bus_r() & 0x3e);
- case RDIO: /* Programmable I/O port - echos back what's written to WRIO */
- return SNES_CPU_REG(WRIO);
- case JOY1L: /* Joypad 1 status register (low) */
- case JOY1H: /* Joypad 1 status register (high) */
- case JOY2L: /* Joypad 2 status register (low) */
- case JOY2H: /* Joypad 2 status register (high) */
- case JOY3L: /* Joypad 3 status register (low) */
- case JOY3H: /* Joypad 3 status register (high) */
- case JOY4L: /* Joypad 4 status register (low) */
- case JOY4H: /* Joypad 4 status register (high) */
- if (m_is_nss && m_input_disabled)
- return 0;
- return SNES_CPU_REG(offset);
-
- case 0x4100: /* NSS Dip-Switches */
- if (m_is_nss)
- return ioport("DSW")->read();
- break;
-// case 0x4101: //PC: a104 - a10e - a12a //only nss_actr (DSW actually reads in word units ...)
-
- default:
-// osd_printf_debug("snes_r: offset = %x pc = %x\n",offset,m_maincpu->pc());
-// Added break; after commenting above line. If uncommenting, drop the break;
- break;
- }
-
-// printf("unsupported read: offset == %08x\n", offset);
-
- /* Unsupported reads returns open bus */
-// printf("%02x %02x\n",offset,snes_open_bus_r());
- return snes_open_bus_r();
-}
-
-/*
- * 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
- */
-void snes_state::snes_w_io(address_space &space, offs_t offset, uint8_t data)
-{
- // PPU accesses are from 2100 to 213f
- if (offset >= INIDISP && offset < APU00)
- {
- m_ppu->write(offset, data);
- return;
- }
-
- // APU is mirrored from 2140 to 217f
- if (offset >= APU00 && offset < WMDATA)
- {
-// printf("816: %02x to APU @ %d (PC=%06x)\n", data, offset & 3,m_maincpu->pc());
- m_soundcpu->spc_port_in_w(offset & 0x3, data);
- machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(20));
- return;
- }
-
- // DMA accesses are from 4300 to 437f
- if (offset >= DMAP0 && offset < 0x4380)
- {
- snes_io_dma_w(offset, data);
- return;
- }
-
- /* offset is from 0x000000 */
- switch (offset)
- {
- case WMDATA: /* Data to write to WRAM */
- m_wram[m_wram_address++] = data;
- m_wram_address &= 0x1ffff;
- return;
- case WMADDL: /* Address to read/write to wram (low) */
- m_wram_address = (m_wram_address & 0xffff00) | (data << 0);
- m_wram_address &= 0x1ffff;
- return;
- case WMADDM: /* Address to read/write to wram (mid) */
- m_wram_address = (m_wram_address & 0xff00ff) | (data << 8);
- m_wram_address &= 0x1ffff;
- return;
- case WMADDH: /* Address to read/write to wram (high) */
- m_wram_address = (m_wram_address & 0x00ffff) | (data << 16);
- m_wram_address &= 0x1ffff;
- return;
- case OLDJOY1: /* Old NES joystick support */
- write_joy_latch(data);
- if (m_is_nss)
- m_game_over_flag = (data & 4) >> 2;
- return;
- case OLDJOY2: /* Old NES joystick support */
- return;
- case NMITIMEN: /* Flag for v-blank, timer int. and joy read */
- if ((data & 0x30) == 0x00)
- {
- m_maincpu->set_input_line(G65816_LINE_IRQ, CLEAR_LINE );
- SNES_CPU_REG(TIMEUP) = 0; // clear pending IRQ if irq is disabled here, 3x3 Eyes - Seima Korin Den behaves on this
- }
- SNES_CPU_REG(NMITIMEN) = data;
- return;
- case WRIO: /* Programmable I/O port - latches H/V counters on a 0->1 transition */
- wrio_write(data);
- SNES_CPU_REG(WRIO) = data;
- return;
- case HTIMEL: /* H-Count timer settings (low) */
- m_htime = (m_htime & 0xff00) | (data << 0);
- m_htime &= 0x1ff;
- return;
- case HTIMEH: /* H-Count timer settings (high) */
- m_htime = (m_htime & 0x00ff) | (data << 8);
- m_htime &= 0x1ff;
- return;
- case VTIMEL: /* V-Count timer settings (low) */
- m_vtime = (m_vtime & 0xff00) | (data << 0);
- m_vtime &= 0x1ff;
- return;
- case VTIMEH: /* V-Count timer settings (high) */
- m_vtime = (m_vtime & 0x00ff) | (data << 8);
- m_vtime &= 0x1ff;
- return;
- case MDMAEN: /* DMA channel designation and trigger */
- dma(space, data);
- SNES_CPU_REG(MDMAEN) = 0; /* Once DMA is done we need to reset all bits to 0 */
- return;
- case HDMAEN: /* HDMA channel designation */
- if (data != SNES_CPU_REG(HDMAEN)) //if a HDMA is enabled, data is inited at the next scanline
- timer_set(m_screen->time_until_pos(m_ppu->current_vert() + 1), TIMER_RESET_HDMA);
- SNES_CPU_REG(HDMAEN) = data;
- return;
- case TIMEUP: // IRQ Flag is cleared on both read and write
- m_maincpu->set_input_line(G65816_LINE_IRQ, CLEAR_LINE );
- SNES_CPU_REG(TIMEUP) = 0;
- return;
- /* 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;
- }
-
-}
-
-void snes_state::write_joy_latch(uint8_t data)
-{
- if (m_oldjoy1_latch == (data & 0x01))
- return;
-
- m_oldjoy1_latch = data & 0x01;
- m_read_idx[0] = 0;
- m_read_idx[1] = 0;
- m_read_idx[2] = 0;
- m_read_idx[3] = 0;
-}
-
-
-void snes_state::wrio_write(uint8_t data)
-{
- if (!(SNES_CPU_REG(WRIO) & 0x80) && (data & 0x80))
- {
- // external latch
- m_ppu->set_latch_hv(m_ppu->current_x(), m_ppu->current_y());
- }
-}
-
-WRITE_LINE_MEMBER(snes_state::snes_extern_irq_w)
-{
- m_maincpu->set_input_line(G65816_LINE_IRQ, state);
-}
-
-/*************************************
-
- Memory Handlers
-
-*************************************/
-
-/*
-There are at least 4 different kind of boards for SNES carts, which we denote with
-mode_20, mode_21, mode_22 and mode_25. Below is a layout of the memory for each of
-them. Notice we mirror ROM at loading time where necessary (e.g. banks 0x80 to 0xff
-at address 0x8000 for mode_20).
-
-MODE_20
- banks 0x00 0x20 0x40 0x60 0x70 0x7e 0x80 0xc0 0xff
-address | | | | | | | |
-0xffff ------------------------------------------------------------------------------|
- ROM | ROM / | ROM | ROM | ROM / | | 0x00 | 0x40 |
- | DSP | | | SRAM? | | to | to |
-0x8000 ----------------------------------------------------------| | 0x3f | 0x7f |
- Reserv | | | | W | | |
- | | | S | R | m | m |
-0x6000 ----------------------------| | DSP / | R | A | i | i |
- I/O | Reserv | | A | M | r | r |
-0x2000 ----------------------------| | Reserv | M | | r | r |
- Low RAM (from 0x7e) | | | | | o | o |
- | | | | | r | r |
-0x0000 -------------------------------------------------------------------------------
-
-MODE_22 is the same, but banks 0x40 to 0x7e at address 0x000 to 0x7fff contain ROM (of
-course mirrored also at 0xc0 to 0xff). Mode 22 is quite similar to the board SHVC-2P3B.
-It is used also in SDD-1 games (only for the first blocks of data). DSP data & status
-can be either at banks 0x20 to 0x40 at address 0x8000 or at banks 0x60 to 0x6f at address
-0x0000.
-
-
-MODE_21
- banks 0x00 0x10 0x30 0x40 0x7e 0x80 0xc0 0xff
-address | | | | | | |
-0xffff --------------------------------------------------------------------|
- mirror | 0xc0 | | mirror | |
- upper half ROM | to | | up half | |
- from 0xc0 to 0xff | 0xff | | ROM | |
-0x8000 --------------------------------------| | |---------| |
- DSP / | Reserv | SRAM | | W | | |
- Reserv | | | m | R | 0x00 | R |
-0x6000 --------------------------------------| i | A | to | O |
- I/O | r | M | 0x3f | M |
-0x2000 --------------------------------------| r | | mirror | |
- Low RAM (from 0x7e) | o | | | |
- | r | | | |
-0x0000 ---------------------------------------------------------------------
-
-
-MODE_25 is very similar to MODE_21, but it is tought for images whose roms is larger than
-the available banks at 0xc0 to 0xff.
-
- banks 0x00 0x20 0x3e 0x40 0x7e 0x80 0xb0 0xc0 0xff
-address | | | | | | | |
-0xffff ----------------------------------------------------------------------------|
- mirror | Last | | | ROM | |
- upper half ROM | 0.5Mbits | | | mirror | |
- from 0x40 to 0x7e | ROM | ROM | | up half | ROM |
-0x8000 -----------------------------------| | |------------------| |
- DSP / | Reserv | next | W | Reserv | SRAM | first |
- Reserv | | | R | | | |
-0x6000 -----------------------------------| 31 | A |------------------| 32 |
- I/O | Mbits | M | 0x00 | Mbits |
-0x2000 -----------------------------------| | | to | |
- Low RAM (from 0x7e) | | | 0x3f | |
- | | | mirror | |
-0x0000 -----------------------------------------------------------------------------
-
-
-*/
-
-inline uint8_t snes_state::snes_rom_access(uint32_t offset)
-{
- uint32_t addr;
- uint8_t value = 0xff;
- uint8_t base_bank = (offset < 0x800000) ? 0x80 : 0x00;
-
- switch (m_cart.mode)
- {
- case SNES_MODE_20:
- case SNES_MODE_22:
- addr = (m_cart.rom_bank_map[offset/0x10000] * 0x8000) + (offset & 0x7fff);
- value = m_cart.m_rom[addr];
- break;
- case SNES_MODE_21:
- case SNES_MODE_25:
- offset &= 0x3fffff;
- addr = (m_cart.rom_bank_map[base_bank + (offset/0x8000)] * 0x8000) + (offset & 0x7fff);
- value = m_cart.m_rom[addr];
- break;
- }
-
- return value;
-}
-
-/* 0x000000 - 0x7dffff */
-uint8_t snes_state::snes_r_bank1(offs_t offset)
-{
- uint8_t value = 0xff;
- uint16_t address = offset & 0xffff;
-
- if (offset < 0x400000)
- {
- if (address < 0x2000) /* Mirror of Low RAM */
- value = m_wram[address];
- else if (address < 0x6000) /* I/O */
- value = snes_r_io(address);
- else if (address < 0x8000)
- {
- if (offset >= 0x300000 && m_cart.mode == SNES_MODE_21 && m_cart.m_nvram_size > 0)
- {
- /* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
- /* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
- int mask = (m_cart.m_nvram_size - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
- value = m_cart.m_nvram[(offset - 0x6000) & mask];
- }
- else
- value = snes_open_bus_r(); /* Reserved */
- }
- else
- value = snes_rom_access(offset); //ROM
- }
- else if (offset < 0x700000)
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 in 0x0000-0x7fff */
- value = snes_open_bus_r();
- else
- value = snes_rom_access(offset); //ROM
- }
- else
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 */
- {
- if (m_cart.m_nvram_size > 0x8000)
- {
- // In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
- int mask = m_cart.m_nvram_size - 1;
- offset = (offset / 0x10000) * 0x8000 + (offset & 0x7fff);
- value = m_cart.m_nvram[offset & mask];
- }
- else if (m_cart.m_nvram_size > 0)
- {
- int mask = m_cart.m_nvram_size - 1; /* Limit SRAM size to what's actually present */
- value = m_cart.m_nvram[offset & mask];
- }
- else
- {
- logerror("(PC=%06x) snes_r_bank1: Unmapped external chip read: %X\n", m_maincpu->pc(), offset);
- value = snes_open_bus_r(); /* Reserved */
- }
- }
- else
- value = snes_rom_access(offset); //ROM
- }
-
- return value;
-}
-
-
-/* 0x800000 - 0xffffff */
-uint8_t snes_state::snes_r_bank2(offs_t offset)
-{
- uint8_t value = 0;
- uint16_t address = offset & 0xffff;
-
- if (offset < 0x400000)
- {
- if (address < 0x8000)
- value = m_maincpu->space(AS_PROGRAM).read_byte(offset);
- else
- value = snes_rom_access(0x800000 + offset); //ROM
- }
- else
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 in 0x0000-0x7fff */
- {
- if (offset < 0x700000)
- value = m_maincpu->space(AS_PROGRAM).read_byte(offset);
- else
- {
- if (m_cart.m_nvram_size > 0x8000)
- {
- // In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
- int mask = m_cart.m_nvram_size - 1;
- offset = (offset / 0x10000) * 0x8000 + (offset & 0x7fff);
- value = m_cart.m_nvram[offset & mask];
- }
- else if (m_cart.m_nvram_size > 0)
- {
- int mask = m_cart.m_nvram_size - 1; /* Limit SRAM size to what's actually present */
- value = m_cart.m_nvram[offset & mask];
- }
- else
- {
- logerror("(PC=%06x) snes_r_bank2: Unmapped external chip read: %X\n", m_maincpu->pc(), offset);
- value = snes_open_bus_r(); /* Reserved */
- }
- }
- }
- else
- value = snes_rom_access(0x800000 + offset); //ROM
- }
-
- return value;
-}
-
-
-/* 0x000000 - 0x7dffff */
-void snes_state::snes_w_bank1(address_space &space, offs_t offset, uint8_t data)
-{
- uint16_t address = offset & 0xffff;
-
- if (offset < 0x400000)
- {
- if (address < 0x2000) /* Mirror of Low RAM */
- m_wram[address] = data;
- else if (address < 0x6000) /* I/O */
- snes_w_io(space, address, data);
- else if (address < 0x8000)
- {
- if (offset >= 0x300000 && m_cart.mode == SNES_MODE_21 && m_cart.m_nvram_size > 0)
- {
- /* Donkey Kong Country checks this and detects a copier if 0x800 is not masked out due to sram size */
- /* OTOH Secret of Mana does not work properly if sram is not mirrored on later banks */
- int mask = (m_cart.m_nvram_size - 1) & 0x7fff; /* Limit SRAM size to what's actually present */
- m_cart.m_nvram[(offset - 0x6000) & mask] = data;
- }
- else
- logerror("(PC=%06x) snes_w_bank1: Attempt to write to reserved address: %X = %02X\n", m_maincpu->pc(), offset, data);
- }
- else
- logerror("(PC=%06x) Attempt to write to ROM address: %X\n", m_maincpu->pc(), offset);
- }
- else if (offset >= 0x600000 && offset < 0x700000)
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 */
- logerror("(PC=%06x) snes_w_bank1: Attempt to write to reserved address: %X = %02X\n", m_maincpu->pc(), offset, data);
- else
- logerror("(PC=%06x) Attempt to write to ROM address: %X\n", m_maincpu->pc(), offset);
- }
- else if (offset >= 0x700000)
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 */
- {
- if (m_cart.m_nvram_size > 0x8000)
- {
- // In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
- int mask = m_cart.m_nvram_size - 1;
- offset = (offset / 0x10000) * 0x8000 + (offset & 0x7fff);
- m_cart.m_nvram[offset & mask] = data;
- }
- else if (m_cart.m_nvram_size > 0)
- {
- int mask = m_cart.m_nvram_size - 1; /* Limit SRAM size to what's actually present */
- m_cart.m_nvram[offset & mask] = data;
- }
- else
- logerror("(PC=%06x) snes_w_bank1: Attempt to write to reserved address: %X = %02X\n", m_maincpu->pc(), offset, data);
- }
- else
- logerror("(PC=%06x) Attempt to write to ROM address: %X\n", m_maincpu->pc(), offset);
- }
-}
-
-/* 0x800000 - 0xffffff */
-void snes_state::snes_w_bank2(offs_t offset, uint8_t data)
-{
- uint16_t address = offset & 0xffff;
-
- if (offset < 0x400000)
- {
- if (address < 0x8000)
- m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
- else
- logerror("(PC=%06x) Attempt to write to ROM address: %X\n", m_maincpu->pc(), offset + 0x800000);
- }
- else
- {
- if (m_cart.mode & 5 && address < 0x8000) /* Mode 20 & 22 in 0x0000-0x7fff */
- {
- if (offset < 0x700000)
- m_maincpu->space(AS_PROGRAM).write_byte(offset, data);
- else
- {
- if (m_cart.m_nvram_size > 0x8000)
- {
- // In this case, SRAM is mapped in 0x8000 chunks at diff offsets: 0x700000-0x707fff, 0x710000-0x717fff, etc.
- int mask = m_cart.m_nvram_size - 1;
- offset = (offset / 0x10000) * 0x8000 + (offset & 0x7fff);
- m_cart.m_nvram[offset & mask] = data;
- }
- else if (m_cart.m_nvram_size > 0)
- {
- int mask = m_cart.m_nvram_size - 1; /* Limit SRAM size to what's actually present */
- m_cart.m_nvram[offset & mask] = data;
- }
- else
- logerror("(PC=%06x) snes_w_bank2: Attempt to write to reserved address: %X = %02X\n", m_maincpu->pc(), offset, data);
- }
- }
- else
- logerror("(PC=%06x) Attempt to write to ROM address: %X\n", m_maincpu->pc(), offset);
- }
-}
-
-
-/*************************************
-
- Input Callbacks
-
-*************************************/
-
-void snes_state::io_read()
-{
- static const char *const portnames[2][2] =
- {
- { "SERIAL1_DATA1", "SERIAL1_DATA2" },
- { "SERIAL2_DATA1", "SERIAL2_DATA2" }
- };
-
- for (int port = 0; port < 2; port++)
- {
- m_data1[port] = ioport(portnames[port][0])->read();
- m_data2[port] = ioport(portnames[port][1])->read();
-
- // avoid sending signals that could crash games
- // if left, no right
- if (m_data1[port] & 0x200)
- m_data1[port] &= ~0x100;
- // if up, no down
- if (m_data1[port] & 0x800)
- m_data1[port] &= ~0x400;
- // if left, no right
- if (m_data2[port] & 0x200)
- m_data2[port] &= ~0x100;
- // if up, no down
- if (m_data2[port] & 0x800)
- m_data2[port] &= ~0x400;
- }
-
- // is automatic reading on? if so, copy port data1/data2 to joy1l->joy4h
- // this actually works like reading the first 16bits from oldjoy1/2 in reverse order
- if (SNES_CPU_REG(NMITIMEN) & 1)
- {
- SNES_CPU_REG(JOY1L) = (m_data1[0] & 0x00ff) >> 0;
- SNES_CPU_REG(JOY1H) = (m_data1[0] & 0xff00) >> 8;
- SNES_CPU_REG(JOY2L) = (m_data1[1] & 0x00ff) >> 0;
- SNES_CPU_REG(JOY2H) = (m_data1[1] & 0xff00) >> 8;
- SNES_CPU_REG(JOY3L) = (m_data2[0] & 0x00ff) >> 0;
- SNES_CPU_REG(JOY3H) = (m_data2[0] & 0xff00) >> 8;
- SNES_CPU_REG(JOY4L) = (m_data2[1] & 0x00ff) >> 0;
- SNES_CPU_REG(JOY4H) = (m_data2[1] & 0xff00) >> 8;
-
- // make sure read_idx starts returning all 1s because the auto-read reads it :-)
- m_read_idx[0] = 16;
- m_read_idx[1] = 16;
- }
-
- if(m_is_nss)
- m_joy_flag = 0;
-}
-
-
-uint8_t snes_state::oldjoy1_read(int latched)
-{
- if (latched)
- return 0;
-
- if (m_read_idx[0] >= 16)
- return 1;
- else
- return (m_data1[0] >> (15 - m_read_idx[0]++)) & 0x01;
-}
-
-uint8_t snes_state::oldjoy2_read(int latched)
-{
- if (latched)
- return 0;
-
- if (m_read_idx[1] >= 16)
- return 1;
- else
- return (m_data1[1] >> (15 - m_read_idx[1]++)) & 0x01;
-}
-
-
-/*************************************
-
- Driver Init
-
-*************************************/
-
-void snes_state::snes_init_timers()
-{
- /* init timers and stop them */
- m_scanline_timer = timer_alloc(TIMER_SCANLINE_TICK);
- m_scanline_timer->adjust(attotime::never);
- m_hblank_timer = timer_alloc(TIMER_HBLANK_TICK);
- m_hblank_timer->adjust(attotime::never);
- m_nmi_timer = timer_alloc(TIMER_NMI_TICK);
- m_nmi_timer->adjust(attotime::never);
- m_hirq_timer = timer_alloc(TIMER_HIRQ_TICK);
- m_hirq_timer->adjust(attotime::never);
- //m_div_timer = timer_alloc(TIMER_DIV);
- //m_div_timer->adjust(attotime::never);
- //m_mult_timer = timer_alloc(TIMER_MULT);
- //m_mult_timer->adjust(attotime::never);
- m_io_timer = timer_alloc(TIMER_UPDATE_IO);
- m_io_timer->adjust(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.
- m_hblank_offset = 274;
- m_hblank_timer->adjust(m_screen->time_until_pos(m_ppu->vtotal() - 1, m_hblank_offset));
-}
-
-void snes_state::snes_init_ram()
-{
- int i;
-
- /* Init work RAM - 0x55 isn't exactly right but it's close */
- /* make sure it happens to the 65816 (CPU 0) */
- const size_t size = m_wram.bytes();
- for (i = 0; i < size; i++)
- {
- m_wram[i] = 0x55;
- }
-
- /* Inititialize registers/variables */
- SNES_CPU_REG(JOY1L) = SNES_CPU_REG(JOY1H) = 0;
- SNES_CPU_REG(JOY2L) = SNES_CPU_REG(JOY2H) = 0;
- SNES_CPU_REG(JOY3L) = SNES_CPU_REG(JOY3H) = 0;
- SNES_CPU_REG(JOY4L) = SNES_CPU_REG(JOY4H) = 0;
- m_data1[0] = m_data2[0] = m_data1[1] = m_data2[1] = 0;
-
- // set up some known register power-up defaults
- SNES_CPU_REG(WRIO) = 0xff;
-
- // init frame counter so first line is 0
- if (m_screen->frame_period().as_hz() >= 59.0)
- m_ppu->set_current_vert(SNES_VTOTAL_NTSC);
- else
- m_ppu->set_current_vert(SNES_VTOTAL_PAL);
-}
-
-void snes_state::machine_start()
-{
- // power-on sets these registers like this
- SNES_CPU_REG(WRIO) = 0xff;
-// SNES_CPU_REG(WRMPYA) = 0xff;
-// SNES_CPU_REG(WRDIVL) = 0xff;
-// SNES_CPU_REG(WRDIVH) = 0xff;
-
- snes_init_timers();
-
- save_item(STRUCT_MEMBER(m_dma_channel, dmap));
- save_item(STRUCT_MEMBER(m_dma_channel, dest_addr));
- save_item(STRUCT_MEMBER(m_dma_channel, src_addr));
- save_item(STRUCT_MEMBER(m_dma_channel, bank));
- save_item(STRUCT_MEMBER(m_dma_channel, trans_size));
- save_item(STRUCT_MEMBER(m_dma_channel, ibank));
- save_item(STRUCT_MEMBER(m_dma_channel, hdma_addr));
- save_item(STRUCT_MEMBER(m_dma_channel, hdma_iaddr));
- save_item(STRUCT_MEMBER(m_dma_channel, hdma_line_counter));
- save_item(STRUCT_MEMBER(m_dma_channel, unk));
- save_item(STRUCT_MEMBER(m_dma_channel, do_transfer));
- save_item(STRUCT_MEMBER(m_dma_channel, dma_disabled));
-
- save_item(NAME(m_hblank_offset));
- save_item(NAME(m_wram_address));
- save_item(NAME(m_htime));
- save_item(NAME(m_vtime));
- save_item(NAME(m_hdmaen));
- save_item(NAME(m_data1));
- save_item(NAME(m_data2));
- save_item(NAME(m_read_idx));
- save_item(NAME(m_dma_regs));
- save_item(NAME(m_cpu_regs));
- save_item(NAME(m_oldjoy1_latch));
- save_item(NAME(m_input_disabled));
- save_item(NAME(m_game_over_flag));
- save_item(NAME(m_joy_flag));
-
- m_is_nss = 0;
- m_is_sfcbox = 0;
- m_input_disabled = 0;
- m_game_over_flag = 0;
- m_joy_flag = 1;
- m_wram_address = 0;
-}
-
-void snes_state::machine_reset()
-{
- snes_init_ram();
-
- /* init DMA regs to be 0xff */
- for (auto & elem : m_dma_channel)
- {
- elem.dmap = 0xff;
- elem.dest_addr = 0xff;
- elem.src_addr = 0xffff;
- elem.bank = 0xff;
- elem.trans_size = 0xffff;
- elem.ibank = 0xff;
- elem.hdma_addr = 0xffff;
- elem.hdma_line_counter = 0xff;
- elem.unk = 0xff;
- }
-
- // reset does this to these registers
- SNES_CPU_REG(NMITIMEN) = 0;
- m_htime = 0x1ff;
- m_vtime = 0x1ff;
-
- m_ppu->reset_interlace();
-}
-
-
-void snes_state::rom_map_setup(uint32_t size)
-{
- int i;
- // setup the rom_bank_map array to faster ROM read
- for (i = 0; i < size / 0x8000; i++)
- m_cart.rom_bank_map[i] = i;
-
- // fill up remaining blocks with mirrors
- while (i % 256)
- {
- int j = 0, repeat_banks;
- while ((i % (256 >> j)) && j < 8)
- j++;
- repeat_banks = i % (256 >> (j - 1));
- for (int k = 0; k < repeat_banks; k++)
- m_cart.rom_bank_map[i + k] = m_cart.rom_bank_map[i + k - repeat_banks];
- i += repeat_banks;
- }
-}
-
-/* for mame we use an init, maybe we will need more for the different games */
-void snes_state::init_snes()
-{
- m_cart.m_rom_size = memregion("user3")->bytes();
- m_cart.m_rom = memregion("user3")->base();
- rom_map_setup(m_cart.m_rom_size);
-
- m_cart.m_nvram_size = 0;
- if (m_cart.m_rom[0x7fd8] > 0)
- {
- uint32_t nvram_size = (1024 << m_cart.m_rom[0x7fd8]);
- if (nvram_size > 0x40000)
- nvram_size = 0x40000;
-
- m_cart.m_nvram = make_unique_clear<uint8_t[]>(nvram_size);
- m_cart.m_nvram_size = nvram_size;
- }
-
- /* all NSS games seem to use MODE 20 */
- m_cart.mode = SNES_MODE_20;
-}
-
-void snes_state::init_snes_hirom()
-{
- m_cart.m_rom_size = memregion("user3")->bytes();
- m_cart.m_rom = memregion("user3")->base();
- rom_map_setup(m_cart.m_rom_size);
-
- m_cart.m_nvram_size = 0;
- if (m_cart.m_rom[0xffd8] > 0)
- {
- uint32_t nvram_size = (1024 << m_cart.m_rom[0xffd8]);
- if (nvram_size > 0x40000)
- nvram_size = 0x40000;
-
- m_cart.m_nvram = make_unique_clear<uint8_t[]>(nvram_size);
- m_cart.m_nvram_size = nvram_size;
- }
-
- m_cart.mode = SNES_MODE_21;
-}
-
-
-/*************************************
-
- HDMA
-
-*************************************/
-
-inline int snes_state::dma_abus_valid( uint32_t address )
-{
- if((address & 0x40ff00) == 0x2100) return 0; //$[00-3f|80-bf]:[2100-21ff]
- if((address & 0x40fe00) == 0x4000) return 0; //$[00-3f|80-bf]:[4000-41ff]
- if((address & 0x40ffe0) == 0x4200) return 0; //$[00-3f|80-bf]:[4200-421f]
- if((address & 0x40ff80) == 0x4300) return 0; //$[00-3f|80-bf]:[4300-437f]
-
- return 1;
-}
-
-inline uint8_t snes_state::abus_read( address_space &space, uint32_t abus )
-{
- if (!dma_abus_valid(abus))
- return 0;
-
- return space.read_byte(abus);
-}
-
-inline void snes_state::dma_transfer( address_space &space, uint8_t dma, uint32_t abus, uint16_t bbus )
-{
- if (m_dma_channel[dma].dmap & 0x80) /* PPU->CPU */
- {
- if (bbus == 0x2180 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000))
- {
- //illegal WRAM->WRAM transfer (bus conflict)
- //no read occurs; write does occur
- space.write_byte(abus, 0x00);
- return;
- }
- else
- {
- if (!dma_abus_valid(abus))
- return;
-
- space.write_byte(abus, space.read_byte(bbus));
- return;
- }
- }
- else /* CPU->PPU */
- {
- if (bbus == 0x2180 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000))
- {
- //illegal WRAM->WRAM transfer (bus conflict)
- //read most likely occurs; no write occurs
- //read is irrelevant, as it cannot be observed by software
- return;
- }
- else
- {
- space.write_byte(bbus, abus_read(space, abus));
- return;
- }
- }
-}
-
-/* WIP: These have the advantage to automatically update the address, but then we would need to
-check again if the transfer is direct/indirect at each step... is it worth? */
-inline uint32_t snes_state::get_hdma_addr( int dma )
-{
- return (m_dma_channel[dma].bank << 16) | (m_dma_channel[dma].hdma_addr++);
-}
-
-inline uint32_t snes_state::get_hdma_iaddr( int dma )
-{
- return (m_dma_channel[dma].ibank << 16) | (m_dma_channel[dma].trans_size++);
-}
-
-inline int snes_state::is_last_active_channel( int dma )
-{
- for (int i = dma + 1; i < 8; i++)
- {
- if (BIT(m_hdmaen, i) && m_dma_channel[i].hdma_line_counter)
- return 0; // there is still at least another channel with incomplete HDMA
- }
-
- // if we arrive here, all hdma transfers from (dma + 1) to 7 are completed or not active
- return 1;
-}
-
-void snes_state::hdma_update( address_space &space, int dma )
-{
- uint32_t abus = get_hdma_addr(dma);
-
- m_dma_channel[dma].hdma_line_counter = abus_read(space, abus);
-
- if (m_dma_channel[dma].dmap & 0x40)
- {
- /* One oddity: if $43xA is 0 and this is the last active HDMA channel for this scanline, only load
- one byte for Address, and use the $00 for the low byte. So Address ends up incremented one less than
- otherwise expected */
-
- abus = get_hdma_addr(dma);
- m_dma_channel[dma].trans_size = abus_read(space, abus) << 8;
-
- if (m_dma_channel[dma].hdma_line_counter || !is_last_active_channel(dma))
- {
- // we enter here if we have more transfers to be done or if there are other active channels after this one
- abus = get_hdma_addr(dma);
- m_dma_channel[dma].trans_size >>= 8;
- m_dma_channel[dma].trans_size |= abus_read(space, abus) << 8;
- }
- }
-
- if (!m_dma_channel[dma].hdma_line_counter)
- m_hdmaen &= ~(1 << dma);
-
- m_dma_channel[dma].do_transfer = 1;
-}
-
-void snes_state::hdma_init( address_space &space )
-{
- m_hdmaen = SNES_CPU_REG(HDMAEN);
- for (int i = 0; i < 8; i++)
- {
- if (BIT(m_hdmaen, i))
- {
- m_dma_channel[i].hdma_addr = m_dma_channel[i].src_addr;
- hdma_update(space, i);
- }
- }
-}
-
-void snes_state::hdma( address_space &space )
-{
- uint16_t bbus;
- uint32_t abus;
-
- for (int i = 0; i < 8; i++)
- {
- if (BIT(m_hdmaen, i))
- {
- if (m_dma_channel[i].do_transfer)
- {
- /* Get transfer addresses */
- if (m_dma_channel[i].dmap & 0x40) /* Indirect */
- abus = (m_dma_channel[i].ibank << 16) + m_dma_channel[i].trans_size;
- else /* Absolute */
- abus = (m_dma_channel[i].bank << 16) + m_dma_channel[i].hdma_addr;
-
- bbus = m_dma_channel[i].dest_addr + 0x2100;
-
-
-
- switch (m_dma_channel[i].dmap & 0x07)
- {
- case 0: /* 1 register write once (1 byte: p ) */
- dma_transfer(space, i, abus++, bbus);
- break;
- case 5: /* 2 registers write twice alternate (4 bytes: p, p+1, p, p+1) */
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus + 1);
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus + 1);
- break;
- case 1: /* 2 registers write once (2 bytes: p, p+1 ) */
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus + 1);
- break;
- case 2: /* 1 register write twice (2 bytes: p, p ) */
- case 6:
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus);
- break;
- case 3: /* 2 registers write twice each (4 bytes: p, p, p+1, p+1) */
- case 7:
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus + 1);
- dma_transfer(space, i, abus++, bbus + 1);
- break;
- case 4: /* 4 registers write once (4 bytes: p, p+1, p+2, p+3) */
- dma_transfer(space, i, abus++, bbus);
- dma_transfer(space, i, abus++, bbus + 1);
- dma_transfer(space, i, abus++, bbus + 2);
- dma_transfer(space, i, abus++, bbus + 3);
- break;
- default:
-#ifdef MAME_DEBUG
- osd_printf_debug( " HDMA of unsupported type: %d\n", m_dma_channel[i].dmap & 0x07);
-#endif
- break;
- }
-
- if (m_dma_channel[i].dmap & 0x40) /* Indirect */
- m_dma_channel[i].trans_size = abus;
- else /* Absolute */
- m_dma_channel[i].hdma_addr = abus;
-
- }
- }
- }
-
- for (int i = 0; i < 8; i++)
- {
- if (BIT(m_hdmaen, i))
- {
- m_dma_channel[i].do_transfer = (--m_dma_channel[i].hdma_line_counter) & 0x80;
-
- if (!(m_dma_channel[i].hdma_line_counter & 0x7f))
- hdma_update(space, i);
- }
- }
-}
-
-void snes_state::dma( address_space &space, uint8_t channels )
-{
- int8_t increment;
- uint16_t bbus;
- uint32_t abus, abus_bank;
- uint16_t length;
-
- /* FIXME: we also need to round to the nearest 8 master cycles */
-
- /* Assume priority of the 8 DMA channels is 0-7 */
- for (int i = 0; i < 8; i++)
- {
- if (BIT(channels, i))
- {
- /* FIXME: the following should be used to stop DMA if the same channel is used by HDMA (being set to 1 in snes_hdma)
- However, this cannot be implemented as is atm, because currently DMA transfers always happen as soon as they are enabled... */
- m_dma_channel[i].dma_disabled = 0;
-
- //printf( "Making a transfer on channel %d\n", i );
- /* Find transfer addresses */
- abus = m_dma_channel[i].src_addr;
- abus_bank = m_dma_channel[i].bank << 16;
- bbus = m_dma_channel[i].dest_addr + 0x2100;
-
- //printf("Address: %06x\n", abus | abus_bank);
- /* Auto increment */
- if (m_dma_channel[i].dmap & 0x8)
- increment = 0;
- else
- {
- if (m_dma_channel[i].dmap & 0x10)
- increment = -1;
- else
- increment = 1;
- }
-
- /* Number of bytes to transfer */
- length = m_dma_channel[i].trans_size;
-
-// printf( "DMA-Ch %d: len: %X, abus: %X, bbus: %X, incr: %d, dir: %s, type: %d\n", i, length, abus | abus_bank, bbus, increment, m_dma_channel[i].dmap & 0x80 ? "PPU->CPU" : "CPU->PPU", m_dma_channel[i].dmap & 0x07);
-
-#ifdef SNES_DBG_DMA
- osd_printf_debug( "DMA-Ch %d: len: %X, abus: %X, bbus: %X, incr: %d, dir: %s, type: %d\n", i, length, abus | abus_bank, bbus, increment, m_dma_channel[i].dmap & 0x80 ? "PPU->CPU" : "CPU->PPU", m_dma_channel[i].dmap & 0x07);
-#endif
-
- switch (m_dma_channel[i].dmap & 0x07)
- {
- case 0: /* 1 register write once */
- case 2: /* 1 register write twice */
- case 6: /* 1 register write twice */
- do
- {
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus);
- abus += increment;
- } while (--length && !m_dma_channel[i].dma_disabled);
- break;
- case 1: /* 2 registers write once */
- case 5: /* 2 registers write twice alternate */
- do
- {
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 1);
- abus += increment;
- } while (--length && !m_dma_channel[i].dma_disabled);
- break;
- case 3: /* 2 registers write twice each */
- case 7: /* 2 registers write twice each */
- do
- {
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 1);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 1);
- abus += increment;
- } while (--length && !m_dma_channel[i].dma_disabled);
- break;
- case 4: /* 4 registers write once */
- do
- {
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 1);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 2);
- abus += increment;
- if (!(--length) || m_dma_channel[i].dma_disabled)
- break;
- dma_transfer(space, i, (abus & 0xffff) | abus_bank, bbus + 3);
- abus += increment;
- } while (--length && !m_dma_channel[i].dma_disabled);
- break;
- default:
-#ifdef MAME_DEBUG
- osd_printf_debug(" DMA of unsupported type: %d\n", m_dma_channel[i].dmap & 0x07);
-#endif
- break;
- }
-
- /* We're done, so write the new abus back to the registers */
- m_dma_channel[i].src_addr = abus;
- m_dma_channel[i].trans_size = 0;
- }
- }
-
-}