/*************************************************************************** Midway DCS Audio Board **************************************************************************** There are several variations of this board, which was in use by Midway and eventually Atari for almost 10 years. DCS ROM-based mono: * ADSP-2105 @ 10MHz * single channel output * 2k external shared program/data RAM * ROM-based, up to 8MB total * used in: Mortal Kombat 2 (1993) Cruisin' USA (1994) Revolution X (1994) Killer Instinct (1994) Killer Instinct 2 (1995) Cruisin' World (1996) Offroad Challenge (1997) * 8k external shared program/data RAM * used in: Mortal Kombat 3 (1994) Ultimate Mortal Kombat 3 (1994) 2 On 2 Open Ice Challenge (1995) WWF Wrestlemania (1995) NBA Hangtime (1996) NBA Maximum Hangtime (1996) Rampage World Tour (1997) DCS2 RAM-based stereo (Seattle): * ADSP-2115 @ 16MHz * dual channel output (stereo) * SDRC ASIC for RAM/ROM access * RAM-based, 2MB total * used in: War Gods (1995) Wayne Gretzky's 3D Hockey (1996) Mace: The Dark Age (1996) Biofreaks (1997) NFL Blitz (1997) California Speed (1998) Vapor TRX (1998) NFL Blitz '99 (1998) CarnEvil (1998) Hyperdrive (1998) NFL Blitz 2000 Gold (1999) DCS2 ROM-based stereo (Zeus): * ADSP-2104 @ 16MHz * dual channel output (stereo) * SDRC ASIC for RAM/ROM access * ROM-based, up to 16MB total * used in: Mortal Kombat 4 (1997) Invasion (1999) Cruisin' Exotica (1999) The Grid (2001) DCS2 RAM-based stereo (Vegas): * ADSP-2104 @ 16MHz * dual channel output (stereo) * SDRC ASIC for RAM/ROM access * RAM-based, 4MB total * used in: Gauntlet Legends (1998) Tenth Degree (1998) Gauntlet Dark Legacy (1999) War: The Final Assault (1999) DCS2 RAM-based stereo (DSIO): * ADSP-2181 @ 16.667MHz * dual channel output (stereo) * custom ASIC for RAM/ROM access * RAM-based, 4MB total * used in: Road Burners (1999) DCS2 RAM-based multi-channel (Denver): * ADSP-2181 @ 16.667MHz * 2-6 channel output * custom ASIC for RAM/ROM access * RAM-based, 4MB total * used in: San Francisco Rush: 2049 (1998) Unknown other DCS boards: * NBA Showtime * NBA Showtime / NFL Blitz 2000 Gold * Cart Fury ***************************************************************************** SDRC (Sound DRAM Control) ASIC * Boot ROM = 32k x 8 * Data ROM = Up to 16MB ROM (4 chip selects) * SRAM = 32k x 24 or 8k x 24 * common map: PGM 0800-0fff -> RAM 4800-4fff PGM 1000-1fff -> RAM 5000-5fff PGM 2000-2fff -> RAM 6000-6fff PGM 3000-3fff -> RAM 7000-7fff * bank = 0: DATA 0800-0fff -> RAM 0800-0fff DATA 1000-17ff -> RAM 0000-07ff DATA 1800-1fff -> RAM 1800-1fff DATA 2000-27ff -> RAM 1000-17ff DATA 2800-2fff -> RAM 2800-2fff DATA 3000-37ff -> RAM 2000-27ff * bank = 1: DATA 0800-0fff -> unmapped DATA 1000-17ff -> unmapped DATA 1800-1fff -> RAM 3800-3fff DATA 2000-27ff -> RAM 3000-37ff DATA 2800-2fff -> RAM 2800-2fff DATA 3000-37ff -> RAM 2000-27ff 0480 (reset = XXX0 0X00 0X00 XX00) 15:13 = SMODE (write only) 12 = SM_BK (SRAM bank: 0 or 1) 11 = SM_EN (SRAM enable: 0=disabled, 1=enabled) 9:7 = ROM_PG (ROM page select: 0-7) 5 = ROM_MS (ROM memory select: 0=boot memory, 1=data memory) 4 = ROM_SZ (ROM area size: 0=4k words, 1=1k words) 1:0 = ROM_ST (ROM memory start: 0=0000, 1=3000, 2=3400, 3=none) 0481 (reset = 000X 00X0 0X00 XX00) 15 = AREF_ACT (read only, 1=DRAM auto refresh in progress) 14 = /MUTE (mute output) 13 = /LED (LED output) 11:10 = /RES_TFS (Reset TFS outputs: low bit = channel 1&2, high = channel 3&4) 8 = TFS_INV (TFS output polarity: 0=same, 1=inverted) 7 = DM_3WS (DRAM wait states: 0=2, 1=3) 5:4 = DM_REF (DRAM refresh: 0=disabled, 1=manual, 2=auto, 3=auto 2x) 1:0 = DM_ST (DRAM memory start: 0=none, 1=0000, 2=3000, 3=3400) 0482 (reset = XXX0 0000 0000 0000) 10:0 = DM_PG[10..0] (DRAM page) 12:0 = EPM_PG[12..0] (EPROM page [low 10 bits used for 4k pages]) 0483 (reset = 1010 0000 1000 0001) 15:8 = SDRC_ID[7..0] (revision: 5A = ASIC version, A0 = FPGA version) 7 = SEC_D7 6 = SEC_D[6..1] 0 = SEC_D0 ****************************************************************************/ #include "emu.h" #include "cpu/adsp2100/adsp2100.h" #include "dcs.h" #include "sound/dmadac.h" #include "machine/midwayic.h" #define LOG_DCS_TRANSFERS (0) #define LOG_DCS_IO (0) #define LOG_BUFFER_FILLING (0) #define ENABLE_HLE_TRANSFERS (1) /************************************* * * Constants * *************************************/ #define LCTRL_OUTPUT_EMPTY 0x400 #define LCTRL_INPUT_EMPTY 0x800 #define IS_OUTPUT_EMPTY() (dcs.latch_control & LCTRL_OUTPUT_EMPTY) #define IS_OUTPUT_FULL() (!(dcs.latch_control & LCTRL_OUTPUT_EMPTY)) #define SET_OUTPUT_EMPTY() (dcs.latch_control |= LCTRL_OUTPUT_EMPTY) #define SET_OUTPUT_FULL() (dcs.latch_control &= ~LCTRL_OUTPUT_EMPTY) #define IS_INPUT_EMPTY() (dcs.latch_control & LCTRL_INPUT_EMPTY) #define IS_INPUT_FULL() (!(dcs.latch_control & LCTRL_INPUT_EMPTY)) #define SET_INPUT_EMPTY() (dcs.latch_control |= LCTRL_INPUT_EMPTY) #define SET_INPUT_FULL() (dcs.latch_control &= ~LCTRL_INPUT_EMPTY) /* These are the some of the control register, we dont use them all */ enum { IDMA_CONTROL_REG = 0, /* 3fe0 */ BDMA_INT_ADDR_REG, /* 3fe1 */ BDMA_EXT_ADDR_REG, /* 3fe2 */ BDMA_CONTROL_REG, /* 3fe3 */ BDMA_WORD_COUNT_REG, /* 3fe4 */ PROG_FLAG_DATA_REG, /* 3fe5 */ PROG_FLAG_CONTROL_REG, /* 3fe6 */ S1_AUTOBUF_REG = 15, /* 3fef */ S1_RFSDIV_REG, /* 3ff0 */ S1_SCLKDIV_REG, /* 3ff1 */ S1_CONTROL_REG, /* 3ff2 */ S0_AUTOBUF_REG, /* 3ff3 */ S0_RFSDIV_REG, /* 3ff4 */ S0_SCLKDIV_REG, /* 3ff5 */ S0_CONTROL_REG, /* 3ff6 */ S0_MCTXLO_REG, /* 3ff7 */ S0_MCTXHI_REG, /* 3ff8 */ S0_MCRXLO_REG, /* 3ff9 */ S0_MCRXHI_REG, /* 3ffa */ TIMER_SCALE_REG, /* 3ffb */ TIMER_COUNT_REG, /* 3ffc */ TIMER_PERIOD_REG, /* 3ffd */ WAITSTATES_REG, /* 3ffe */ SYSCONTROL_REG /* 3fff */ }; /* these macros are used to reference the SDRC ASIC */ #define SDRC_ROM_ST ((dcs.sdrc.reg[0] >> 0) & 3) /* 0=0000, 1=3000, 2=3400, 3=none */ #define SDRC_ROM_SZ ((dcs.sdrc.reg[0] >> 4) & 1) /* 0=4k, 1=1k */ #define SDRC_ROM_MS ((dcs.sdrc.reg[0] >> 5) & 1) /* 0=/BMS, 1=/DMS */ #define SDRC_ROM_PG ((dcs.sdrc.reg[0] >> 7) & 7) #define SDRC_SM_EN ((dcs.sdrc.reg[0] >> 11) & 1) #define SDRC_SM_BK ((dcs.sdrc.reg[0] >> 12) & 1) #define SDRC_SMODE ((dcs.sdrc.reg[0] >> 13) & 7) #define SDRC_DM_ST ((dcs.sdrc.reg[1] >> 0) & 3) /* 0=none, 1=0000, 2=3000, 3=3400 */ #define SDRC_DM_REF ((dcs.sdrc.reg[1] >> 4) & 3) #define SDRC_DM_3WS ((dcs.sdrc.reg[1] >> 7) & 1) #define SDRC_TFS_INV ((dcs.sdrc.reg[1] >> 8) & 1) #define SDRC_RES_TFS ((dcs.sdrc.reg[1] >> 10) & 3) #define SDRC_LED ((dcs.sdrc.reg[1] >> 13) & 1) #define SDRC_MUTE ((dcs.sdrc.reg[1] >> 14) & 1) #define SDRC_AREF_ACT ((dcs.sdrc.reg[1] >> 15) & 1) #define SDRC_DM_PG ((dcs.sdrc.reg[2] >> 0) & 0x7ff) #define SDRC_EPM_PG ((dcs.sdrc.reg[2] >> 0) & 0x1fff) /* these macros are used to reference the DSIO ASIC */ #define DSIO_EMPTY_FIFO ((dcs.dsio.reg[1] >> 0) & 1) #define DSIO_CUR_OUTPUT ((dcs.dsio.reg[1] >> 4) & 1) #define DSIO_RES_TFS ((dcs.dsio.reg[1] >> 10) & 1) #define DSIO_LED ((dcs.dsio.reg[1] >> 13) & 1) #define DSIO_MUTE ((dcs.dsio.reg[1] >> 14) & 1) #define DSIO_DM_PG ((dcs.dsio.reg[2] >> 0) & 0x7ff) /* these macros are used to reference the DENVER ASIC */ #define DENV_DSP_SPEED ((dcs.dsio.reg[1] >> 2) & 3) /* read only: 1=33.33MHz */ #define DENV_RES_TFS ((dcs.dsio.reg[1] >> 10) & 1) #define DENV_CHANNELS ((dcs.dsio.reg[1] >> 11) & 3) /* 0=2ch, 1=4ch, 2=6ch */ #define DENV_LED ((dcs.dsio.reg[1] >> 13) & 1) #define DENV_MUTE ((dcs.dsio.reg[1] >> 14) & 1) #define DENV_DM_PG ((dcs.dsio.reg[2] >> 0) & 0x7ff) /************************************* * * Type definitions * *************************************/ typedef struct _sdrc_state sdrc_state; struct _sdrc_state { UINT16 reg[4]; UINT8 seed; }; typedef struct _dsio_denver_state dsio_state; struct _dsio_denver_state { UINT16 reg[4]; UINT8 start_on_next_write; UINT16 channelbits; }; typedef struct _hle_transfer_state hle_transfer_state; struct _hle_transfer_state { UINT8 hle_enabled; INT32 dcs_state; INT32 state; INT32 start; INT32 stop; INT32 type; INT32 temp; INT32 writes_left; UINT16 sum; INT32 fifo_entries; timer_device *watchdog; }; typedef struct _dcs_state dcs_state; struct _dcs_state { adsp21xx_device *cpu; address_space *program; address_space *data; UINT8 rev; offs_t polling_offset; UINT32 polling_count; /* sound output */ UINT8 channels; UINT16 size; UINT16 incs; dmadac_sound_device *dmadac[6]; timer_device *reg_timer; timer_device *sport_timer; timer_device *internal_timer; INT32 ireg; UINT16 ireg_base; UINT16 control_regs[32]; /* memory access/booting */ UINT16 * bootrom; UINT32 bootrom_words; UINT16 * sounddata; UINT32 sounddata_words; UINT32 sounddata_banks; UINT16 sounddata_bank; /* I/O with the host */ UINT8 auto_ack; UINT16 latch_control; UINT16 input_data; UINT16 output_data; UINT16 output_control; UINT64 output_control_cycles; UINT8 last_output_full; UINT8 last_input_empty; UINT16 progflags; void (*output_full_cb)(running_machine &, int); void (*input_empty_cb)(running_machine &, int); UINT16 (*fifo_data_r)(device_t *device); UINT16 (*fifo_status_r)(device_t *device); /* timers */ UINT8 timer_enable; UINT8 timer_ignore; UINT64 timer_start_cycles; UINT32 timer_start_count; UINT32 timer_scale; UINT32 timer_period; UINT32 timers_fired; UINT16 *sram; UINT16 *polling_base; UINT32 *internal_program_ram; UINT32 *external_program_ram; sdrc_state sdrc; dsio_state dsio; hle_transfer_state transfer; }; /************************************* * * Statics * *************************************/ static dcs_state dcs; /************************************* * * Prototypes * *************************************/ static READ16_HANDLER( dcs_dataram_r ); static WRITE16_HANDLER( dcs_dataram_w ); static WRITE16_HANDLER( dcs_data_bank_select_w ); static void sdrc_reset(running_machine &machine); static READ16_HANDLER( sdrc_r ); static WRITE16_HANDLER( sdrc_w ); static void dsio_reset(running_machine &machine); static READ16_HANDLER( dsio_r ); static WRITE16_HANDLER( dsio_w ); static void denver_reset(running_machine &machine); static READ16_HANDLER( denver_r ); static WRITE16_HANDLER( denver_w ); static READ16_HANDLER( adsp_control_r ); static WRITE16_HANDLER( adsp_control_w ); static READ16_HANDLER( latch_status_r ); static READ16_HANDLER( fifo_input_r ); static READ16_HANDLER( input_latch_r ); static WRITE16_HANDLER( input_latch_ack_w ); static WRITE16_HANDLER( output_latch_w ); static READ16_HANDLER( output_control_r ); static WRITE16_HANDLER( output_control_w ); static void timer_enable_callback(adsp21xx_device &device, int enable); static TIMER_DEVICE_CALLBACK( internal_timer_callback ); static TIMER_DEVICE_CALLBACK( dcs_irq ); static TIMER_DEVICE_CALLBACK( sport0_irq ); static void recompute_sample_rate(running_machine &machine); static void sound_tx_callback(adsp21xx_device &device, int port, INT32 data); static READ16_HANDLER( dcs_polling_r ); static WRITE16_HANDLER( dcs_polling_w ); static TIMER_DEVICE_CALLBACK( transfer_watchdog_callback ); static int preprocess_write(running_machine &machine, UINT16 data); static void sdrc_remap_memory(running_machine &machine); /************************************* * * Original DCS Memory Maps * *************************************/ /* DCS 2k memory map */ static ADDRESS_MAP_START( dcs_2k_program_map, AS_PROGRAM, 32 ) AM_RANGE(0x0000, 0x03ff) AM_RAM AM_SHARE("dcsint") AM_RANGE(0x0800, 0x0fff) AM_RAM AM_SHARE("dcsext") AM_RANGE(0x1000, 0x17ff) AM_RAM AM_SHARE("dcsext") AM_RANGE(0x1800, 0x1fff) AM_RAM AM_SHARE("dcsext") ADDRESS_MAP_END static ADDRESS_MAP_START( dcs_2k_data_map, AS_DATA, 16 ) AM_RANGE(0x0000, 0x07ff) AM_MIRROR(0x1800) AM_READWRITE(dcs_dataram_r, dcs_dataram_w) AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank") AM_RANGE(0x3000, 0x33ff) AM_WRITE(dcs_data_bank_select_w) AM_RANGE(0x3400, 0x37ff) AM_READWRITE(input_latch_r, output_latch_w) AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END /* DCS 2k with UART memory map */ static ADDRESS_MAP_START( dcs_2k_uart_data_map, AS_DATA, 16 ) AM_RANGE(0x0000, 0x07ff) AM_MIRROR(0x1800) AM_READWRITE(dcs_dataram_r, dcs_dataram_w) AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank") AM_RANGE(0x3000, 0x33ff) AM_WRITE(dcs_data_bank_select_w) AM_RANGE(0x3400, 0x3402) AM_NOP /* UART (ignored) */ AM_RANGE(0x3403, 0x3403) AM_READWRITE(input_latch_r, output_latch_w) AM_RANGE(0x3404, 0x3405) AM_NOP /* UART (ignored) */ AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END /* DCS 8k memory map */ static ADDRESS_MAP_START( dcs_8k_program_map, AS_PROGRAM, 32 ) AM_RANGE(0x0000, 0x03ff) AM_RAM AM_SHARE("dcsint") AM_RANGE(0x0800, 0x1fff) AM_RAM AM_SHARE("dcsext") ADDRESS_MAP_END static ADDRESS_MAP_START( dcs_8k_data_map, AS_DATA, 16 ) AM_RANGE(0x0000, 0x07ff) AM_RAM AM_RANGE(0x0800, 0x1fff) AM_READWRITE(dcs_dataram_r, dcs_dataram_w) AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank") AM_RANGE(0x3000, 0x33ff) AM_WRITE(dcs_data_bank_select_w) AM_RANGE(0x3400, 0x37ff) AM_READWRITE(input_latch_r, output_latch_w) AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END /************************************* * * DCS2 Memory Maps * *************************************/ static ADDRESS_MAP_START( dcs2_2115_program_map, AS_PROGRAM, 32 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x03ff) AM_RAM AM_SHARE("dcsint") ADDRESS_MAP_END static ADDRESS_MAP_START( dcs2_2104_program_map, AS_PROGRAM, 32 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x01ff) AM_RAM AM_SHARE("dcsint") ADDRESS_MAP_END static ADDRESS_MAP_START( dcs2_2115_data_map, AS_DATA, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0400, 0x0400) AM_READWRITE(input_latch_r, input_latch_ack_w) AM_RANGE(0x0401, 0x0401) AM_WRITE(output_latch_w) AM_RANGE(0x0402, 0x0402) AM_READWRITE(output_control_r, output_control_w) AM_RANGE(0x0403, 0x0403) AM_READ(latch_status_r) AM_RANGE(0x0404, 0x0407) AM_READ(fifo_input_r) AM_RANGE(0x0480, 0x0483) AM_READWRITE(sdrc_r, sdrc_w) AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END static ADDRESS_MAP_START( dcs2_2104_data_map, AS_DATA, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0400, 0x0400) AM_READWRITE(input_latch_r, input_latch_ack_w) AM_RANGE(0x0401, 0x0401) AM_WRITE(output_latch_w) AM_RANGE(0x0402, 0x0402) AM_READWRITE(output_control_r, output_control_w) AM_RANGE(0x0403, 0x0403) AM_READ(latch_status_r) AM_RANGE(0x0404, 0x0407) AM_READ(fifo_input_r) AM_RANGE(0x0480, 0x0483) AM_READWRITE(sdrc_r, sdrc_w) AM_RANGE(0x3800, 0x38ff) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END /************************************* * * DSIO Memory Maps * *************************************/ static ADDRESS_MAP_START( dsio_program_map, AS_PROGRAM, 32 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x3fff) AM_RAM AM_SHARE("dcsint") ADDRESS_MAP_END static ADDRESS_MAP_START( dsio_data_map, AS_DATA, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x03ff) AM_RAMBANK("databank") AM_RANGE(0x0400, 0x3fdf) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END static ADDRESS_MAP_START( dsio_io_map, AS_IO, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0400, 0x0400) AM_READWRITE(input_latch_r, input_latch_ack_w) AM_RANGE(0x0401, 0x0401) AM_WRITE(output_latch_w) AM_RANGE(0x0402, 0x0402) AM_READWRITE(output_control_r, output_control_w) AM_RANGE(0x0403, 0x0403) AM_READ(latch_status_r) AM_RANGE(0x0404, 0x0407) AM_READ(fifo_input_r) AM_RANGE(0x0480, 0x0483) AM_READWRITE(dsio_r, dsio_w) ADDRESS_MAP_END /************************************* * * Denver Memory Maps * *************************************/ static ADDRESS_MAP_START( denver_program_map, AS_PROGRAM, 32 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x3fff) AM_RAM AM_SHARE("dcsint") ADDRESS_MAP_END static ADDRESS_MAP_START( denver_data_map, AS_DATA, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0000, 0x07ff) AM_RAMBANK("databank") AM_RANGE(0x0800, 0x3fdf) AM_RAM AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) ADDRESS_MAP_END static ADDRESS_MAP_START( denver_io_map, AS_IO, 16 ) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(0x0400, 0x0400) AM_READWRITE(input_latch_r, input_latch_ack_w) AM_RANGE(0x0401, 0x0401) AM_WRITE(output_latch_w) AM_RANGE(0x0402, 0x0402) AM_READWRITE(output_control_r, output_control_w) AM_RANGE(0x0403, 0x0403) AM_READ(latch_status_r) AM_RANGE(0x0404, 0x0407) AM_READ(fifo_input_r) AM_RANGE(0x0480, 0x0483) AM_READWRITE(denver_r, denver_w) ADDRESS_MAP_END /************************************* * * CPU configuration * *************************************/ static const adsp21xx_config adsp_config = { NULL, /* callback for serial receive */ sound_tx_callback, /* callback for serial transmit */ timer_enable_callback /* callback for timer fired */ }; /************************************* * * Original DCS Machine Drivers * *************************************/ /* Basic DCS system with ADSP-2105 and 2k of SRAM (T-unit, V-unit, Killer Instinct) */ MACHINE_CONFIG_FRAGMENT( dcs_audio_2k ) MCFG_CPU_ADD("dcs", ADSP2105, XTAL_10MHz) MCFG_ADSP21XX_CONFIG(adsp_config) MCFG_CPU_PROGRAM_MAP(dcs_2k_program_map) MCFG_CPU_DATA_MAP(dcs_2k_data_map) MCFG_TIMER_ADD("dcs_reg_timer", dcs_irq) MCFG_TIMER_ADD("dcs_int_timer", internal_timer_callback) MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_ADD("dac", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) MACHINE_CONFIG_END /* Basic DCS system with ADSP-2105 and 2k of SRAM, using a UART for communications (X-unit) */ MACHINE_CONFIG_DERIVED( dcs_audio_2k_uart, dcs_audio_2k ) MCFG_CPU_MODIFY("dcs") MCFG_CPU_DATA_MAP(dcs_2k_uart_data_map) MACHINE_CONFIG_END /* Basic DCS system with ADSP-2105 and 8k of SRAM (Wolf-unit) */ MACHINE_CONFIG_DERIVED( dcs_audio_8k, dcs_audio_2k ) MCFG_CPU_MODIFY("dcs") MCFG_CPU_PROGRAM_MAP(dcs_8k_program_map) MCFG_CPU_DATA_MAP(dcs_8k_data_map) MACHINE_CONFIG_END /************************************* * * DCS2 Machine Drivers * *************************************/ MACHINE_CONFIG_FRAGMENT( dcs2_audio_2115 ) MCFG_CPU_ADD("dcs2", ADSP2115, XTAL_16MHz) MCFG_ADSP21XX_CONFIG(adsp_config) MCFG_CPU_PROGRAM_MAP(dcs2_2115_program_map) MCFG_CPU_DATA_MAP(dcs2_2115_data_map) MCFG_TIMER_ADD("dcs_reg_timer", dcs_irq) MCFG_TIMER_ADD("dcs_sport_timer", sport0_irq) MCFG_TIMER_ADD("dcs_int_timer", internal_timer_callback) MCFG_TIMER_ADD("dcs_hle_timer", transfer_watchdog_callback) MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") MCFG_SOUND_ADD("dac1", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MCFG_SOUND_ADD("dac2", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( dcs2_audio_2104, dcs2_audio_2115 ) MCFG_CPU_REPLACE("dcs2", ADSP2104, XTAL_16MHz) MCFG_ADSP21XX_CONFIG(adsp_config) MCFG_CPU_PROGRAM_MAP(dcs2_2104_program_map) MCFG_CPU_DATA_MAP(dcs2_2104_data_map) MACHINE_CONFIG_END /************************************* * * DSIO Machine Drivers * *************************************/ MACHINE_CONFIG_FRAGMENT( dcs2_audio_dsio ) MCFG_CPU_ADD("dsio", ADSP2181, XTAL_32MHz) MCFG_ADSP21XX_CONFIG(adsp_config) MCFG_CPU_PROGRAM_MAP(dsio_program_map) MCFG_CPU_DATA_MAP(dsio_data_map) MCFG_CPU_IO_MAP(dsio_io_map) MCFG_TIMER_ADD("dcs_reg_timer", dcs_irq) MCFG_TIMER_ADD("dcs_int_timer", internal_timer_callback) MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") MCFG_SOUND_ADD("dac1", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MCFG_SOUND_ADD("dac2", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MACHINE_CONFIG_END /************************************* * * Denver Machine Drivers * *************************************/ MACHINE_CONFIG_FRAGMENT( dcs2_audio_denver ) MCFG_CPU_ADD("denver", ADSP2181, XTAL_33_333MHz) MCFG_ADSP21XX_CONFIG(adsp_config) MCFG_CPU_PROGRAM_MAP(denver_program_map) MCFG_CPU_DATA_MAP(denver_data_map) MCFG_CPU_IO_MAP(denver_io_map) MCFG_TIMER_ADD("dcs_reg_timer", dcs_irq) MCFG_TIMER_ADD("dcs_int_timer", internal_timer_callback) MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") MCFG_SOUND_ADD("dac1", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MCFG_SOUND_ADD("dac2", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ADD("dac3", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MCFG_SOUND_ADD("dac4", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ADD("dac5", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MCFG_SOUND_ADD("dac6", DMADAC, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MACHINE_CONFIG_END /************************************* * * ADSP booting * *************************************/ static void dcs_boot(running_machine &machine) { UINT8 buffer[0x1000]; // UINT32 max_banks; UINT16 *base; int i; switch (dcs.rev) { /* rev 1: use the last set data bank to boot from */ case 1: /* determine the base */ // max_banks = dcs.bootrom_words / 0x1000; base = dcs.bootrom + ((dcs.sounddata_bank * 0x1000) % dcs.bootrom_words); /* convert from 16-bit data to 8-bit data and boot */ for (i = 0; i < 0x1000; i++) buffer[i] = base[i]; dcs.cpu->load_boot_data(buffer, dcs.internal_program_ram); break; /* rev 2: use the ROM page in the SDRC to boot from */ case 2: /* determine the base */ if (dcs.bootrom == dcs.sounddata) { /* EPROM case: page is selected from the page register */ base = dcs.bootrom + ((SDRC_EPM_PG * 0x1000) % dcs.bootrom_words); } else { /* DRAM case: page is selected from the ROM page register */ base = dcs.bootrom + ((SDRC_ROM_PG * 0x1000) % dcs.bootrom_words); } /* convert from 16-bit data to 8-bit data and boot */ for (i = 0; i < 0x1000; i++) buffer[i] = base[i]; dcs.cpu->load_boot_data(buffer, dcs.internal_program_ram); break; /* rev 3/4: HALT the ADSP-2181 until program is downloaded via IDMA */ case 3: case 4: device_set_input_line(dcs.cpu, INPUT_LINE_HALT, ASSERT_LINE); dcs.dsio.start_on_next_write = 0; break; } } /************************************* * * System reset * *************************************/ static TIMER_CALLBACK( dcs_reset ) { if (LOG_DCS_IO) logerror("dcs_reset\n"); /* reset the memory banking */ switch (dcs.rev) { /* rev 1: just reset the bank to 0 */ case 1: dcs.sounddata_bank = 0; memory_set_bank(machine, "databank", 0); break; /* rev 2: reset the SDRC ASIC */ case 2: sdrc_reset(machine); break; /* rev 3: reset the DSIO ASIC */ case 3: dsio_reset(machine); break; /* rev 4: reset the Denver ASIC */ case 4: denver_reset(machine); break; } /* initialize our state structure and install the transmit callback */ dcs.size = 0; dcs.incs = 0; dcs.ireg = 0; /* initialize the ADSP control regs */ memset(dcs.control_regs, 0, sizeof(dcs.control_regs)); /* clear all interrupts */ device_set_input_line(dcs.cpu, ADSP2105_IRQ0, CLEAR_LINE); device_set_input_line(dcs.cpu, ADSP2105_IRQ1, CLEAR_LINE); device_set_input_line(dcs.cpu, ADSP2105_IRQ2, CLEAR_LINE); /* initialize the comm bits */ SET_INPUT_EMPTY(); SET_OUTPUT_EMPTY(); if (!dcs.last_input_empty && dcs.input_empty_cb) (*dcs.input_empty_cb)(machine, dcs.last_input_empty = 1); if (dcs.last_output_full && dcs.output_full_cb) (*dcs.output_full_cb)(machine, dcs.last_output_full = 0); /* boot */ dcs_boot(machine); /* reset timers */ dcs.timer_enable = 0; dcs.timer_scale = 1; dcs.internal_timer->reset(); /* start the SPORT0 timer */ if (dcs.sport_timer != NULL) dcs.sport_timer->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000)); /* reset the HLE transfer states */ dcs.transfer.dcs_state = dcs.transfer.state = 0; } /************************************* * * System setup * *************************************/ static void dcs_register_state(running_machine &machine) { state_save_register_global_array(machine, dcs.sdrc.reg); state_save_register_global(machine, dcs.sdrc.seed); state_save_register_global_array(machine, dcs.dsio.reg); state_save_register_global(machine, dcs.dsio.start_on_next_write); state_save_register_global(machine, dcs.dsio.channelbits); state_save_register_global(machine, dcs.channels); state_save_register_global(machine, dcs.size); state_save_register_global(machine, dcs.incs); state_save_register_global(machine, dcs.ireg); state_save_register_global(machine, dcs.ireg_base); state_save_register_global_array(machine, dcs.control_regs); state_save_register_global(machine, dcs.sounddata_bank); state_save_register_global(machine, dcs.auto_ack); state_save_register_global(machine, dcs.latch_control); state_save_register_global(machine, dcs.input_data); state_save_register_global(machine, dcs.output_data); state_save_register_global(machine, dcs.output_control); state_save_register_global(machine, dcs.output_control_cycles); state_save_register_global(machine, dcs.last_output_full); state_save_register_global(machine, dcs.last_input_empty); state_save_register_global(machine, dcs.progflags); state_save_register_global(machine, dcs.timer_enable); state_save_register_global(machine, dcs.timer_ignore); state_save_register_global(machine, dcs.timer_start_cycles); state_save_register_global(machine, dcs.timer_start_count); state_save_register_global(machine, dcs.timer_scale); state_save_register_global(machine, dcs.timer_period); state_save_register_global(machine, dcs.timers_fired); state_save_register_global(machine, dcs.transfer.dcs_state); state_save_register_global(machine, dcs.transfer.state); state_save_register_global(machine, dcs.transfer.start); state_save_register_global(machine, dcs.transfer.stop); state_save_register_global(machine, dcs.transfer.type); state_save_register_global(machine, dcs.transfer.temp); state_save_register_global(machine, dcs.transfer.writes_left); state_save_register_global(machine, dcs.transfer.sum); state_save_register_global(machine, dcs.transfer.fifo_entries); if (dcs.sram != NULL) state_save_register_global_pointer(machine, dcs.sram, 0x8000*4 / sizeof(dcs.sram[0])); if (dcs.rev == 2) machine.save().register_postload(save_prepost_delegate(FUNC(sdrc_remap_memory), &machine)); } void dcs_init(running_machine &machine) { memset(&dcs, 0, sizeof(dcs)); dcs.sram = NULL; dcs.internal_program_ram = (UINT32 *)memory_get_shared(machine, "dcsint"); dcs.external_program_ram = (UINT32 *)memory_get_shared(machine, "dcsext"); /* find the DCS CPU and the sound ROMs */ dcs.cpu = machine.device("dcs"); dcs.program = dcs.cpu->space(AS_PROGRAM); dcs.data = dcs.cpu->space(AS_DATA); dcs.rev = 1; dcs.channels = 1; dcs.dmadac[0] = machine.device("dac"); /* configure boot and sound ROMs */ dcs.bootrom = (UINT16 *)machine.region("dcs")->base(); dcs.bootrom_words = machine.region("dcs")->bytes() / 2; dcs.sounddata = dcs.bootrom; dcs.sounddata_words = dcs.bootrom_words; dcs.sounddata_banks = dcs.sounddata_words / 0x1000; memory_configure_bank(machine, "databank", 0, dcs.sounddata_banks, dcs.sounddata, 0x1000*2); /* create the timers */ dcs.internal_timer = machine.device("dcs_int_timer"); dcs.reg_timer = machine.device("dcs_reg_timer"); /* non-RAM based automatically acks */ dcs.auto_ack = TRUE; /* register for save states */ dcs_register_state(machine); /* reset the system */ dcs_reset(machine, NULL, 0); } void dcs2_init(running_machine &machine, int dram_in_mb, offs_t polling_offset) { int soundbank_words; memset(&dcs, 0, sizeof(dcs)); dcs.internal_program_ram = (UINT32 *)memory_get_shared(machine, "dcsint"); dcs.external_program_ram = (UINT32 *)memory_get_shared(machine, "dcsext"); /* find the DCS CPU and the sound ROMs */ dcs.cpu = machine.device("dcs2"); dcs.rev = 2; soundbank_words = 0x1000; if (dcs.cpu == NULL) { dcs.cpu = machine.device("dsio"); dcs.rev = 3; soundbank_words = 0x400; } if (dcs.cpu == NULL) { dcs.cpu = machine.device("denver"); dcs.rev = 4; soundbank_words = 0x800; } dcs.program = dcs.cpu->space(AS_PROGRAM); dcs.data = dcs.cpu->space(AS_DATA); dcs.channels = 2; dcs.dmadac[0] = machine.device("dac1"); dcs.dmadac[1] = machine.device("dac2"); /* always boot from the base of "dcs" */ dcs.bootrom = (UINT16 *)machine.region("dcs")->base(); dcs.bootrom_words = machine.region("dcs")->bytes() / 2; /* supports both RAM and ROM variants */ if (dram_in_mb != 0) { dcs.sounddata = auto_alloc_array(machine, UINT16, dram_in_mb << (20-1)); dcs.sounddata_words = (dram_in_mb << 20) / 2; } else { dcs.sounddata = dcs.bootrom; dcs.sounddata_words = dcs.bootrom_words; } dcs.sounddata_banks = dcs.sounddata_words / soundbank_words; if (dcs.rev != 2) memory_configure_bank(machine, "databank", 0, dcs.sounddata_banks, dcs.sounddata, soundbank_words*2); /* allocate memory for the SRAM */ dcs.sram = auto_alloc_array(machine, UINT16, 0x8000*4/2); /* create the timers */ dcs.internal_timer = machine.device("dcs_int_timer"); dcs.reg_timer = machine.device("dcs_reg_timer"); dcs.sport_timer = machine.device("dcs_sport_timer"); /* we don't do auto-ack by default */ dcs.auto_ack = FALSE; /* install the speedup handler */ dcs.polling_offset = polling_offset; if (polling_offset) dcs.polling_base = dcs.cpu->space(AS_DATA)->install_legacy_readwrite_handler(dcs.polling_offset, dcs.polling_offset, FUNC(dcs_polling_r), FUNC(dcs_polling_w)); /* allocate a watchdog timer for HLE transfers */ dcs.transfer.hle_enabled = (ENABLE_HLE_TRANSFERS && dram_in_mb != 0); if (dcs.transfer.hle_enabled) dcs.transfer.watchdog = machine.device("dcs_hle_timer"); /* register for save states */ dcs_register_state(machine); /* reset the system */ dcs_reset(machine, NULL, 0); } void dcs_set_auto_ack(running_machine &machine, int state) { dcs.auto_ack = state; } /************************************* * * Original DCS read/write handlers * *************************************/ static READ16_HANDLER( dcs_dataram_r ) { return dcs.external_program_ram[offset] >> 8; } static WRITE16_HANDLER( dcs_dataram_w ) { UINT16 newdata = dcs.external_program_ram[offset] >> 8; COMBINE_DATA(&newdata); dcs.external_program_ram[offset] = (newdata << 8) | (dcs.external_program_ram[offset] & 0xff); } static WRITE16_HANDLER( dcs_data_bank_select_w ) { dcs.sounddata_bank = data & 0x7ff; memory_set_bank(space->machine(), "databank", dcs.sounddata_bank % dcs.sounddata_banks); /* bit 11 = sound board led */ #if 0 set_led_status(space->machine(), 2, data & 0x800); #endif } /************************************* * * SDRC ASIC Memory handling * *************************************/ INLINE void sdrc_update_bank_pointers(running_machine &machine) { if (SDRC_SM_EN != 0) { int pagesize = (SDRC_ROM_SZ == 0 && SDRC_ROM_ST != 0) ? 4096 : 1024; /* update the bank pointer based on whether we are ROM-based or RAM-based */ if (dcs.bootrom == dcs.sounddata) { /* ROM-based; use the memory page to select from ROM */ if (SDRC_ROM_MS == 1 && SDRC_ROM_ST != 3) memory_set_bankptr(machine, "rompage", &dcs.sounddata[(SDRC_EPM_PG * pagesize) % dcs.sounddata_words]); } else { /* RAM-based; use the ROM page to select from ROM, and the memory page to select from RAM */ if (SDRC_ROM_MS == 1 && SDRC_ROM_ST != 3) memory_set_bankptr(machine, "rompage", &dcs.bootrom[(SDRC_ROM_PG * 4096 /*pagesize*/) % dcs.bootrom_words]); if (SDRC_DM_ST != 0) memory_set_bankptr(machine, "drampage", &dcs.sounddata[(SDRC_DM_PG * 1024) % dcs.sounddata_words]); } } } static void sdrc_remap_memory(running_machine &machine) { /* if SRAM disabled, clean it out */ if (SDRC_SM_EN == 0) { dcs.program->unmap_readwrite(0x0800, 0x3fff); dcs.data->unmap_readwrite(0x0800, 0x37ff); } /* otherwise, map the SRAM */ else { /* first start with a clean program map */ dcs.program->install_ram(0x0800, 0x3fff, dcs.sram + 0x4800); /* set up the data map based on the SRAM banking */ /* map 0: ram from 0800-37ff */ if (SDRC_SM_BK == 0) { dcs.data->install_ram(0x0800, 0x17ff, dcs.sram + 0x0000); dcs.data->install_ram(0x1800, 0x27ff, dcs.sram + 0x1000); dcs.data->install_ram(0x2800, 0x37ff, dcs.sram + 0x2000); } /* map 1: nothing from 0800-17ff, alternate RAM at 1800-27ff, same RAM at 2800-37ff */ else { dcs.data->unmap_readwrite(0x0800, 0x17ff); dcs.data->install_ram(0x1800, 0x27ff, dcs.sram + 0x3000); dcs.data->install_ram(0x2800, 0x37ff, dcs.sram + 0x2000); } } /* map the ROM page as bank 25 */ if (SDRC_ROM_MS == 1 && SDRC_ROM_ST != 3) { int baseaddr = (SDRC_ROM_ST == 0) ? 0x0000 : (SDRC_ROM_ST == 1) ? 0x3000 : 0x3400; int pagesize = (SDRC_ROM_SZ == 0 && SDRC_ROM_ST != 0) ? 4096 : 1024; dcs.data->install_read_bank(baseaddr, baseaddr + pagesize - 1, "rompage"); } /* map the DRAM page as bank 26 */ if (SDRC_DM_ST != 0) { int baseaddr = (SDRC_DM_ST == 1) ? 0x0000 : (SDRC_DM_ST == 2) ? 0x3000 : 0x3400; dcs.data->install_readwrite_bank(baseaddr, baseaddr + 0x3ff, "drampage"); } /* update the bank pointers */ sdrc_update_bank_pointers(machine); /* reinstall the polling hotspot */ if (dcs.polling_offset) dcs.polling_base = dcs.cpu->space(AS_DATA)->install_legacy_readwrite_handler(dcs.polling_offset, dcs.polling_offset, FUNC(dcs_polling_r), FUNC(dcs_polling_w)); } static void sdrc_reset(running_machine &machine) { memset(dcs.sdrc.reg, 0, sizeof(dcs.sdrc.reg)); sdrc_remap_memory(machine); } /************************************* * * SDRC ASIC read/write * *************************************/ static READ16_HANDLER( sdrc_r ) { sdrc_state &sdrc = dcs.sdrc; UINT16 result = sdrc.reg[offset]; /* offset 3 is for security */ if (offset == 3) { switch (SDRC_SMODE) { default: case 0: /* no-op */ result = 0x5a81; break; case 1: /* write seed */ result = 0x5aa4; break; case 2: /* read data */ result = 0x5a00 | ((sdrc.seed & 0x3f) << 1); break; case 3: /* shift left */ result = 0x5ab9; break; case 4: /* add */ result = 0x5a03; break; case 5: /* xor */ result = 0x5a69; break; case 6: /* prg */ result = 0x5a20; break; case 7: /* invert */ result = 0x5aff; break; } } return result; } static WRITE16_HANDLER( sdrc_w ) { sdrc_state &sdrc = dcs.sdrc; UINT16 diff = sdrc.reg[offset] ^ data; switch (offset) { /* offset 0 controls ROM mapping */ case 0: sdrc.reg[0] = data; if (diff & 0x1833) sdrc_remap_memory(space->machine()); if (diff & 0x0380) sdrc_update_bank_pointers(space->machine()); break; /* offset 1 controls RAM mapping */ case 1: sdrc.reg[1] = data; //dmadac_enable(&dcs.dmadac[0], dcs.channels, SDRC_MUTE); if (diff & 0x0003) sdrc_remap_memory(space->machine()); break; /* offset 2 controls paging */ case 2: sdrc.reg[2] = data; if (diff & 0x1fff) sdrc_update_bank_pointers(space->machine()); break; /* offset 3 controls security */ case 3: switch (SDRC_SMODE) { case 0: /* no-op */ case 2: /* read data */ break; case 1: /* write seed */ sdrc.seed = data & 0xff; break; case 3: /* shift left */ sdrc.seed = (sdrc.seed << 1) | 1; break; case 4: /* add */ sdrc.seed += sdrc.seed >> 1; break; case 5: /* xor */ sdrc.seed ^= (sdrc.seed << 1) | 1; break; case 6: /* prg */ sdrc.seed = (((sdrc.seed << 7) ^ (sdrc.seed << 5) ^ (sdrc.seed << 4) ^ (sdrc.seed << 3)) & 0x80) | (sdrc.seed >> 1); break; case 7: /* invert */ sdrc.seed = ~sdrc.seed; break; } break; } } /************************************* * * DSIO ASIC read/write * *************************************/ static void dsio_reset(running_machine &machine) { memset(&dcs.dsio, 0, sizeof(dcs.dsio)); } static READ16_HANDLER( dsio_r ) { dsio_state &dsio = dcs.dsio; UINT16 result = dsio.reg[offset]; if (offset == 1) { /* bit 4 specifies which channel is being output */ dsio.channelbits ^= 0x0010; result = (result & ~0x0010) | dsio.channelbits; } return result; } static WRITE16_HANDLER( dsio_w ) { dsio_state &dsio = dcs.dsio; switch (offset) { /* offset 1 controls I/O */ case 1: dsio.reg[1] = data; /* determine /MUTE and number of channels */ dmadac_enable(&dcs.dmadac[0], dcs.channels, DSIO_MUTE); /* bit 0 resets the FIFO */ midway_ioasic_fifo_reset_w(space->machine(), DSIO_EMPTY_FIFO ^ 1); break; /* offset 2 controls RAM pages */ case 2: dsio.reg[2] = data; memory_set_bank(space->machine(), "databank", DSIO_DM_PG % dcs.sounddata_banks); break; } } /************************************* * * Denver ASIC read/write * *************************************/ static void denver_reset(running_machine &machine) { memset(&dcs.dsio, 0, sizeof(dcs.dsio)); } static READ16_HANDLER( denver_r ) { UINT16 result = dcs.dsio.reg[offset]; if (offset == 3) { /* returns 1 for DRAM, 2 for EPROM-based */ result = 0x0001; } return result; } static WRITE16_HANDLER( denver_w ) { dsio_state &dsio = dcs.dsio; int enable, channels, chan; switch (offset) { /* offset 1 controls I/O */ case 1: dsio.reg[1] = data; /* determine /MUTE and number of channels */ enable = DENV_MUTE; channels = 2 + 2 * DENV_CHANNELS; /* if the number of channels has changed, adjust */ if (channels != dcs.channels) { dcs.channels = channels; for (chan = 0; chan < dcs.channels; chan++) { char buffer[10]; sprintf(buffer, "dac%d", chan + 1); dcs.dmadac[chan] = space->machine().device(buffer); } dmadac_enable(&dcs.dmadac[0], dcs.channels, enable); if (dcs.channels < 6) dmadac_enable(&dcs.dmadac[dcs.channels], 6 - dcs.channels, FALSE); recompute_sample_rate(space->machine()); } break; /* offset 2 controls RAM pages */ case 2: dsio.reg[2] = data; memory_set_bank(space->machine(), "databank", DENV_DM_PG % dcs.sounddata_bank); break; /* offset 3 controls FIFO reset */ case 3: midway_ioasic_fifo_reset_w(space->machine(), 1); break; } } /************************************* * * DSIO/Denver IDMA access * *************************************/ WRITE32_HANDLER( dsio_idma_addr_w ) { dsio_state &dsio = dcs.dsio; if (LOG_DCS_TRANSFERS) logerror("%08X:IDMA_addr = %04X\n", cpu_get_pc(&space->device()), data); downcast(dcs.cpu)->idma_addr_w(data); if (data == 0) dsio.start_on_next_write = 2; } WRITE32_HANDLER( dsio_idma_data_w ) { dsio_state &dsio = dcs.dsio; UINT32 pc = cpu_get_pc(&space->device()); if (ACCESSING_BITS_0_15) { if (LOG_DCS_TRANSFERS) logerror("%08X:IDMA_data_w(%04X) = %04X\n", pc, downcast(dcs.cpu)->idma_addr_r(), data & 0xffff); downcast(dcs.cpu)->idma_data_w(data & 0xffff); } if (ACCESSING_BITS_16_31) { if (LOG_DCS_TRANSFERS) logerror("%08X:IDMA_data_w(%04X) = %04X\n", pc, downcast(dcs.cpu)->idma_addr_r(), data >> 16); downcast(dcs.cpu)->idma_data_w(data >> 16); } if (dsio.start_on_next_write && --dsio.start_on_next_write == 0) { logerror("Starting DSIO CPU\n"); device_set_input_line(dcs.cpu, INPUT_LINE_HALT, CLEAR_LINE); } } READ32_HANDLER( dsio_idma_data_r ) { UINT32 result; result = downcast(dcs.cpu)->idma_data_r(); if (LOG_DCS_TRANSFERS) logerror("%08X:IDMA_data_r(%04X) = %04X\n", cpu_get_pc(&space->device()), downcast(dcs.cpu)->idma_addr_r(), result); return result; } /*************************************************************************** DCS COMMUNICATIONS ****************************************************************************/ void dcs_set_io_callbacks(void (*output_full_cb)(running_machine &, int), void (*input_empty_cb)(running_machine &, int)) { dcs.input_empty_cb = input_empty_cb; dcs.output_full_cb = output_full_cb; } void dcs_set_fifo_callbacks(UINT16 (*fifo_data_r)(device_t *device), UINT16 (*fifo_status_r)(device_t *device)) { dcs.fifo_data_r = fifo_data_r; dcs.fifo_status_r = fifo_status_r; } int dcs_control_r(running_machine &machine) { /* only boost for DCS2 boards */ if (!dcs.auto_ack && !dcs.transfer.hle_enabled) machine.scheduler().boost_interleave(attotime::from_nsec(500), attotime::from_usec(5)); return dcs.latch_control; } void dcs_reset_w(running_machine &machine, int state) { /* going high halts the CPU */ if (state) { logerror("%s: DCS reset = %d\n", machine.describe_context(), state); /* just run through the init code again */ machine.scheduler().synchronize(FUNC(dcs_reset)); device_set_input_line(dcs.cpu, INPUT_LINE_RESET, ASSERT_LINE); } /* going low resets and reactivates the CPU */ else device_set_input_line(dcs.cpu, INPUT_LINE_RESET, CLEAR_LINE); } static READ16_HANDLER( latch_status_r ) { int result = 0; if (IS_INPUT_FULL()) result |= 0x80; if (IS_OUTPUT_EMPTY()) result |= 0x40; if (dcs.fifo_status_r != NULL && (!dcs.transfer.hle_enabled || dcs.transfer.state == 0)) result |= (*dcs.fifo_status_r)(dcs.cpu) & 0x38; if (dcs.transfer.hle_enabled && dcs.transfer.state != 0) result |= 0x08; return result; } static READ16_HANDLER( fifo_input_r ) { if (dcs.fifo_data_r) return (*dcs.fifo_data_r)(dcs.cpu); else return 0xffff; } /*************************************************************************** INPUT LATCH (data from host to DCS) ****************************************************************************/ static void dcs_delayed_data_w(running_machine &machine, int data) { if (LOG_DCS_IO) logerror("%s:dcs_data_w(%04X)\n", machine.describe_context(), data); /* boost the interleave temporarily */ machine.scheduler().boost_interleave(attotime::from_nsec(500), attotime::from_usec(5)); /* set the IRQ line on the ADSP */ device_set_input_line(dcs.cpu, ADSP2105_IRQ2, ASSERT_LINE); /* indicate we are no longer empty */ if (dcs.last_input_empty && dcs.input_empty_cb) (*dcs.input_empty_cb)(machine, dcs.last_input_empty = 0); SET_INPUT_FULL(); /* set the data */ dcs.input_data = data; } static TIMER_CALLBACK( dcs_delayed_data_w_callback ) { dcs_delayed_data_w(machine, param); } void dcs_data_w(running_machine &machine, int data) { /* preprocess the write */ if (preprocess_write(machine, data)) return; /* if we are DCS1, set a timer to latch the data */ if (dcs.sport_timer == NULL) machine.scheduler().synchronize(FUNC(dcs_delayed_data_w_callback), data); else dcs_delayed_data_w(machine, data); } static WRITE16_HANDLER( input_latch_ack_w ) { if (!dcs.last_input_empty && dcs.input_empty_cb) (*dcs.input_empty_cb)(space->machine(), dcs.last_input_empty = 1); SET_INPUT_EMPTY(); device_set_input_line(dcs.cpu, ADSP2105_IRQ2, CLEAR_LINE); } static READ16_HANDLER( input_latch_r ) { if (dcs.auto_ack) input_latch_ack_w(space,0,0,0xffff); if (LOG_DCS_IO) logerror("%08X:input_latch_r(%04X)\n", cpu_get_pc(&space->device()), dcs.input_data); return dcs.input_data; } /*************************************************************************** OUTPUT LATCH (data from DCS to host) ****************************************************************************/ static TIMER_CALLBACK( latch_delayed_w ) { if (!dcs.last_output_full && dcs.output_full_cb) (*dcs.output_full_cb)(machine, dcs.last_output_full = 1); SET_OUTPUT_FULL(); dcs.output_data = param; } static WRITE16_HANDLER( output_latch_w ) { if (LOG_DCS_IO) logerror("%08X:output_latch_w(%04X) (empty=%d)\n", cpu_get_pc(&space->device()), data, IS_OUTPUT_EMPTY()); space->machine().scheduler().synchronize(FUNC(latch_delayed_w), data); } static void delayed_ack_w(running_machine &machine) { SET_OUTPUT_EMPTY(); } static TIMER_CALLBACK( delayed_ack_w_callback ) { delayed_ack_w(machine); } void dcs_ack_w(running_machine &machine) { machine.scheduler().synchronize(FUNC(delayed_ack_w_callback)); } int dcs_data_r(running_machine &machine) { /* data is actually only 8 bit (read from d8-d15) */ if (dcs.last_output_full && dcs.output_full_cb) (*dcs.output_full_cb)(machine, dcs.last_output_full = 0); if (dcs.auto_ack) delayed_ack_w(machine); if (LOG_DCS_IO) logerror("%s:dcs_data_r(%04X)\n", machine.describe_context(), dcs.output_data); return dcs.output_data; } /*************************************************************************** OUTPUT CONTROL BITS (has 3 additional lines to the host) ****************************************************************************/ static TIMER_CALLBACK( output_control_delayed_w ) { if (LOG_DCS_IO) logerror("output_control = %04X\n", param); dcs.output_control = param; dcs.output_control_cycles = 0; } static WRITE16_HANDLER( output_control_w ) { if (LOG_DCS_IO) logerror("%04X:output_control = %04X\n", cpu_get_pc(&space->device()), data); space->machine().scheduler().synchronize(FUNC(output_control_delayed_w), data); } static READ16_HANDLER( output_control_r ) { dcs.output_control_cycles = dcs.cpu->total_cycles(); return dcs.output_control; } int dcs_data2_r(running_machine &machine) { return dcs.output_control; } /************************************* * * Timer management * *************************************/ static void update_timer_count(running_machine &machine) { UINT64 periods_since_start; UINT64 elapsed_cycles; UINT64 elapsed_clocks; /* if not enabled, skip */ if (!dcs.timer_enable) return; /* count cycles */ elapsed_cycles = dcs.cpu->total_cycles() - dcs.timer_start_cycles; elapsed_clocks = elapsed_cycles / dcs.timer_scale; /* if we haven't counted past the initial count yet, just do that */ if (elapsed_clocks < dcs.timer_start_count + 1) dcs.control_regs[TIMER_COUNT_REG] = dcs.timer_start_count - elapsed_clocks; /* otherwise, count how many periods */ else { elapsed_clocks -= dcs.timer_start_count + 1; periods_since_start = elapsed_clocks / (dcs.timer_period + 1); elapsed_clocks -= periods_since_start * (dcs.timer_period + 1); dcs.control_regs[TIMER_COUNT_REG] = dcs.timer_period - elapsed_clocks; } } static TIMER_DEVICE_CALLBACK( internal_timer_callback ) { INT64 target_cycles; /* compute the absolute cycle when the next one should fire */ /* we do this to avoid drifting */ dcs.timers_fired++; target_cycles = dcs.timer_start_cycles + dcs.timer_scale * (dcs.timer_start_count + 1 + dcs.timers_fired * (UINT64)(dcs.timer_period + 1)); target_cycles -= dcs.cpu->total_cycles(); /* set the next timer, but only if it's for a reasonable number */ if (!dcs.timer_ignore && (dcs.timer_period > 10 || dcs.timer_scale > 1)) timer.adjust(dcs.cpu->cycles_to_attotime(target_cycles)); /* the IRQ line is edge triggered */ device_set_input_line(dcs.cpu, ADSP2105_TIMER, ASSERT_LINE); device_set_input_line(dcs.cpu, ADSP2105_TIMER, CLEAR_LINE); } static void reset_timer(running_machine &machine) { /* if not enabled, skip */ if (!dcs.timer_enable) return; /* compute the time until the first firing */ dcs.timer_start_cycles = dcs.cpu->total_cycles(); dcs.timers_fired = 0; /* if this is the first timer, check the IRQ routine for the DRAM refresh stub */ /* if that's all the timer does, we don't really need to fire */ if (!dcs.timer_ignore) { /* Road Burners: @ 28: JMP $0032 18032F, same code at $32 */ if (dcs.program->read_dword(0x18*4) == 0x0c0030 && /* ENA SEC_REG */ dcs.program->read_dword(0x19*4) == 0x804828 && /* SI = DM($0482) */ dcs.program->read_dword(0x1a*4) == 0x904828 && /* DM($0482) = SI */ dcs.program->read_dword(0x1b*4) == 0x0C0020 && /* DIS SEC_REG */ dcs.program->read_dword(0x1c*4) == 0x0A001F) /* RTI */ { dcs.timer_ignore = TRUE; } } /* adjust the timer if not optimized */ if (!dcs.timer_ignore) dcs.internal_timer->adjust(dcs.cpu->cycles_to_attotime(dcs.timer_scale * (dcs.timer_start_count + 1))); } static void timer_enable_callback(adsp21xx_device &device, int enable) { dcs.timer_enable = enable; dcs.timer_ignore = 0; if (enable) { //mame_printf_debug("Timer enabled @ %d cycles/int, or %f Hz\n", dcs.timer_scale * (dcs.timer_period + 1), 1.0 / dcs.cpu->cycles_to_attotime(dcs.timer_scale * (dcs.timer_period + 1))); reset_timer(device.machine()); } else { //mame_printf_debug("Timer disabled\n"); dcs.internal_timer->reset(); } } /*************************************************************************** ADSP CONTROL & TRANSMIT CALLBACK ****************************************************************************/ /* The ADSP2105 memory map when in boot rom mode is as follows: Program Memory: 0x0000-0x03ff = Internal Program Ram (contents of boot rom gets copied here) 0x0400-0x07ff = Reserved 0x0800-0x3fff = External Program Ram Data Memory: 0x0000-0x03ff = External Data - 0 Waitstates 0x0400-0x07ff = External Data - 1 Waitstates 0x0800-0x2fff = External Data - 2 Waitstates 0x3000-0x33ff = External Data - 3 Waitstates 0x3400-0x37ff = External Data - 4 Waitstates 0x3800-0x39ff = Internal Data Ram 0x3a00-0x3bff = Reserved (extra internal ram space on ADSP2101, etc) 0x3c00-0x3fff = Memory Mapped control registers & reserved. */ static READ16_HANDLER( adsp_control_r ) { UINT16 result = 0xffff; switch (offset) { case PROG_FLAG_DATA_REG: /* Denver waits for this & 0x000e == 0x0000 */ /* Denver waits for this & 0x000e == 0x0006 */ result = dcs.progflags ^= 0x0006; break; case IDMA_CONTROL_REG: result = downcast(dcs.cpu)->idma_addr_r(); break; case TIMER_COUNT_REG: update_timer_count(space->machine()); result = dcs.control_regs[offset]; break; default: result = dcs.control_regs[offset]; break; } return result; } static WRITE16_HANDLER( adsp_control_w ) { dcs.control_regs[offset] = data; switch (offset) { case SYSCONTROL_REG: /* bit 9 forces a reset */ if (data & 0x0200) { logerror("%04X:Rebooting DCS due to SYSCONTROL write\n", cpu_get_pc(&space->device())); device_set_input_line(dcs.cpu, INPUT_LINE_RESET, PULSE_LINE); dcs_boot(space->machine()); dcs.control_regs[SYSCONTROL_REG] = 0; } /* see if SPORT1 got disabled */ if ((data & 0x0800) == 0) { dmadac_enable(&dcs.dmadac[0], dcs.channels, 0); dcs.reg_timer->reset(); } break; case S1_AUTOBUF_REG: /* autobuffer off: nuke the timer, and disable the DAC */ if ((data & 0x0002) == 0) { dmadac_enable(&dcs.dmadac[0], dcs.channels, 0); dcs.reg_timer->reset(); } break; case S1_CONTROL_REG: if (((data >> 4) & 3) == 2) logerror("DCS: Oh no!, the data is compresed with u-law encoding\n"); if (((data >> 4) & 3) == 3) logerror("DCS: Oh no!, the data is compresed with A-law encoding\n"); break; case TIMER_SCALE_REG: data = (data & 0xff) + 1; if (data != dcs.timer_scale) { update_timer_count(space->machine()); dcs.timer_scale = data; reset_timer(space->machine()); } break; case TIMER_COUNT_REG: dcs.timer_start_count = data; reset_timer(space->machine()); break; case TIMER_PERIOD_REG: if (data != dcs.timer_period) { update_timer_count(space->machine()); dcs.timer_period = data; reset_timer(space->machine()); } break; case IDMA_CONTROL_REG: downcast(dcs.cpu)->idma_addr_w(data); break; } } /*************************************************************************** DCS IRQ GENERATION CALLBACKS ****************************************************************************/ static TIMER_DEVICE_CALLBACK( dcs_irq ) { /* get the index register */ int reg = cpu_get_reg(dcs.cpu, ADSP2100_I0 + dcs.ireg); /* copy the current data into the buffer */ { int count = dcs.size / 2; INT16 buffer[0x400]; int i; for (i = 0; i < count; i++) { buffer[i] = dcs.data->read_word(reg * 2); reg += dcs.incs; } if (dcs.channels) dmadac_transfer(&dcs.dmadac[0], dcs.channels, 1, dcs.channels, (dcs.size / 2) / dcs.channels, buffer); } /* check for wrapping */ if (reg >= dcs.ireg_base + dcs.size) { /* reset the base pointer */ reg = dcs.ireg_base; /* generate the (internal, thats why the pulse) irq */ generic_pulse_irq_line(dcs.cpu, ADSP2105_IRQ1); } /* store it */ dcs.cpu->set_state(ADSP2100_I0 + dcs.ireg, reg); } static TIMER_DEVICE_CALLBACK( sport0_irq ) { /* this latches internally, so we just pulse */ /* note that there is non-interrupt code that reads/modifies/writes the output_control */ /* register; if we don't interlock it, we will eventually lose sound (see CarnEvil) */ /* so we skip the SPORT interrupt if we read with output_control within the last 5 cycles */ if ((dcs.cpu->total_cycles() - dcs.output_control_cycles) > 5) { device_set_input_line(dcs.cpu, ADSP2115_SPORT0_RX, ASSERT_LINE); device_set_input_line(dcs.cpu, ADSP2115_SPORT0_RX, CLEAR_LINE); } } static void recompute_sample_rate(running_machine &machine) { /* calculate how long until we generate an interrupt */ /* frequency the time per each bit sent */ attotime sample_period = attotime::from_hz(dcs.cpu->unscaled_clock()) * (2 * (dcs.control_regs[S1_SCLKDIV_REG] + 1)); /* now put it down to samples, so we know what the channel frequency has to be */ sample_period = sample_period * (16 * dcs.channels); dmadac_set_frequency(&dcs.dmadac[0], dcs.channels, ATTOSECONDS_TO_HZ(sample_period.attoseconds)); dmadac_enable(&dcs.dmadac[0], dcs.channels, 1); /* fire off a timer wich will hit every half-buffer */ if (dcs.incs) { attotime period = (sample_period * dcs.size) / (2 * dcs.channels * dcs.incs); dcs.reg_timer->adjust(period, 0, period); } } static void sound_tx_callback(adsp21xx_device &device, int port, INT32 data) { /* check if it's for SPORT1 */ if (port != 1) return; /* check if SPORT1 is enabled */ if (dcs.control_regs[SYSCONTROL_REG] & 0x0800) /* bit 11 */ { /* we only support autobuffer here (wich is what this thing uses), bail if not enabled */ if (dcs.control_regs[S1_AUTOBUF_REG] & 0x0002) /* bit 1 */ { /* get the autobuffer registers */ int mreg, lreg; UINT16 source; dcs.ireg = (dcs.control_regs[S1_AUTOBUF_REG] >> 9) & 7; mreg = (dcs.control_regs[S1_AUTOBUF_REG] >> 7) & 3; mreg |= dcs.ireg & 0x04; /* msb comes from ireg */ lreg = dcs.ireg; /* now get the register contents in a more legible format */ /* we depend on register indexes to be continuous (wich is the case in our core) */ source = device.state(ADSP2100_I0 + dcs.ireg); dcs.incs = device.state(ADSP2100_M0 + mreg); dcs.size = device.state(ADSP2100_L0 + lreg); /* get the base value, since we need to keep it around for wrapping */ source -= dcs.incs; /* make it go back one so we dont lose the first sample */ device.set_state(ADSP2100_I0 + dcs.ireg, source); /* save it as it is now */ dcs.ireg_base = source; /* recompute the sample rate and timer */ recompute_sample_rate(device.machine()); return; } else logerror( "ADSP SPORT1: trying to transmit and autobuffer not enabled!\n" ); } /* if we get there, something went wrong. Disable playing */ dmadac_enable(&dcs.dmadac[0], dcs.channels, 0); /* remove timer */ dcs.reg_timer->reset(); } /*************************************************************************** VERY BASIC & SAFE OPTIMIZATIONS ****************************************************************************/ static READ16_HANDLER( dcs_polling_r ) { if (dcs.polling_count++ > 5) device_eat_cycles(&space->device(), 10000); return *dcs.polling_base; } static WRITE16_HANDLER( dcs_polling_w ) { dcs.polling_count = 0; COMBINE_DATA(dcs.polling_base); } /*************************************************************************** DATA TRANSFER HLE MECHANISM ****************************************************************************/ void dcs_fifo_notify(running_machine &machine, int count, int max) { hle_transfer_state &transfer = dcs.transfer; /* skip if not in mid-transfer */ if (!transfer.hle_enabled || transfer.state == 0 || !dcs.fifo_data_r) { transfer.fifo_entries = 0; return; } /* preprocess a word */ transfer.fifo_entries = count; if (transfer.state != 5 || transfer.fifo_entries == transfer.writes_left || transfer.fifo_entries >= 256) { for ( ; transfer.fifo_entries; transfer.fifo_entries--) preprocess_write(machine, (*dcs.fifo_data_r)(dcs.cpu)); } } static TIMER_DEVICE_CALLBACK( transfer_watchdog_callback ) { hle_transfer_state &transfer = dcs.transfer; int starting_writes_left = param; if (transfer.fifo_entries && starting_writes_left == transfer.writes_left) { for ( ; transfer.fifo_entries; transfer.fifo_entries--) preprocess_write(timer.machine(), (*dcs.fifo_data_r)(dcs.cpu)); } if (transfer.watchdog != NULL) transfer.watchdog->adjust(attotime::from_msec(1), transfer.writes_left); } static TIMER_CALLBACK( s1_ack_callback2 ) { /* if the output is full, stall for a usec */ if (IS_OUTPUT_FULL()) { machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s1_ack_callback2), param); return; } output_latch_w(dcs.cpu->memory().space(AS_PROGRAM), 0, 0x000a, 0xffff); } static TIMER_CALLBACK( s1_ack_callback1 ) { /* if the output is full, stall for a usec */ if (IS_OUTPUT_FULL()) { machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s1_ack_callback1), param); return; } output_latch_w(dcs.cpu->memory().space(AS_PROGRAM), 0, param, 0xffff); /* chain to the next word we need to write back */ machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s1_ack_callback2)); } static int preprocess_stage_1(running_machine &machine, UINT16 data) { hle_transfer_state &transfer = dcs.transfer; switch (transfer.state) { case 0: /* look for command 0x001a to transfer chunks of data */ if (data == 0x001a) { if (LOG_DCS_TRANSFERS) logerror("%s:DCS Transfer command %04X\n", machine.describe_context(), data); transfer.state++; if (transfer.hle_enabled) return 1; } /* look for command 0x002a to start booting the uploaded program */ else if (data == 0x002a) { if (LOG_DCS_TRANSFERS) logerror("%s:DCS State change %04X\n", machine.describe_context(), data); transfer.dcs_state = 1; } /* anything else is ignored */ else { if (LOG_DCS_TRANSFERS) logerror("Command: %04X\n", data); } break; case 1: /* first word is the start address */ transfer.start = data; transfer.state++; if (LOG_DCS_TRANSFERS) logerror("Start address = %04X\n", transfer.start); if (transfer.hle_enabled) return 1; break; case 2: /* second word is the stop address */ transfer.stop = data; transfer.state++; if (LOG_DCS_TRANSFERS) logerror("Stop address = %04X\n", transfer.stop); if (transfer.hle_enabled) return 1; break; case 3: /* third word is the transfer type */ /* transfer type 0 = program memory */ /* transfer type 1 = SRAM bank 0 */ /* transfer type 2 = SRAM bank 1 */ transfer.type = data; transfer.state++; if (LOG_DCS_TRANSFERS) logerror("Transfer type = %04X\n", transfer.type); /* at this point, we can compute how many words to expect for the transfer */ transfer.writes_left = transfer.stop - transfer.start + 1; if (transfer.type == 0) transfer.writes_left *= 2; /* reset the checksum */ transfer.sum = 0; /* handle the HLE case */ if (transfer.hle_enabled) { if (transfer.type == 1 && SDRC_SM_BK == 1) { dcs.sdrc.reg[0] &= ~0x1000; sdrc_remap_memory(machine); } if (transfer.type == 2 && SDRC_SM_BK == 0) { dcs.sdrc.reg[0] |= 0x1000; sdrc_remap_memory(machine); } return 1; } break; case 4: /* accumulate the sum over all data */ transfer.sum += data; /* if we're out, stop the transfer */ if (--transfer.writes_left == 0) { if (LOG_DCS_TRANSFERS) logerror("Transfer done, sum = %04X\n", transfer.sum); transfer.state = 0; } /* handle the HLE case */ if (transfer.hle_enabled) { /* write the new data to memory */ if (transfer.type == 0) { if (transfer.writes_left & 1) transfer.temp = data; else dcs.program->write_dword(transfer.start++ * 4, (transfer.temp << 8) | (data & 0xff)); } else dcs.data->write_word(transfer.start++ * 2, data); /* if we're done, start a timer to send the response words */ if (transfer.state == 0) machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s1_ack_callback1), transfer.sum); return 1; } break; } return 0; } static TIMER_CALLBACK( s2_ack_callback ) { address_space *space = dcs.cpu->memory().space(AS_PROGRAM); /* if the output is full, stall for a usec */ if (IS_OUTPUT_FULL()) { machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s2_ack_callback), param); return; } output_latch_w(space, 0, param, 0xffff); output_control_w(space, 0, (dcs.output_control & ~0xff00) | 0x0300, 0xffff); } static int preprocess_stage_2(running_machine &machine, UINT16 data) { hle_transfer_state &transfer = dcs.transfer; switch (transfer.state) { case 0: /* look for command 0x55d0 or 0x55d1 to transfer chunks of data */ if (data == 0x55d0 || data == 0x55d1) { if (LOG_DCS_TRANSFERS) logerror("%s:DCS Transfer command %04X\n", machine.describe_context(), data); transfer.state++; if (transfer.hle_enabled) return 1; } /* anything else is ignored */ else { if (LOG_DCS_TRANSFERS) logerror("%s:Command: %04X\n", machine.describe_context(), data); } break; case 1: /* first word is the upper bits of the start address */ transfer.start = data << 16; transfer.state++; if (transfer.hle_enabled) return 1; break; case 2: /* second word is the lower bits of the start address */ transfer.start |= data; transfer.state++; if (LOG_DCS_TRANSFERS) logerror("Start address = %08X\n", transfer.start); if (transfer.hle_enabled) return 1; break; case 3: /* third word is the upper bits of the stop address */ transfer.stop = data << 16; transfer.state++; if (transfer.hle_enabled) return 1; break; case 4: /* fourth word is the lower bits of the stop address */ transfer.stop |= data; transfer.state++; if (LOG_DCS_TRANSFERS) logerror("Stop address = %08X\n", transfer.stop); /* at this point, we can compute how many words to expect for the transfer */ transfer.writes_left = transfer.stop - transfer.start + 1; /* reset the checksum */ transfer.sum = 0; if (transfer.hle_enabled) { transfer.watchdog->adjust(attotime::from_msec(1), transfer.writes_left); return 1; } break; case 5: /* accumulate the sum over all data */ transfer.sum += data; /* if we're out, stop the transfer */ if (--transfer.writes_left == 0) { if (LOG_DCS_TRANSFERS) logerror("Transfer done, sum = %04X\n", transfer.sum); transfer.state = 0; } /* handle the HLE case */ if (transfer.hle_enabled) { /* write the new data to memory */ dcs.sounddata[transfer.start++] = data; /* if we're done, start a timer to send the response words */ if (transfer.state == 0) { machine.scheduler().timer_set(attotime::from_usec(1), FUNC(s2_ack_callback), transfer.sum); transfer.watchdog->reset(); } return 1; } break; } return 0; } static int preprocess_write(running_machine &machine, UINT16 data) { hle_transfer_state &transfer = dcs.transfer; int result; /* if we're not DCS2, skip */ if (dcs.sport_timer == NULL) return 0; /* state 0 - initialization phase */ if (transfer.dcs_state == 0) result = preprocess_stage_1(machine, data); else result = preprocess_stage_2(machine, data); /* if we did the write, toggle the full/not full state so interrupts are generated */ if (result && dcs.input_empty_cb) { if (dcs.last_input_empty) (*dcs.input_empty_cb)(machine, dcs.last_input_empty = 0); if (!dcs.last_input_empty) (*dcs.input_empty_cb)(machine, dcs.last_input_empty = 1); } return result; }