diff --git a/.gitattributes b/.gitattributes index b9087766b07..78a0ecdba3c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1337,6 +1337,7 @@ src/emu/machine/s3c2410.h svneol=native#text/plain src/emu/machine/s3c2440.c svneol=native#text/plain src/emu/machine/s3c2440.h svneol=native#text/plain src/emu/machine/s3c24xx.c svneol=native#text/plain +src/emu/machine/saturn.c svneol=native#text/plain src/emu/machine/scsibus.c svneol=native#text/plain src/emu/machine/scsibus.h svneol=native#text/plain src/emu/machine/scsicb.c svneol=native#text/plain @@ -3445,7 +3446,6 @@ src/mame/drivers/safarir.c svneol=native#text/plain src/mame/drivers/sandscrp.c svneol=native#text/plain src/mame/drivers/sangho.c svneol=native#text/plain src/mame/drivers/sanremo.c svneol=native#text/plain -src/mame/drivers/saturn.c svneol=native#text/plain src/mame/drivers/sauro.c svneol=native#text/plain src/mame/drivers/savquest.c svneol=native#text/plain src/mame/drivers/sbasketb.c svneol=native#text/plain @@ -6196,6 +6196,7 @@ src/mess/drivers/sacstate.c svneol=native#text/plain src/mess/drivers/sage2.c svneol=native#text/plain src/mess/drivers/samcoupe.c svneol=native#text/plain src/mess/drivers/sapi1.c svneol=native#text/plain +src/mess/drivers/saturn.c svneol=native#text/plain src/mess/drivers/savia84.c svneol=native#text/plain src/mess/drivers/sbc6510.c svneol=native#text/plain src/mess/drivers/sc1.c svneol=native#text/plain diff --git a/src/emu/emu.mak b/src/emu/emu.mak index 8539a623f09..0a3c9baf275 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -255,6 +255,7 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/s3c2410.o \ $(EMUMACHINE)/s3c2440.o \ $(EMUMACHINE)/s3520cf.o \ + $(EMUMACHINE)/saturn.o \ $(EMUMACHINE)/scsibus.o \ $(EMUMACHINE)/scsicb.o \ $(EMUMACHINE)/scsicd.o \ @@ -265,7 +266,7 @@ EMUMACHINEOBJS = \ $(EMUMACHINE)/secflash.o \ $(EMUMACHINE)/seibu_cop.o \ $(EMUMACHINE)/smc91c9x.o \ - $(EMUMACHINE)/smpc.o \ + $(EMUMACHINE)/smpc.o \ $(EMUMACHINE)/stvcd.o \ $(EMUMACHINE)/tc009xlvc.o \ $(EMUMACHINE)/timekpr.o \ diff --git a/src/emu/machine/saturn.c b/src/emu/machine/saturn.c new file mode 100644 index 00000000000..82fe90352d6 --- /dev/null +++ b/src/emu/machine/saturn.c @@ -0,0 +1,939 @@ +#include "emu.h" +#include "includes/stv.h" +#include "machine/scudsp.h" +#include "cpu/sh2/sh2.h" + +/* TODO: do this in a verboselog style */ +#define LOG_CDB 0 +#define LOG_SCU 1 +#define LOG_IRQ 0 +#define LOG_IOGA 0 + +int saturn_state::DectoBCD(int num) +{ + int i, cnt = 0, tmp, res = 0; + + while (num > 0) { + tmp = num; + while (tmp >= 10) tmp %= 10; + for (i=0; i +29 0074 +30 0078 +31 007c DMA Status Register +32 0080 DSP Program Control Port +33 0084 DSP Program RAM Data Port +34 0088 DSP Data RAM Address Port +35 008c DSP Data RAM Data Port +36 0090 Timer 0 Compare Register +37 0094 Timer 1 Set Data Register +38 0098 Timer 1 Mode Register +39 009c +40 00a0 Interrupt Mask Register +41 00a4 Interrupt Status Register +42 00a8 A-Bus Interrupt Acknowledge +43 00ac +44 00b0 A-Bus Set Register +45 00b4 +46 00b8 A-Bus Refresh Register +47 00bc +48 00c0 +49 00c4 SCU SDRAM Select Register +50 00c8 SCU Version Register +51 00cc +52 00cf +=================================================================================== +DMA Status Register(32-bit): +xxxx xxxx x--- xx-- xx-- xx-- xx-- xx-- UNUSED +---- ---- -x-- ---- ---- ---- ---- ---- DMA DSP-Bus access +---- ---- --x- ---- ---- ---- ---- ---- DMA B-Bus access +---- ---- ---x ---- ---- ---- ---- ---- DMA A-Bus access +---- ---- ---- --x- ---- ---- ---- ---- DMA lv 1 interrupt +---- ---- ---- ---x ---- ---- ---- ---- DMA lv 0 interrupt +---- ---- ---- ---- --x- ---- ---- ---- DMA lv 2 in stand-by +---- ---- ---- ---- ---x ---- ---- ---- DMA lv 2 in operation +---- ---- ---- ---- ---- --x- ---- ---- DMA lv 1 in stand-by +---- ---- ---- ---- ---- ---x ---- ---- DMA lv 1 in operation +---- ---- ---- ---- ---- ---- --x- ---- DMA lv 0 in stand-by +---- ---- ---- ---- ---- ---- ---x ---- DMA lv 0 in operation +---- ---- ---- ---- ---- ---- ---- --x- DSP side DMA in stand-by +---- ---- ---- ---- ---- ---- ---- ---x DSP side DMA in operation + +**********************************************************************************/ +/* +DMA TODO: +-Remove CD transfer DMA hack (tied with CD block bug(s)?) +-Add timings(but how fast are each DMA?). +-Add level priority & DMA status register. +*/ + +#define DIRECT_MODE(_lv_) (!(m_scu_regs[5+(_lv_*8)] & 0x01000000)) +#define INDIRECT_MODE(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x01000000) +#define DRUP(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x00010000) +#define DWUP(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x00000100) + +/*These macros sets the various DMA status flags.*/ +#define DnMV_1(_ch_) m_scu.status|=(0x10 << 4 * _ch_) +#define DnMV_0(_ch_) m_scu.status&=~(0x10 << 4 * _ch_) + +/*For area checking*/ +#define BIOS_BUS(var) (var & 0x07000000) == 0 +#define ABUS(_lv_) ((m_scu.src[_lv_] & 0x07000000) >= 0x02000000) && ((m_scu.src[_lv_] & 0x07000000) <= 0x04000000) +#define BBUS(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05a00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05ffffff) +#define VDP1_REGS(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05d00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05dfffff) +#define VDP2(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05e00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05fdffff) +#define WORK_RAM_L(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x00200000) && ((scu_##_lv_ & 0x07ffffff) <= 0x002fffff) +#define WORK_RAM_H(var) (var & 0x07000000) == 0x06000000 +#define SOUND_RAM(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05a00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05afffff) + +void saturn_state::scu_do_transfer(UINT8 event) +{ + address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); + int i; + + for(i=0;i<3;i++) + { + if(m_scu.enable_mask[i] && m_scu.start_factor[i] == event) + { + if(DIRECT_MODE(i)) { scu_dma_direct(space,i); } + else { scu_dma_indirect(space,i); } + } + } +} + +/* test pending irqs */ +void saturn_state::scu_test_pending_irq() +{ + int i; + const int irq_level[32] = { 0xf, 0xe, 0xd, 0xc, + 0xb, 0xa, 0x9, 0x8, + 0x8, 0x6, 0x6, 0x5, + 0x3, 0x2, -1, -1, + 0x7, 0x7, 0x7, 0x7, + 0x4, 0x4, 0x4, 0x4, + 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1 }; + + for(i=0;i<32;i++) + { + if((!(m_scu.ism & 1 << i)) && (m_scu.ist & 1 << i)) + { + if(irq_level[i] != -1) /* TODO: cheap check for undefined irqs */ + { + m_maincpu->set_input_line_and_vector(irq_level[i], HOLD_LINE, 0x40 + i); + m_scu.ist &= ~(1 << i); + return; /* avoid spurious irqs, correct? */ + } + } + } +} + +READ32_MEMBER(saturn_state::saturn_scu_r) +{ + UINT32 res; + + /*TODO: write only registers must return 0 or open bus */ + switch(offset) + { + case 0x5c/4: + // Super Major League and Shin Megami Tensei - Akuma Zensho reads from there (undocumented), DMA status mirror? + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) DMA status reg read\n",space.device().safe_pc()); + res = m_scu.status; + break; + case 0x7c/4: + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) DMA status reg read\n",space.device().safe_pc()); + res = m_scu.status; + break; + case 0x80/4: + res = dsp_prg_ctrl_r(space); + break; + case 0x8c/4: + if(LOG_SCU && !space.debugger_access()) logerror( "DSP mem read at %08X\n", m_scu_regs[34]); + res = dsp_ram_addr_r(); + break; + case 0xa0/4: + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) IRQ mask reg read %08x MASK=%08x\n",space.device().safe_pc(),mem_mask,m_scu_regs[0xa0/4]); + res = m_scu.ism; + break; + case 0xa4/4: + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) IRQ status reg read MASK=%08x IST=%08x | ISM=%08x\n",space.device().safe_pc(),mem_mask,m_scu.ist,m_scu.ism); + /* TODO: Bug! trips an HW fault. Basically, it tries to read the IST bit 1 with that irq enabled. + Densetsu no Ogre Battle doesn't like this, so it needs investigation ... + */ +// res = m_scu.ist | ~m_scu.ism; + res = m_scu.ist; + break; + case 0xc8/4: + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) SCU version reg read\n",space.device().safe_pc()); + res = 0x00000004;/*SCU Version 4, OK? */ + break; + default: + if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) SCU reg read at %d = %08x\n",space.device().safe_pc(),offset,m_scu_regs[offset]); + res = m_scu_regs[offset]; + break; + } + + return res; +} + +#define DMA_CH ((offset & 0x18) / 8) + +WRITE32_MEMBER(saturn_state::saturn_scu_w) +{ + COMBINE_DATA(&m_scu_regs[offset]); + + switch(offset) + { + /*LV 0 DMA*/ + case 0x00/4: case 0x20/4: case 0x40/4: m_scu.src[DMA_CH] = ((m_scu_regs[offset] & 0x07ffffff) >> 0); break; + case 0x04/4: case 0x24/4: case 0x44/4: m_scu.dst[DMA_CH] = ((m_scu_regs[offset] & 0x07ffffff) >> 0); break; + case 0x08/4: case 0x28/4: case 0x48/4: m_scu.size[DMA_CH] = ((m_scu_regs[offset] & ((offset == 2) ? 0x000fffff : 0xfff)) >> 0); break; + case 0x0c/4: case 0x2c/4: case 0x4c/4: + m_scu.src_add[DMA_CH] = (m_scu_regs[offset] & 0x100) ? 4 : 0; + m_scu.dst_add[DMA_CH] = 1 << (m_scu_regs[offset] & 7); + if(m_scu.dst_add[DMA_CH] == 1) { m_scu.dst_add[DMA_CH] = 0; } + break; + case 0x10/4: case 0x30/4: case 0x50/4: + m_scu.enable_mask[DMA_CH] = (data & 0x100) >> 8; + if(m_scu.enable_mask[DMA_CH] && m_scu.start_factor[DMA_CH] == 7 && m_scu_regs[offset] & 1) + { + if(DIRECT_MODE(DMA_CH)) { scu_dma_direct(space,DMA_CH); } + else { scu_dma_indirect(space,DMA_CH); } + m_scu_regs[offset]&=~1;//disable starting bit. + } + break; + case 0x14/4: case 0x34/4: case 0x54/4: + if(INDIRECT_MODE(DMA_CH)) + { + //if(LOG_SCU) logerror("Indirect Mode DMA lv %d set\n",DMA_CH); + if(!DWUP(DMA_CH)) m_scu.index[DMA_CH] = m_scu.dst[DMA_CH]; + } + + m_scu.start_factor[DMA_CH] = m_scu_regs[offset] & 7; + break; + + case 0x60/4: + if(LOG_SCU) logerror("DMA Forced Stop Register set = %02x\n",m_scu_regs[24]); + break; + case 0x7c/4: if(LOG_SCU) logerror("Warning: DMA status WRITE! Offset %02x(%d)\n",offset*4,offset); break; + /*DSP section*/ + case 0x80/4: + /* TODO: you can't overwrite some flags with this */ + dsp_prg_ctrl_w(space, m_scu_regs[offset]); + if(LOG_SCU) logerror("SCU DSP: Program Control Port Access %08x\n",data); + break; + case 0x84/4: + dsp_prg_data(m_scu_regs[offset]); + if(LOG_SCU) logerror("SCU DSP: Program RAM Data Port Access %08x\n",data); + break; + case 0x88/4: + dsp_ram_addr_ctrl(m_scu_regs[offset]); + if(LOG_SCU) logerror("SCU DSP: Data RAM Address Port Access %08x\n",data); + break; + case 0x8c/4: + dsp_ram_addr_w(m_scu_regs[offset]); + if(LOG_SCU) logerror("SCU DSP: Data RAM Data Port Access %08x\n",data); + break; + case 0x90/4: /*if(LOG_SCU) logerror("timer 0 compare data = %03x\n",m_scu_regs[36]);*/ break; + case 0x94/4: /*if(LOG_SCU) logerror("timer 1 set data = %08x\n",m_scu_regs[37]);*/ break; + case 0x98/4: /*if(LOG_SCU) logerror("timer 1 mode data = %08x\n",m_scu_regs[38]);*/ break; + case 0xa0/4: /* IRQ mask */ + m_scu.ism = m_scu_regs[0xa0/4]; + scu_test_pending_irq(); + break; + case 0xa4/4: /* IRQ control */ + if(LOG_SCU) logerror("PC=%08x IRQ status reg set:%08x %08x\n",space.device().safe_pc(),m_scu_regs[41],mem_mask); + m_scu.ist &= m_scu_regs[offset]; + scu_test_pending_irq(); + break; + case 0xa8/4: + /* This sends an irq signal to the extra devices connected to the A-Bus, not really needed for now. */ + //if(LOG_SCU) logerror("A-Bus IRQ ACK %08x\n",m_scu_regs[42]); + break; + case 0xc4/4: if(LOG_SCU) logerror("SCU SDRAM set: %02x\n",m_scu_regs[49]); break; + default: if(LOG_SCU) logerror("Warning: unused SCU reg set %d = %08x\n",offset,data); + } +} + +/*Lv 0 DMA end irq*/ +TIMER_CALLBACK_MEMBER(saturn_state::dma_lv0_ended ) +{ + if(!(m_scu.ism & IRQ_DMALV0)) + m_maincpu->set_input_line_and_vector(5, HOLD_LINE, 0x4b); + else + m_scu.ist |= (IRQ_DMALV0); + + DnMV_0(0); +} + +/*Lv 1 DMA end irq*/ +TIMER_CALLBACK_MEMBER(saturn_state::dma_lv1_ended) +{ + if(!(m_scu.ism & IRQ_DMALV1)) + m_maincpu->set_input_line_and_vector(6, HOLD_LINE, 0x4a); + else + m_scu.ist |= (IRQ_DMALV1); + + DnMV_0(1); +} + +/*Lv 2 DMA end irq*/ +TIMER_CALLBACK_MEMBER(saturn_state::dma_lv2_ended) +{ + if(!(m_scu.ism & IRQ_DMALV2)) + m_maincpu->set_input_line_and_vector(6, HOLD_LINE, 0x49); + else + m_scu.ist |= (IRQ_DMALV2); + + DnMV_0(2); +} + +void saturn_state::scu_single_transfer(address_space &space, UINT32 src, UINT32 dst,UINT8 *src_shift) +{ + UINT32 src_data; + + if(src & 1) + { + /* Road Blaster does a work ram h to color ram with offsetted source address, do some data rotation */ + src_data = ((space.read_dword(src & 0x07fffffc) & 0x00ffffff)<<8); + src_data |= ((space.read_dword((src & 0x07fffffc)+4) & 0xff000000) >> 24); + src_data >>= (*src_shift)*16; + } + else + src_data = space.read_dword(src & 0x07fffffc) >> (*src_shift)*16; + + space.write_word(dst,src_data); + + *src_shift ^= 1; +} + +void saturn_state::scu_dma_direct(address_space &space, UINT8 dma_ch) +{ + UINT32 tmp_src,tmp_dst,total_size; + UINT8 cd_transfer_flag; + + if(m_scu.src_add[dma_ch] == 0 || (m_scu.dst_add[dma_ch] != 2 && m_scu.dst_add[dma_ch] != 4)) + { + if(LOG_SCU) printf("DMA lv %d transfer START\n" + "Start %08x End %08x Size %04x\n",dma_ch,m_scu.src[dma_ch],m_scu.dst[dma_ch],m_scu.size[dma_ch]); + if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]); + } + + /* TODO: Game Basic and World Cup 98 trips this, according to the docs the SCU can't transfer from BIOS area (can't communicate from/to that bus) */ + if(BIOS_BUS(m_scu.src[dma_ch])) + { + popmessage("Warning: SCU transfer from BIOS area, contact MAMEdev"); + if(!(m_scu.ism & IRQ_DMAILL)) + m_maincpu->set_input_line_and_vector(3, HOLD_LINE, 0x4c); + else + m_scu.ist |= (IRQ_DMAILL); + return; + } + + DnMV_1(dma_ch); + + /* max size */ + if(m_scu.size[dma_ch] == 0) { m_scu.size[dma_ch] = (dma_ch == 0) ? 0x00100000 : 0x1000; } + + tmp_src = tmp_dst = 0; + + total_size = m_scu.size[dma_ch]; + if(!(DRUP(dma_ch))) tmp_src = m_scu.src[dma_ch]; + if(!(DWUP(dma_ch))) tmp_dst = m_scu.dst[dma_ch]; + + cd_transfer_flag = m_scu.src_add[dma_ch] == 0 && m_scu.src[dma_ch] == 0x05818000; + + /* TODO: Many games directly accesses CD-ROM register 0x05818000, it must be a dword access with current implementation otherwise it won't work */ + if(cd_transfer_flag) + { + int i; + if(WORK_RAM_H(m_scu.dst[dma_ch])) + m_scu.dst_add[dma_ch] = 4; + else + m_scu.dst_add[dma_ch] <<= 1; + + for (i = 0; i < m_scu.size[dma_ch];i+=m_scu.dst_add[dma_ch]) + { + space.write_dword(m_scu.dst[dma_ch],space.read_dword(m_scu.src[dma_ch])); + if(m_scu.dst_add[dma_ch] == 8) + space.write_dword(m_scu.dst[dma_ch]+4,space.read_dword(m_scu.src[dma_ch])); + + m_scu.src[dma_ch]+=m_scu.src_add[dma_ch]; + m_scu.dst[dma_ch]+=m_scu.dst_add[dma_ch]; + } + } + else + { + int i; + UINT8 src_shift; + + src_shift = ((m_scu.src[dma_ch] & 2) >> 1) ^ 1; + + for (i = 0; i < m_scu.size[dma_ch];i+=2) + { + scu_single_transfer(space,m_scu.src[dma_ch],m_scu.dst[dma_ch],&src_shift); + + if(src_shift) + m_scu.src[dma_ch]+=m_scu.src_add[dma_ch]; + + /* if target is Work RAM H, the add value is fixed, behaviour confirmed by Final Romance 2, Virtual Mahjong and Burning Rangers */ + m_scu.dst[dma_ch]+=(WORK_RAM_H(m_scu.dst[dma_ch])) ? 2 : m_scu.dst_add[dma_ch]; + } + } + + /* Burning Rangers doesn't agree with this. */ +// m_scu.size[dma_ch] = 0; + if(!(DRUP(dma_ch))) m_scu.src[dma_ch] = tmp_src; + if(!(DWUP(dma_ch))) m_scu.dst[dma_ch] = tmp_dst; + + { + /*TODO: change DMA into DRQ model. Timing is a guess. */ + switch(dma_ch) + { + case 0: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv0_ended),this)); break; + case 1: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv1_ended),this)); break; + case 2: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv2_ended),this)); break; + } + } +} + +void saturn_state::scu_dma_indirect(address_space &space,UINT8 dma_ch) +{ + /*Helper to get out of the cycle*/ + UINT8 job_done = 0; + /*temporary storage for the transfer data*/ + UINT32 tmp_src; + UINT32 indirect_src,indirect_dst; + INT32 indirect_size; + UINT32 total_size = 0; + + DnMV_1(dma_ch); + + m_scu.index[dma_ch] = m_scu.dst[dma_ch]; + + do{ + tmp_src = m_scu.index[dma_ch]; + + indirect_size = space.read_dword(m_scu.index[dma_ch]); + indirect_src = space.read_dword(m_scu.index[dma_ch]+8); + indirect_dst = space.read_dword(m_scu.index[dma_ch]+4); + + /*Indirect Mode end factor*/ + if(indirect_src & 0x80000000) + job_done = 1; + + if(m_scu.src_add[dma_ch] == 0 || (m_scu.dst_add[dma_ch] != 2)) + { + if(LOG_SCU) printf("DMA lv %d indirect mode transfer START\n" + "Index %08x Start %08x End %08x Size %04x\n",dma_ch,tmp_src,indirect_src,indirect_dst,indirect_size); + if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]); + } + + indirect_src &=0x07ffffff; + indirect_dst &=0x07ffffff; + indirect_size &= ((dma_ch == 0) ? 0xfffff : 0x3ffff); //TODO: Guardian Heroes sets up a 0x23000 transfer for the FMV? + + if(indirect_size == 0) { indirect_size = (dma_ch == 0) ? 0x00100000 : 0x2000; } + + { + int i; + UINT8 src_shift; + + src_shift = ((indirect_src & 2) >> 1) ^ 1; + + for (i = 0; i < indirect_size;i+=2) + { + scu_single_transfer(space,indirect_src,indirect_dst,&src_shift); + + if(src_shift) + indirect_src+=m_scu.src_add[dma_ch]; + + indirect_dst+= (WORK_RAM_H(indirect_dst)) ? 2 : m_scu.dst_add[dma_ch]; + } + } + + /* Guess: Size + data acquire (1 cycle for src/dst/size) */ + total_size += indirect_size + 3*4; + + //if(DRUP(0)) space.write_dword(tmp_src+8,m_scu.src[0]|job_done ? 0x80000000 : 0); + //if(DWUP(0)) space.write_dword(tmp_src+4,m_scu.dst[0]); + + m_scu.index[dma_ch] = tmp_src+0xc; + + }while(job_done == 0); + + { + /*TODO: change DMA into DRQ model. Timing is a guess. */ + switch(dma_ch) + { + case 0: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv0_ended),this)); break; + case 1: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv1_ended),this)); break; + case 2: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv2_ended),this)); break; + } + } +} + + +/**************************************************************************************/ + +WRITE16_MEMBER(saturn_state::saturn_soundram_w) +{ + //machine().scheduler().synchronize(); // force resync + + COMBINE_DATA(&m_sound_ram[offset]); +} + +READ16_MEMBER(saturn_state::saturn_soundram_r) +{ + //machine().scheduler().synchronize(); // force resync + + return m_sound_ram[offset]; +} + +/* communication,SLAVE CPU acquires data from the MASTER CPU and triggers an irq. */ +WRITE32_MEMBER(saturn_state::minit_w) +{ + //logerror("cpu %s (PC=%08X) MINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); + machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost)); + machine().scheduler().trigger(1000); + machine().scheduler().synchronize(); // force resync + sh2_set_frt_input(m_slave, PULSE_LINE); +} + +WRITE32_MEMBER(saturn_state::sinit_w) +{ + //logerror("cpu %s (PC=%08X) SINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); + machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost)); + machine().scheduler().synchronize(); // force resync + sh2_set_frt_input(m_maincpu, PULSE_LINE); +} + +/* +TODO: +Some games seems to not like either MAME's interleave system and/or SH-2 DRC, causing an hard crash. +Reported games are: +Blast Wind (before FMV) +Choro Q Park (car selection) +060311E4: MOV.L R14,@-SP ;R14 = 0x60ffba0 / R15 = 0x60ffba0 +060311E6: MOV SP,R14 ;R14 = 0x60ffba0 / R15 = 0x60ffb9c / [0x60ffb9c] <- 0x60ffba0 +060311E8: MOV.L @SP+,R14 ;R14 = 0x60ffb9c / R15 = 0x60ffb9c / [0x60ffb9c] -> R14 +060311EA: RTS ;R14 = 0x60ffba0 / R15 = 0x60ffba0 +060311EC: NOP +06031734: MULS.W R9, R8 ;R14 = 0x60ffba0 / R15 = 0x60ffba0 / EA = 0x60311E4 +on DRC this becomes: +R14 0x6031b78 (cause of the crash later on), R15 = 0x60ffba4 and EA = 0 + +Shinrei Jusatsushi Taromaru (options menu) + +*/ + +WRITE32_MEMBER(saturn_state::saturn_minit_w) +{ + //logerror("cpu %s (PC=%08X) MINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); + if(m_fake_comms->read() & 1) + machine().scheduler().synchronize(); // force resync + else + { + machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost)); + machine().scheduler().trigger(1000); + } + + sh2_set_frt_input(m_slave, PULSE_LINE); +} + +WRITE32_MEMBER(saturn_state::saturn_sinit_w) +{ + //logerror("cpu %s (PC=%08X) SINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); + if(m_fake_comms->read() & 1) + machine().scheduler().synchronize(); // force resync + else + machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost)); + + sh2_set_frt_input(m_maincpu, PULSE_LINE); +} + + +READ8_MEMBER(saturn_state::saturn_backupram_r) +{ + if(!(offset & 1)) + return 0; // yes, it makes sure the "holes" are there. + + return m_backupram[offset >> 1] & 0xff; +} + +WRITE8_MEMBER(saturn_state::saturn_backupram_w) +{ + if(!(offset & 1)) + return; + + m_backupram[offset >> 1] = data; +} + +void saturn_state::scu_reset(void) +{ + m_scu.ism = 0xbfff; + m_scu.ist = 0; + m_scu.start_factor[0] = 7; + m_scu.start_factor[1] = 7; + m_scu.start_factor[2] = 7; + m_scu.status = 0; +} + +TIMER_CALLBACK_MEMBER(saturn_state::stv_rtc_increment) +{ + static const UINT8 dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 }; + static int year_num, year_count; + + /* + m_smpc.rtc_data[0] = DectoBCD(systime.local_time.year /100); + m_smpc.rtc_data[1] = DectoBCD(systime.local_time.year %100); + m_smpc.rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1); + m_smpc.rtc_data[3] = DectoBCD(systime.local_time.mday); + m_smpc.rtc_data[4] = DectoBCD(systime.local_time.hour); + m_smpc.rtc_data[5] = DectoBCD(systime.local_time.minute); + m_smpc.rtc_data[6] = DectoBCD(systime.local_time.second); + */ + + m_smpc.rtc_data[6]++; + + /* seconds from 9 -> 10*/ + if((m_smpc.rtc_data[6] & 0x0f) >= 0x0a) { m_smpc.rtc_data[6]+=0x10; m_smpc.rtc_data[6]&=0xf0; } + /* seconds from 59 -> 0 */ + if((m_smpc.rtc_data[6] & 0xf0) >= 0x60) { m_smpc.rtc_data[5]++; m_smpc.rtc_data[6] = 0; } + /* minutes from 9 -> 10 */ + if((m_smpc.rtc_data[5] & 0x0f) >= 0x0a) { m_smpc.rtc_data[5]+=0x10; m_smpc.rtc_data[5]&=0xf0; } + /* minutes from 59 -> 0 */ + if((m_smpc.rtc_data[5] & 0xf0) >= 0x60) { m_smpc.rtc_data[4]++; m_smpc.rtc_data[5] = 0; } + /* hours from 9 -> 10 */ + if((m_smpc.rtc_data[4] & 0x0f) >= 0x0a) { m_smpc.rtc_data[4]+=0x10; m_smpc.rtc_data[4]&=0xf0; } + /* hours from 23 -> 0 */ + if((m_smpc.rtc_data[4] & 0xff) >= 0x24) { m_smpc.rtc_data[3]++; m_smpc.rtc_data[2]+=0x10; m_smpc.rtc_data[4] = 0; } + /* week day name sunday -> monday */ + if((m_smpc.rtc_data[2] & 0xf0) >= 0x70) { m_smpc.rtc_data[2]&=0x0f; } + /* day number 9 -> 10 */ + if((m_smpc.rtc_data[3] & 0x0f) >= 0x0a) { m_smpc.rtc_data[3]+=0x10; m_smpc.rtc_data[3]&=0xf0; } + + // year BCD to dec conversion (for the leap year stuff) + { + year_num = (m_smpc.rtc_data[1] & 0xf); + + for(year_count = 0; year_count < (m_smpc.rtc_data[1] & 0xf0); year_count += 0x10) + year_num += 0xa; + + year_num += (m_smpc.rtc_data[0] & 0xf)*0x64; + + for(year_count = 0; year_count < (m_smpc.rtc_data[0] & 0xf0); year_count += 0x10) + year_num += 0x3e8; + } + + /* month +1 check */ + /* the RTC have a range of 1980 - 2100, so we don't actually need to support the leap year special conditions */ + if(((year_num % 4) == 0) && (m_smpc.rtc_data[2] & 0xf) == 2) + { + if((m_smpc.rtc_data[3] & 0xff) >= dpm[(m_smpc.rtc_data[2] & 0xf)-1]+1+1) + { m_smpc.rtc_data[2]++; m_smpc.rtc_data[3] = 0x01; } + } + else if((m_smpc.rtc_data[3] & 0xff) >= dpm[(m_smpc.rtc_data[2] & 0xf)-1]+1){ m_smpc.rtc_data[2]++; m_smpc.rtc_data[3] = 0x01; } + /* year +1 check */ + if((m_smpc.rtc_data[2] & 0x0f) > 12) { m_smpc.rtc_data[1]++; m_smpc.rtc_data[2] = (m_smpc.rtc_data[2] & 0xf0) | 0x01; } + /* year from 9 -> 10 */ + if((m_smpc.rtc_data[1] & 0x0f) >= 0x0a) { m_smpc.rtc_data[1]+=0x10; m_smpc.rtc_data[1]&=0xf0; } + /* year from 99 -> 100 */ + if((m_smpc.rtc_data[1] & 0xf0) >= 0xa0) { m_smpc.rtc_data[0]++; m_smpc.rtc_data[1] = 0; } + + // probably not SO precise, here just for reference ... + /* year from 999 -> 1000 */ + //if((m_smpc.rtc_data[0] & 0x0f) >= 0x0a) { m_smpc.rtc_data[0]+=0x10; m_smpc.rtc_data[0]&=0xf0; } + /* year from 9999 -> 0 */ + //if((m_smpc.rtc_data[0] & 0xf0) >= 0xa0) { m_smpc.rtc_data[0] = 0; } //roll over +} + +/* Official documentation says that the "RESET/TAS opcodes aren't supported", but Out Run definitely contradicts with it. + Since that m68k can't reset itself via the RESET opcode I suppose that the SMPC actually do it by reading an i/o + connected to this opcode. */ +void saturn_state::m68k_reset_callback(device_t *device) +{ + saturn_state *state = device->machine().driver_data(); + device->machine().scheduler().timer_set(attotime::from_usec(100), timer_expired_delegate(FUNC(saturn_state::smpc_audio_reset_line_pulse), state)); + + printf("m68k RESET opcode triggered\n"); +} + +void scsp_irq(device_t *device, int irq) +{ + saturn_state *state = device->machine().driver_data(); + + // don't bother the 68k if it's off + if (!state->m_en_68k) + { + return; + } + + if (irq > 0) + { + state->m_scsp_last_line = irq; + device->machine().device("audiocpu")->execute().set_input_line(irq, ASSERT_LINE); + } + else if (irq < 0) + { + device->machine().device("audiocpu")->execute().set_input_line(-irq, CLEAR_LINE); + } + else + { + device->machine().device("audiocpu")->execute().set_input_line(state->m_scsp_last_line, CLEAR_LINE); + } +} + +WRITE_LINE_MEMBER(saturn_state::scsp_to_main_irq) +{ + if(state) + { + if(!(m_scu.ism & IRQ_SOUND_REQ)) + { + m_maincpu->set_input_line_and_vector(9, HOLD_LINE, 0x46); + scu_do_transfer(5); + } + else + m_scu.ist |= (IRQ_SOUND_REQ); + } +} + + + +/* +(Preliminary) explanation about this: +VBLANK-OUT is used at the start of the vblank period.It also sets the timer zero +variable to 0. +If the Timer Compare register is zero too,the Timer 0 irq is triggered. + +HBLANK-IN is used at the end of each scanline except when in VBLANK-IN/OUT periods. + +The timer 0 is also incremented by one at each HBLANK and checked with the value +of the Timer Compare register;if equal,the timer 0 irq is triggered here too. +Notice that the timer 0 compare register can be more than the VBLANK maximum range,in +this case the timer 0 irq is simply never triggered.This is a known Sega Saturn/ST-V "bug". + +VBLANK-IN is used at the end of the vblank period. + +SCU register[36] is the timer zero compare register. +SCU register[40] is for IRQ masking. + +TODO: +- VDP1 timing and CEF emulation isn't accurate at all. +*/ + + +TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_scanline) +{ + int scanline = param; + int y_step,vblank_line; + + vblank_line = get_vblank_start_position(); + y_step = get_ystep_count(); + + //popmessage("%08x %d T0 %d T1 %d %08x",m_scu.ism ^ 0xffffffff,max_y,m_scu_regs[36],m_scu_regs[37],m_scu_regs[38]); + + if(scanline == (0)*y_step) + { + video_update_vdp1(); + + if(STV_VDP1_VBE) + m_vdp1.framebuffer_clear_on_next_frame = 1; + + if(!(m_scu.ism & IRQ_VDP1_END)) + { + m_maincpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d); + scu_do_transfer(6); + } + else + m_scu.ist |= (IRQ_VDP1_END); + } + + if(scanline == 0*y_step) + { + if(!(m_scu.ism & IRQ_VBLANK_OUT)) + { + m_maincpu->set_input_line_and_vector(0xe, HOLD_LINE, 0x41); + scu_do_transfer(1); + } + else + m_scu.ist |= (IRQ_VBLANK_OUT); + + } + else if(scanline == vblank_line*y_step) + { + if(!(m_scu.ism & IRQ_VBLANK_IN)) + { + m_maincpu->set_input_line_and_vector(0xf, HOLD_LINE ,0x40); + scu_do_transfer(0); + } + else + m_scu.ist |= (IRQ_VBLANK_IN); + } + else if((scanline % y_step) == 0 && scanline < vblank_line*y_step) + { + if(!(m_scu.ism & IRQ_HBLANK_IN)) + { + m_maincpu->set_input_line_and_vector(0xd, HOLD_LINE, 0x42); + scu_do_transfer(2); + } + else + m_scu.ist |= (IRQ_HBLANK_IN); + } + + if(scanline == (m_scu_regs[36] & 0x3ff)*y_step) + { + if(!(m_scu.ism & IRQ_TIMER_0)) + { + m_maincpu->set_input_line_and_vector(0xc, HOLD_LINE, 0x43 ); + scu_do_transfer(3); + } + else + m_scu.ist |= (IRQ_TIMER_0); + } + + /* TODO: this isn't completely correct */ + if(m_scu_regs[38] & 0x1) + { + if((!(m_scu_regs[38] & 0x100) && (scanline % y_step) == 0) || + ((m_scu_regs[38] & 0x100) && (scanline == (m_scu_regs[36] & 0x3ff)*y_step))) + { + if(!(m_scu.ism & IRQ_TIMER_1)) + { + m_maincpu->set_input_line_and_vector(0xb, HOLD_LINE, 0x44 ); + scu_do_transfer(4); + } + else + m_scu.ist |= (IRQ_TIMER_1); + } + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_slave_scanline ) +{ + int scanline = param; + int y_step,vblank_line; + + vblank_line = get_vblank_start_position(); + y_step = get_ystep_count(); + + if(scanline == vblank_line*y_step) + m_slave->set_input_line_and_vector(0x6, HOLD_LINE, 0x43); + else if((scanline % y_step) == 0 && scanline < vblank_line*y_step) + m_slave->set_input_line_and_vector(0x2, HOLD_LINE, 0x41); +} + +static const gfx_layout tiles8x8x4_layout = +{ + 8,8, + 0x100000/(32*8/8), + 4, + { 0, 1, 2, 3 }, + { 0, 4, 8, 12, 16, 20, 24, 28 }, + { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 }, + 32*8 +}; + +static const gfx_layout tiles16x16x4_layout = +{ + 16,16, + 0x100000/(32*32/8), + 4, + { 0, 1, 2, 3 }, + { 0, 4, 8, 12, 16, 20, 24, 28, + 32*8+0, 32*8+4, 32*8+8, 32*8+12, 32*8+16, 32*8+20, 32*8+24, 32*8+28, + + }, + { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32, + 32*16, 32*17,32*18, 32*19,32*20,32*21,32*22,32*23 + + }, + 32*32 +}; + +static const gfx_layout tiles8x8x8_layout = +{ + 8,8, + 0x100000/(32*8/8), + 8, + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 8, 16, 24, 32, 40, 48, 56 }, + { 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64 }, + 32*8 /* really 64*8, but granularity is 32 bytes */ +}; + +static const gfx_layout tiles16x16x8_layout = +{ + 16,16, + 0x100000/(64*16/8), + 8, + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 8, 16, 24, 32, 40, 48, 56, + 64*8+0, 65*8, 66*8, 67*8, 68*8, 69*8, 70*8, 71*8 + + }, + { 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64, + 64*16, 64*17, 64*18, 64*19, 64*20, 64*21, 64*22, 64*23 + }, + 64*16 /* really 128*16, but granularity is 32 bytes */ +}; + + + + +GFXDECODE_START( stv ) + GFXDECODE_ENTRY( NULL, 0, tiles8x8x4_layout, 0x00, (0x80*(2+1)) ) + GFXDECODE_ENTRY( NULL, 0, tiles16x16x4_layout, 0x00, (0x80*(2+1)) ) + GFXDECODE_ENTRY( NULL, 0, tiles8x8x8_layout, 0x00, (0x08*(2+1)) ) + GFXDECODE_ENTRY( NULL, 0, tiles16x16x8_layout, 0x00, (0x08*(2+1)) ) +GFXDECODE_END diff --git a/src/mame/mame.mak b/src/mame/mame.mak index 4429a67fbc2..8e7025e1e98 100644 --- a/src/mame/mame.mak +++ b/src/mame/mame.mak @@ -1288,7 +1288,7 @@ $(MAMEOBJ)/sega.a: \ $(DRIVERS)/segaybd.o $(VIDEO)/segaybd.o \ $(DRIVERS)/sg1000a.o \ $(DRIVERS)/stactics.o $(VIDEO)/stactics.o \ - $(DRIVERS)/stv.o $(DRIVERS)/saturn.o $(MACHINE)/stvprot.o \ + $(DRIVERS)/stv.o $(MACHINE)/stvprot.o \ $(DRIVERS)/suprloco.o $(VIDEO)/suprloco.o \ $(DRIVERS)/system1.o $(VIDEO)/system1.o \ $(DRIVERS)/system16.o $(VIDEO)/system16.o \ diff --git a/src/mame/drivers/saturn.c b/src/mess/drivers/saturn.c similarity index 69% rename from src/mame/drivers/saturn.c rename to src/mess/drivers/saturn.c index c9dafe6184c..fdf0bb22850 100644 --- a/src/mame/drivers/saturn.c +++ b/src/mess/drivers/saturn.c @@ -71,621 +71,6 @@ TODO: #include "coreutil.h" -/* TODO: do this in a verboselog style */ -#define LOG_CDB 0 -#define LOG_SCU 1 -#define LOG_IRQ 0 -#define LOG_IOGA 0 - -int saturn_state::DectoBCD(int num) -{ - int i, cnt = 0, tmp, res = 0; - - while (num > 0) { - tmp = num; - while (tmp >= 10) tmp %= 10; - for (i=0; i -29 0074 -30 0078 -31 007c DMA Status Register -32 0080 DSP Program Control Port -33 0084 DSP Program RAM Data Port -34 0088 DSP Data RAM Address Port -35 008c DSP Data RAM Data Port -36 0090 Timer 0 Compare Register -37 0094 Timer 1 Set Data Register -38 0098 Timer 1 Mode Register -39 009c -40 00a0 Interrupt Mask Register -41 00a4 Interrupt Status Register -42 00a8 A-Bus Interrupt Acknowledge -43 00ac -44 00b0 A-Bus Set Register -45 00b4 -46 00b8 A-Bus Refresh Register -47 00bc -48 00c0 -49 00c4 SCU SDRAM Select Register -50 00c8 SCU Version Register -51 00cc -52 00cf -=================================================================================== -DMA Status Register(32-bit): -xxxx xxxx x--- xx-- xx-- xx-- xx-- xx-- UNUSED ----- ---- -x-- ---- ---- ---- ---- ---- DMA DSP-Bus access ----- ---- --x- ---- ---- ---- ---- ---- DMA B-Bus access ----- ---- ---x ---- ---- ---- ---- ---- DMA A-Bus access ----- ---- ---- --x- ---- ---- ---- ---- DMA lv 1 interrupt ----- ---- ---- ---x ---- ---- ---- ---- DMA lv 0 interrupt ----- ---- ---- ---- --x- ---- ---- ---- DMA lv 2 in stand-by ----- ---- ---- ---- ---x ---- ---- ---- DMA lv 2 in operation ----- ---- ---- ---- ---- --x- ---- ---- DMA lv 1 in stand-by ----- ---- ---- ---- ---- ---x ---- ---- DMA lv 1 in operation ----- ---- ---- ---- ---- ---- --x- ---- DMA lv 0 in stand-by ----- ---- ---- ---- ---- ---- ---x ---- DMA lv 0 in operation ----- ---- ---- ---- ---- ---- ---- --x- DSP side DMA in stand-by ----- ---- ---- ---- ---- ---- ---- ---x DSP side DMA in operation - -**********************************************************************************/ -/* -DMA TODO: --Remove CD transfer DMA hack (tied with CD block bug(s)?) --Add timings(but how fast are each DMA?). --Add level priority & DMA status register. -*/ - -#define DIRECT_MODE(_lv_) (!(m_scu_regs[5+(_lv_*8)] & 0x01000000)) -#define INDIRECT_MODE(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x01000000) -#define DRUP(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x00010000) -#define DWUP(_lv_) (m_scu_regs[5+(_lv_*8)] & 0x00000100) - -/*These macros sets the various DMA status flags.*/ -#define DnMV_1(_ch_) m_scu.status|=(0x10 << 4 * _ch_) -#define DnMV_0(_ch_) m_scu.status&=~(0x10 << 4 * _ch_) - -/*For area checking*/ -#define BIOS_BUS(var) (var & 0x07000000) == 0 -#define ABUS(_lv_) ((m_scu.src[_lv_] & 0x07000000) >= 0x02000000) && ((m_scu.src[_lv_] & 0x07000000) <= 0x04000000) -#define BBUS(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05a00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05ffffff) -#define VDP1_REGS(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05d00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05dfffff) -#define VDP2(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05e00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05fdffff) -#define WORK_RAM_L(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x00200000) && ((scu_##_lv_ & 0x07ffffff) <= 0x002fffff) -#define WORK_RAM_H(var) (var & 0x07000000) == 0x06000000 -#define SOUND_RAM(_lv_) ((scu_##_lv_ & 0x07ffffff) >= 0x05a00000) && ((scu_##_lv_ & 0x07ffffff) <= 0x05afffff) - -void saturn_state::scu_do_transfer(UINT8 event) -{ - address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); - int i; - - for(i=0;i<3;i++) - { - if(m_scu.enable_mask[i] && m_scu.start_factor[i] == event) - { - if(DIRECT_MODE(i)) { scu_dma_direct(space,i); } - else { scu_dma_indirect(space,i); } - } - } -} - -/* test pending irqs */ -void saturn_state::scu_test_pending_irq() -{ - int i; - const int irq_level[32] = { 0xf, 0xe, 0xd, 0xc, - 0xb, 0xa, 0x9, 0x8, - 0x8, 0x6, 0x6, 0x5, - 0x3, 0x2, -1, -1, - 0x7, 0x7, 0x7, 0x7, - 0x4, 0x4, 0x4, 0x4, - 0x1, 0x1, 0x1, 0x1, - 0x1, 0x1, 0x1, 0x1 }; - - for(i=0;i<32;i++) - { - if((!(m_scu.ism & 1 << i)) && (m_scu.ist & 1 << i)) - { - if(irq_level[i] != -1) /* TODO: cheap check for undefined irqs */ - { - m_maincpu->set_input_line_and_vector(irq_level[i], HOLD_LINE, 0x40 + i); - m_scu.ist &= ~(1 << i); - return; /* avoid spurious irqs, correct? */ - } - } - } -} - -READ32_MEMBER(saturn_state::saturn_scu_r) -{ - UINT32 res; - - /*TODO: write only registers must return 0 or open bus */ - switch(offset) - { - case 0x5c/4: - // Super Major League and Shin Megami Tensei - Akuma Zensho reads from there (undocumented), DMA status mirror? - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) DMA status reg read\n",space.device().safe_pc()); - res = m_scu.status; - break; - case 0x7c/4: - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) DMA status reg read\n",space.device().safe_pc()); - res = m_scu.status; - break; - case 0x80/4: - res = dsp_prg_ctrl_r(space); - break; - case 0x8c/4: - if(LOG_SCU && !space.debugger_access()) logerror( "DSP mem read at %08X\n", m_scu_regs[34]); - res = dsp_ram_addr_r(); - break; - case 0xa0/4: - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) IRQ mask reg read %08x MASK=%08x\n",space.device().safe_pc(),mem_mask,m_scu_regs[0xa0/4]); - res = m_scu.ism; - break; - case 0xa4/4: - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) IRQ status reg read MASK=%08x IST=%08x | ISM=%08x\n",space.device().safe_pc(),mem_mask,m_scu.ist,m_scu.ism); - /* TODO: Bug! trips an HW fault. Basically, it tries to read the IST bit 1 with that irq enabled. - Densetsu no Ogre Battle doesn't like this, so it needs investigation ... - */ -// res = m_scu.ist | ~m_scu.ism; - res = m_scu.ist; - break; - case 0xc8/4: - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) SCU version reg read\n",space.device().safe_pc()); - res = 0x00000004;/*SCU Version 4, OK? */ - break; - default: - if(LOG_SCU && !space.debugger_access()) logerror("(PC=%08x) SCU reg read at %d = %08x\n",space.device().safe_pc(),offset,m_scu_regs[offset]); - res = m_scu_regs[offset]; - break; - } - - return res; -} - -#define DMA_CH ((offset & 0x18) / 8) - -WRITE32_MEMBER(saturn_state::saturn_scu_w) -{ - COMBINE_DATA(&m_scu_regs[offset]); - - switch(offset) - { - /*LV 0 DMA*/ - case 0x00/4: case 0x20/4: case 0x40/4: m_scu.src[DMA_CH] = ((m_scu_regs[offset] & 0x07ffffff) >> 0); break; - case 0x04/4: case 0x24/4: case 0x44/4: m_scu.dst[DMA_CH] = ((m_scu_regs[offset] & 0x07ffffff) >> 0); break; - case 0x08/4: case 0x28/4: case 0x48/4: m_scu.size[DMA_CH] = ((m_scu_regs[offset] & ((offset == 2) ? 0x000fffff : 0xfff)) >> 0); break; - case 0x0c/4: case 0x2c/4: case 0x4c/4: - m_scu.src_add[DMA_CH] = (m_scu_regs[offset] & 0x100) ? 4 : 0; - m_scu.dst_add[DMA_CH] = 1 << (m_scu_regs[offset] & 7); - if(m_scu.dst_add[DMA_CH] == 1) { m_scu.dst_add[DMA_CH] = 0; } - break; - case 0x10/4: case 0x30/4: case 0x50/4: - m_scu.enable_mask[DMA_CH] = (data & 0x100) >> 8; - if(m_scu.enable_mask[DMA_CH] && m_scu.start_factor[DMA_CH] == 7 && m_scu_regs[offset] & 1) - { - if(DIRECT_MODE(DMA_CH)) { scu_dma_direct(space,DMA_CH); } - else { scu_dma_indirect(space,DMA_CH); } - m_scu_regs[offset]&=~1;//disable starting bit. - } - break; - case 0x14/4: case 0x34/4: case 0x54/4: - if(INDIRECT_MODE(DMA_CH)) - { - //if(LOG_SCU) logerror("Indirect Mode DMA lv %d set\n",DMA_CH); - if(!DWUP(DMA_CH)) m_scu.index[DMA_CH] = m_scu.dst[DMA_CH]; - } - - m_scu.start_factor[DMA_CH] = m_scu_regs[offset] & 7; - break; - - case 0x60/4: - if(LOG_SCU) logerror("DMA Forced Stop Register set = %02x\n",m_scu_regs[24]); - break; - case 0x7c/4: if(LOG_SCU) logerror("Warning: DMA status WRITE! Offset %02x(%d)\n",offset*4,offset); break; - /*DSP section*/ - case 0x80/4: - /* TODO: you can't overwrite some flags with this */ - dsp_prg_ctrl_w(space, m_scu_regs[offset]); - if(LOG_SCU) logerror("SCU DSP: Program Control Port Access %08x\n",data); - break; - case 0x84/4: - dsp_prg_data(m_scu_regs[offset]); - if(LOG_SCU) logerror("SCU DSP: Program RAM Data Port Access %08x\n",data); - break; - case 0x88/4: - dsp_ram_addr_ctrl(m_scu_regs[offset]); - if(LOG_SCU) logerror("SCU DSP: Data RAM Address Port Access %08x\n",data); - break; - case 0x8c/4: - dsp_ram_addr_w(m_scu_regs[offset]); - if(LOG_SCU) logerror("SCU DSP: Data RAM Data Port Access %08x\n",data); - break; - case 0x90/4: /*if(LOG_SCU) logerror("timer 0 compare data = %03x\n",m_scu_regs[36]);*/ break; - case 0x94/4: /*if(LOG_SCU) logerror("timer 1 set data = %08x\n",m_scu_regs[37]);*/ break; - case 0x98/4: /*if(LOG_SCU) logerror("timer 1 mode data = %08x\n",m_scu_regs[38]);*/ break; - case 0xa0/4: /* IRQ mask */ - m_scu.ism = m_scu_regs[0xa0/4]; - scu_test_pending_irq(); - break; - case 0xa4/4: /* IRQ control */ - if(LOG_SCU) logerror("PC=%08x IRQ status reg set:%08x %08x\n",space.device().safe_pc(),m_scu_regs[41],mem_mask); - m_scu.ist &= m_scu_regs[offset]; - scu_test_pending_irq(); - break; - case 0xa8/4: - /* This sends an irq signal to the extra devices connected to the A-Bus, not really needed for now. */ - //if(LOG_SCU) logerror("A-Bus IRQ ACK %08x\n",m_scu_regs[42]); - break; - case 0xc4/4: if(LOG_SCU) logerror("SCU SDRAM set: %02x\n",m_scu_regs[49]); break; - default: if(LOG_SCU) logerror("Warning: unused SCU reg set %d = %08x\n",offset,data); - } -} - -/*Lv 0 DMA end irq*/ -TIMER_CALLBACK_MEMBER(saturn_state::dma_lv0_ended ) -{ - if(!(m_scu.ism & IRQ_DMALV0)) - m_maincpu->set_input_line_and_vector(5, HOLD_LINE, 0x4b); - else - m_scu.ist |= (IRQ_DMALV0); - - DnMV_0(0); -} - -/*Lv 1 DMA end irq*/ -TIMER_CALLBACK_MEMBER(saturn_state::dma_lv1_ended) -{ - if(!(m_scu.ism & IRQ_DMALV1)) - m_maincpu->set_input_line_and_vector(6, HOLD_LINE, 0x4a); - else - m_scu.ist |= (IRQ_DMALV1); - - DnMV_0(1); -} - -/*Lv 2 DMA end irq*/ -TIMER_CALLBACK_MEMBER(saturn_state::dma_lv2_ended) -{ - if(!(m_scu.ism & IRQ_DMALV2)) - m_maincpu->set_input_line_and_vector(6, HOLD_LINE, 0x49); - else - m_scu.ist |= (IRQ_DMALV2); - - DnMV_0(2); -} - -void saturn_state::scu_single_transfer(address_space &space, UINT32 src, UINT32 dst,UINT8 *src_shift) -{ - UINT32 src_data; - - if(src & 1) - { - /* Road Blaster does a work ram h to color ram with offsetted source address, do some data rotation */ - src_data = ((space.read_dword(src & 0x07fffffc) & 0x00ffffff)<<8); - src_data |= ((space.read_dword((src & 0x07fffffc)+4) & 0xff000000) >> 24); - src_data >>= (*src_shift)*16; - } - else - src_data = space.read_dword(src & 0x07fffffc) >> (*src_shift)*16; - - space.write_word(dst,src_data); - - *src_shift ^= 1; -} - -void saturn_state::scu_dma_direct(address_space &space, UINT8 dma_ch) -{ - UINT32 tmp_src,tmp_dst,total_size; - UINT8 cd_transfer_flag; - - if(m_scu.src_add[dma_ch] == 0 || (m_scu.dst_add[dma_ch] != 2 && m_scu.dst_add[dma_ch] != 4)) - { - if(LOG_SCU) printf("DMA lv %d transfer START\n" - "Start %08x End %08x Size %04x\n",dma_ch,m_scu.src[dma_ch],m_scu.dst[dma_ch],m_scu.size[dma_ch]); - if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]); - } - - /* TODO: Game Basic and World Cup 98 trips this, according to the docs the SCU can't transfer from BIOS area (can't communicate from/to that bus) */ - if(BIOS_BUS(m_scu.src[dma_ch])) - { - popmessage("Warning: SCU transfer from BIOS area, contact MAMEdev"); - if(!(m_scu.ism & IRQ_DMAILL)) - m_maincpu->set_input_line_and_vector(3, HOLD_LINE, 0x4c); - else - m_scu.ist |= (IRQ_DMAILL); - return; - } - - DnMV_1(dma_ch); - - /* max size */ - if(m_scu.size[dma_ch] == 0) { m_scu.size[dma_ch] = (dma_ch == 0) ? 0x00100000 : 0x1000; } - - tmp_src = tmp_dst = 0; - - total_size = m_scu.size[dma_ch]; - if(!(DRUP(dma_ch))) tmp_src = m_scu.src[dma_ch]; - if(!(DWUP(dma_ch))) tmp_dst = m_scu.dst[dma_ch]; - - cd_transfer_flag = m_scu.src_add[dma_ch] == 0 && m_scu.src[dma_ch] == 0x05818000; - - /* TODO: Many games directly accesses CD-ROM register 0x05818000, it must be a dword access with current implementation otherwise it won't work */ - if(cd_transfer_flag) - { - int i; - if(WORK_RAM_H(m_scu.dst[dma_ch])) - m_scu.dst_add[dma_ch] = 4; - else - m_scu.dst_add[dma_ch] <<= 1; - - for (i = 0; i < m_scu.size[dma_ch];i+=m_scu.dst_add[dma_ch]) - { - space.write_dword(m_scu.dst[dma_ch],space.read_dword(m_scu.src[dma_ch])); - if(m_scu.dst_add[dma_ch] == 8) - space.write_dword(m_scu.dst[dma_ch]+4,space.read_dword(m_scu.src[dma_ch])); - - m_scu.src[dma_ch]+=m_scu.src_add[dma_ch]; - m_scu.dst[dma_ch]+=m_scu.dst_add[dma_ch]; - } - } - else - { - int i; - UINT8 src_shift; - - src_shift = ((m_scu.src[dma_ch] & 2) >> 1) ^ 1; - - for (i = 0; i < m_scu.size[dma_ch];i+=2) - { - scu_single_transfer(space,m_scu.src[dma_ch],m_scu.dst[dma_ch],&src_shift); - - if(src_shift) - m_scu.src[dma_ch]+=m_scu.src_add[dma_ch]; - - /* if target is Work RAM H, the add value is fixed, behaviour confirmed by Final Romance 2, Virtual Mahjong and Burning Rangers */ - m_scu.dst[dma_ch]+=(WORK_RAM_H(m_scu.dst[dma_ch])) ? 2 : m_scu.dst_add[dma_ch]; - } - } - - /* Burning Rangers doesn't agree with this. */ -// m_scu.size[dma_ch] = 0; - if(!(DRUP(dma_ch))) m_scu.src[dma_ch] = tmp_src; - if(!(DWUP(dma_ch))) m_scu.dst[dma_ch] = tmp_dst; - - { - /*TODO: change DMA into DRQ model. Timing is a guess. */ - switch(dma_ch) - { - case 0: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv0_ended),this)); break; - case 1: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv1_ended),this)); break; - case 2: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv2_ended),this)); break; - } - } -} - -void saturn_state::scu_dma_indirect(address_space &space,UINT8 dma_ch) -{ - /*Helper to get out of the cycle*/ - UINT8 job_done = 0; - /*temporary storage for the transfer data*/ - UINT32 tmp_src; - UINT32 indirect_src,indirect_dst; - INT32 indirect_size; - UINT32 total_size = 0; - - DnMV_1(dma_ch); - - m_scu.index[dma_ch] = m_scu.dst[dma_ch]; - - do{ - tmp_src = m_scu.index[dma_ch]; - - indirect_size = space.read_dword(m_scu.index[dma_ch]); - indirect_src = space.read_dword(m_scu.index[dma_ch]+8); - indirect_dst = space.read_dword(m_scu.index[dma_ch]+4); - - /*Indirect Mode end factor*/ - if(indirect_src & 0x80000000) - job_done = 1; - - if(m_scu.src_add[dma_ch] == 0 || (m_scu.dst_add[dma_ch] != 2)) - { - if(LOG_SCU) printf("DMA lv %d indirect mode transfer START\n" - "Index %08x Start %08x End %08x Size %04x\n",dma_ch,tmp_src,indirect_src,indirect_dst,indirect_size); - if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]); - } - - indirect_src &=0x07ffffff; - indirect_dst &=0x07ffffff; - indirect_size &= ((dma_ch == 0) ? 0xfffff : 0x3ffff); //TODO: Guardian Heroes sets up a 0x23000 transfer for the FMV? - - if(indirect_size == 0) { indirect_size = (dma_ch == 0) ? 0x00100000 : 0x2000; } - - { - int i; - UINT8 src_shift; - - src_shift = ((indirect_src & 2) >> 1) ^ 1; - - for (i = 0; i < indirect_size;i+=2) - { - scu_single_transfer(space,indirect_src,indirect_dst,&src_shift); - - if(src_shift) - indirect_src+=m_scu.src_add[dma_ch]; - - indirect_dst+= (WORK_RAM_H(indirect_dst)) ? 2 : m_scu.dst_add[dma_ch]; - } - } - - /* Guess: Size + data acquire (1 cycle for src/dst/size) */ - total_size += indirect_size + 3*4; - - //if(DRUP(0)) space.write_dword(tmp_src+8,m_scu.src[0]|job_done ? 0x80000000 : 0); - //if(DWUP(0)) space.write_dword(tmp_src+4,m_scu.dst[0]); - - m_scu.index[dma_ch] = tmp_src+0xc; - - }while(job_done == 0); - - { - /*TODO: change DMA into DRQ model. Timing is a guess. */ - switch(dma_ch) - { - case 0: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv0_ended),this)); break; - case 1: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv1_ended),this)); break; - case 2: machine().scheduler().timer_set(m_maincpu->cycles_to_attotime(total_size/4), timer_expired_delegate(FUNC(saturn_state::dma_lv2_ended),this)); break; - } - } -} - - -/**************************************************************************************/ - -WRITE16_MEMBER(saturn_state::saturn_soundram_w) -{ - //machine().scheduler().synchronize(); // force resync - - COMBINE_DATA(&m_sound_ram[offset]); -} - -READ16_MEMBER(saturn_state::saturn_soundram_r) -{ - //machine().scheduler().synchronize(); // force resync - - return m_sound_ram[offset]; -} - -/* communication,SLAVE CPU acquires data from the MASTER CPU and triggers an irq. */ -WRITE32_MEMBER(saturn_state::minit_w) -{ - //logerror("cpu %s (PC=%08X) MINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); - machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost)); - machine().scheduler().trigger(1000); - machine().scheduler().synchronize(); // force resync - sh2_set_frt_input(m_slave, PULSE_LINE); -} - -WRITE32_MEMBER(saturn_state::sinit_w) -{ - //logerror("cpu %s (PC=%08X) SINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); - machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost)); - machine().scheduler().synchronize(); // force resync - sh2_set_frt_input(m_maincpu, PULSE_LINE); -} - -/* -TODO: -Some games seems to not like either MAME's interleave system and/or SH-2 DRC, causing an hard crash. -Reported games are: -Blast Wind (before FMV) -Choro Q Park (car selection) -060311E4: MOV.L R14,@-SP ;R14 = 0x60ffba0 / R15 = 0x60ffba0 -060311E6: MOV SP,R14 ;R14 = 0x60ffba0 / R15 = 0x60ffb9c / [0x60ffb9c] <- 0x60ffba0 -060311E8: MOV.L @SP+,R14 ;R14 = 0x60ffb9c / R15 = 0x60ffb9c / [0x60ffb9c] -> R14 -060311EA: RTS ;R14 = 0x60ffba0 / R15 = 0x60ffba0 -060311EC: NOP -06031734: MULS.W R9, R8 ;R14 = 0x60ffba0 / R15 = 0x60ffba0 / EA = 0x60311E4 -on DRC this becomes: -R14 0x6031b78 (cause of the crash later on), R15 = 0x60ffba4 and EA = 0 - -Shinrei Jusatsushi Taromaru (options menu) - -*/ - -WRITE32_MEMBER(saturn_state::saturn_minit_w) -{ - //logerror("cpu %s (PC=%08X) MINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); - if(m_fake_comms->read() & 1) - machine().scheduler().synchronize(); // force resync - else - { - machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost)); - machine().scheduler().trigger(1000); - } - - sh2_set_frt_input(m_slave, PULSE_LINE); -} - -WRITE32_MEMBER(saturn_state::saturn_sinit_w) -{ - //logerror("cpu %s (PC=%08X) SINIT write = %08x\n", space.device().tag(), space.device().safe_pc(),data); - if(m_fake_comms->read() & 1) - machine().scheduler().synchronize(); // force resync - else - machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost)); - - sh2_set_frt_input(m_maincpu, PULSE_LINE); -} - - -READ8_MEMBER(saturn_state::saturn_backupram_r) -{ - if(!(offset & 1)) - return 0; // yes, it makes sure the "holes" are there. - - return m_backupram[offset >> 1] & 0xff; -} - -WRITE8_MEMBER(saturn_state::saturn_backupram_w) -{ - if(!(offset & 1)) - return; - - m_backupram[offset >> 1] = data; -} /* TODO: if you change the driver configuration then NVRAM contents gets screwed, needs mods in MAME framework */ static NVRAM_HANDLER(saturn) @@ -1204,113 +589,9 @@ static INPUT_PORTS_START( saturn ) PORT_CONFSETTING(0x01,"One Shot (Hack)") INPUT_PORTS_END -static const gfx_layout tiles8x8x4_layout = -{ - 8,8, - 0x100000/(32*8/8), - 4, - { 0, 1, 2, 3 }, - { 0, 4, 8, 12, 16, 20, 24, 28 }, - { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 }, - 32*8 -}; - -static const gfx_layout tiles16x16x4_layout = -{ - 16,16, - 0x100000/(32*32/8), - 4, - { 0, 1, 2, 3 }, - { 0, 4, 8, 12, 16, 20, 24, 28, - 32*8+0, 32*8+4, 32*8+8, 32*8+12, 32*8+16, 32*8+20, 32*8+24, 32*8+28, - - }, - { 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32, - 32*16, 32*17,32*18, 32*19,32*20,32*21,32*22,32*23 - - }, - 32*32 -}; - -static const gfx_layout tiles8x8x8_layout = -{ - 8,8, - 0x100000/(32*8/8), - 8, - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 0, 8, 16, 24, 32, 40, 48, 56 }, - { 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64 }, - 32*8 /* really 64*8, but granularity is 32 bytes */ -}; - -static const gfx_layout tiles16x16x8_layout = -{ - 16,16, - 0x100000/(64*16/8), - 8, - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 0, 8, 16, 24, 32, 40, 48, 56, - 64*8+0, 65*8, 66*8, 67*8, 68*8, 69*8, 70*8, 71*8 - - }, - { 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64, - 64*16, 64*17, 64*18, 64*19, 64*20, 64*21, 64*22, 64*23 - }, - 64*16 /* really 128*16, but granularity is 32 bytes */ -}; - - - - -GFXDECODE_START( stv ) - GFXDECODE_ENTRY( NULL, 0, tiles8x8x4_layout, 0x00, (0x80*(2+1)) ) - GFXDECODE_ENTRY( NULL, 0, tiles16x16x4_layout, 0x00, (0x80*(2+1)) ) - GFXDECODE_ENTRY( NULL, 0, tiles8x8x8_layout, 0x00, (0x08*(2+1)) ) - GFXDECODE_ENTRY( NULL, 0, tiles16x16x8_layout, 0x00, (0x08*(2+1)) ) -GFXDECODE_END - static const sh2_cpu_core sh2_conf_master = { 0, NULL }; static const sh2_cpu_core sh2_conf_slave = { 1, NULL }; -void scsp_irq(device_t *device, int irq) -{ - saturn_state *state = device->machine().driver_data(); - - // don't bother the 68k if it's off - if (!state->m_en_68k) - { - return; - } - - if (irq > 0) - { - state->m_scsp_last_line = irq; - device->machine().device("audiocpu")->execute().set_input_line(irq, ASSERT_LINE); - } - else if (irq < 0) - { - device->machine().device("audiocpu")->execute().set_input_line(-irq, CLEAR_LINE); - } - else - { - device->machine().device("audiocpu")->execute().set_input_line(state->m_scsp_last_line, CLEAR_LINE); - } -} - -WRITE_LINE_MEMBER(saturn_state::scsp_to_main_irq) -{ - if(state) - { - if(!(m_scu.ism & IRQ_SOUND_REQ)) - { - m_maincpu->set_input_line_and_vector(9, HOLD_LINE, 0x46); - scu_do_transfer(5); - } - else - m_scu.ist |= (IRQ_SOUND_REQ); - } -} - static const scsp_interface scsp_config = { 0, @@ -1318,86 +599,6 @@ static const scsp_interface scsp_config = DEVCB_DRIVER_LINE_MEMBER(saturn_state, scsp_to_main_irq) }; -TIMER_CALLBACK_MEMBER(saturn_state::stv_rtc_increment) -{ - static const UINT8 dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 }; - static int year_num, year_count; - - /* - m_smpc.rtc_data[0] = DectoBCD(systime.local_time.year /100); - m_smpc.rtc_data[1] = DectoBCD(systime.local_time.year %100); - m_smpc.rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1); - m_smpc.rtc_data[3] = DectoBCD(systime.local_time.mday); - m_smpc.rtc_data[4] = DectoBCD(systime.local_time.hour); - m_smpc.rtc_data[5] = DectoBCD(systime.local_time.minute); - m_smpc.rtc_data[6] = DectoBCD(systime.local_time.second); - */ - - m_smpc.rtc_data[6]++; - - /* seconds from 9 -> 10*/ - if((m_smpc.rtc_data[6] & 0x0f) >= 0x0a) { m_smpc.rtc_data[6]+=0x10; m_smpc.rtc_data[6]&=0xf0; } - /* seconds from 59 -> 0 */ - if((m_smpc.rtc_data[6] & 0xf0) >= 0x60) { m_smpc.rtc_data[5]++; m_smpc.rtc_data[6] = 0; } - /* minutes from 9 -> 10 */ - if((m_smpc.rtc_data[5] & 0x0f) >= 0x0a) { m_smpc.rtc_data[5]+=0x10; m_smpc.rtc_data[5]&=0xf0; } - /* minutes from 59 -> 0 */ - if((m_smpc.rtc_data[5] & 0xf0) >= 0x60) { m_smpc.rtc_data[4]++; m_smpc.rtc_data[5] = 0; } - /* hours from 9 -> 10 */ - if((m_smpc.rtc_data[4] & 0x0f) >= 0x0a) { m_smpc.rtc_data[4]+=0x10; m_smpc.rtc_data[4]&=0xf0; } - /* hours from 23 -> 0 */ - if((m_smpc.rtc_data[4] & 0xff) >= 0x24) { m_smpc.rtc_data[3]++; m_smpc.rtc_data[2]+=0x10; m_smpc.rtc_data[4] = 0; } - /* week day name sunday -> monday */ - if((m_smpc.rtc_data[2] & 0xf0) >= 0x70) { m_smpc.rtc_data[2]&=0x0f; } - /* day number 9 -> 10 */ - if((m_smpc.rtc_data[3] & 0x0f) >= 0x0a) { m_smpc.rtc_data[3]+=0x10; m_smpc.rtc_data[3]&=0xf0; } - - // year BCD to dec conversion (for the leap year stuff) - { - year_num = (m_smpc.rtc_data[1] & 0xf); - - for(year_count = 0; year_count < (m_smpc.rtc_data[1] & 0xf0); year_count += 0x10) - year_num += 0xa; - - year_num += (m_smpc.rtc_data[0] & 0xf)*0x64; - - for(year_count = 0; year_count < (m_smpc.rtc_data[0] & 0xf0); year_count += 0x10) - year_num += 0x3e8; - } - - /* month +1 check */ - /* the RTC have a range of 1980 - 2100, so we don't actually need to support the leap year special conditions */ - if(((year_num % 4) == 0) && (m_smpc.rtc_data[2] & 0xf) == 2) - { - if((m_smpc.rtc_data[3] & 0xff) >= dpm[(m_smpc.rtc_data[2] & 0xf)-1]+1+1) - { m_smpc.rtc_data[2]++; m_smpc.rtc_data[3] = 0x01; } - } - else if((m_smpc.rtc_data[3] & 0xff) >= dpm[(m_smpc.rtc_data[2] & 0xf)-1]+1){ m_smpc.rtc_data[2]++; m_smpc.rtc_data[3] = 0x01; } - /* year +1 check */ - if((m_smpc.rtc_data[2] & 0x0f) > 12) { m_smpc.rtc_data[1]++; m_smpc.rtc_data[2] = (m_smpc.rtc_data[2] & 0xf0) | 0x01; } - /* year from 9 -> 10 */ - if((m_smpc.rtc_data[1] & 0x0f) >= 0x0a) { m_smpc.rtc_data[1]+=0x10; m_smpc.rtc_data[1]&=0xf0; } - /* year from 99 -> 100 */ - if((m_smpc.rtc_data[1] & 0xf0) >= 0xa0) { m_smpc.rtc_data[0]++; m_smpc.rtc_data[1] = 0; } - - // probably not SO precise, here just for reference ... - /* year from 999 -> 1000 */ - //if((m_smpc.rtc_data[0] & 0x0f) >= 0x0a) { m_smpc.rtc_data[0]+=0x10; m_smpc.rtc_data[0]&=0xf0; } - /* year from 9999 -> 0 */ - //if((m_smpc.rtc_data[0] & 0xf0) >= 0xa0) { m_smpc.rtc_data[0] = 0; } //roll over -} - -/* Official documentation says that the "RESET/TAS opcodes aren't supported", but Out Run definitely contradicts with it. - Since that m68k can't reset itself via the RESET opcode I suppose that the SMPC actually do it by reading an i/o - connected to this opcode. */ -void saturn_state::m68k_reset_callback(device_t *device) -{ - saturn_state *state = device->machine().driver_data(); - device->machine().scheduler().timer_set(attotime::from_usec(100), timer_expired_delegate(FUNC(saturn_state::smpc_audio_reset_line_pulse), state)); - - printf("m68k RESET opcode triggered\n"); -} - MACHINE_START_MEMBER(saturn_state,saturn) { system_time systime; @@ -1444,130 +645,6 @@ MACHINE_START_MEMBER(saturn_state,saturn) m68k_set_reset_callback(m_audiocpu, &saturn_state::m68k_reset_callback); } - -/* -(Preliminary) explanation about this: -VBLANK-OUT is used at the start of the vblank period.It also sets the timer zero -variable to 0. -If the Timer Compare register is zero too,the Timer 0 irq is triggered. - -HBLANK-IN is used at the end of each scanline except when in VBLANK-IN/OUT periods. - -The timer 0 is also incremented by one at each HBLANK and checked with the value -of the Timer Compare register;if equal,the timer 0 irq is triggered here too. -Notice that the timer 0 compare register can be more than the VBLANK maximum range,in -this case the timer 0 irq is simply never triggered.This is a known Sega Saturn/ST-V "bug". - -VBLANK-IN is used at the end of the vblank period. - -SCU register[36] is the timer zero compare register. -SCU register[40] is for IRQ masking. - -TODO: -- VDP1 timing and CEF emulation isn't accurate at all. -*/ - - -TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_scanline) -{ - int scanline = param; - int y_step,vblank_line; - - vblank_line = get_vblank_start_position(); - y_step = get_ystep_count(); - - //popmessage("%08x %d T0 %d T1 %d %08x",m_scu.ism ^ 0xffffffff,max_y,m_scu_regs[36],m_scu_regs[37],m_scu_regs[38]); - - if(scanline == (0)*y_step) - { - video_update_vdp1(); - - if(STV_VDP1_VBE) - m_vdp1.framebuffer_clear_on_next_frame = 1; - - if(!(m_scu.ism & IRQ_VDP1_END)) - { - m_maincpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d); - scu_do_transfer(6); - } - else - m_scu.ist |= (IRQ_VDP1_END); - } - - if(scanline == 0*y_step) - { - if(!(m_scu.ism & IRQ_VBLANK_OUT)) - { - m_maincpu->set_input_line_and_vector(0xe, HOLD_LINE, 0x41); - scu_do_transfer(1); - } - else - m_scu.ist |= (IRQ_VBLANK_OUT); - - } - else if(scanline == vblank_line*y_step) - { - if(!(m_scu.ism & IRQ_VBLANK_IN)) - { - m_maincpu->set_input_line_and_vector(0xf, HOLD_LINE ,0x40); - scu_do_transfer(0); - } - else - m_scu.ist |= (IRQ_VBLANK_IN); - } - else if((scanline % y_step) == 0 && scanline < vblank_line*y_step) - { - if(!(m_scu.ism & IRQ_HBLANK_IN)) - { - m_maincpu->set_input_line_and_vector(0xd, HOLD_LINE, 0x42); - scu_do_transfer(2); - } - else - m_scu.ist |= (IRQ_HBLANK_IN); - } - - if(scanline == (m_scu_regs[36] & 0x3ff)*y_step) - { - if(!(m_scu.ism & IRQ_TIMER_0)) - { - m_maincpu->set_input_line_and_vector(0xc, HOLD_LINE, 0x43 ); - scu_do_transfer(3); - } - else - m_scu.ist |= (IRQ_TIMER_0); - } - - /* TODO: this isn't completely correct */ - if(m_scu_regs[38] & 0x1) - { - if((!(m_scu_regs[38] & 0x100) && (scanline % y_step) == 0) || - ((m_scu_regs[38] & 0x100) && (scanline == (m_scu_regs[36] & 0x3ff)*y_step))) - { - if(!(m_scu.ism & IRQ_TIMER_1)) - { - m_maincpu->set_input_line_and_vector(0xb, HOLD_LINE, 0x44 ); - scu_do_transfer(4); - } - else - m_scu.ist |= (IRQ_TIMER_1); - } - } -} - -TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_slave_scanline ) -{ - int scanline = param; - int y_step,vblank_line; - - vblank_line = get_vblank_start_position(); - y_step = get_ystep_count(); - - if(scanline == vblank_line*y_step) - m_slave->set_input_line_and_vector(0x6, HOLD_LINE, 0x43); - else if((scanline % y_step) == 0 && scanline < vblank_line*y_step) - m_slave->set_input_line_and_vector(0x2, HOLD_LINE, 0x41); -} - /* Die Hard Trilogy tests RAM address 0x25e7ffe bit 2 with Slave during FRT minit irq, in-development tool for breaking execution of it? */ READ32_MEMBER(saturn_state::saturn_null_ram_r) { @@ -1619,16 +696,6 @@ WRITE32_MEMBER(saturn_state::saturn_cs1_w) m_cart_backupram[offset*2+1] = (data & 0x000000ff) >> 0; } -void saturn_state::scu_reset(void) -{ - m_scu.ism = 0xbfff; - m_scu.ist = 0; - m_scu.start_factor[0] = 7; - m_scu.start_factor[1] = 7; - m_scu.start_factor[2] = 7; - m_scu.status = 0; -} - MACHINE_RESET_MEMBER(saturn_state,saturn) { m_scsp_last_line = 0; diff --git a/src/mess/mess.mak b/src/mess/mess.mak index be0f241ef8a..aa1db8be619 100644 --- a/src/mess/mess.mak +++ b/src/mess/mess.mak @@ -1643,7 +1643,7 @@ $(MESSOBJ)/sega.a: \ $(MESS_DRIVERS)/dccons.o \ $(MAME_MACHINE)/gdrom.o \ $(MESS_MACHINE)/dccons.o \ - $(MAME_DRIVERS)/saturn.o \ + $(MESS_DRIVERS)/saturn.o \ $(MESS_MACHINE)/sms.o \ $(MESS_DRIVERS)/sms.o \ $(MESS_DRIVERS)/svmu.o \