diff options
Diffstat (limited to 'src/devices/bus/isa/areplay.cpp')
-rw-r--r-- | src/devices/bus/isa/areplay.cpp | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/src/devices/bus/isa/areplay.cpp b/src/devices/bus/isa/areplay.cpp new file mode 100644 index 00000000000..68c665f8bab --- /dev/null +++ b/src/devices/bus/isa/areplay.cpp @@ -0,0 +1,614 @@ +// license:BSD-3-Clause +// copyright-holders:Bavarese +/*************************************************************************** + Action Replay for DOS (DATEL UK 1994/1995). + Requires 386/486 and DOS 3.2 or above (no Windows). + + Hardware details: + - 6 DIP switches define hardware address and i/o port, + another 6 define the IRQ + DIP switches should only be altered when PC is off. No Plug & Play. + + - 2 x UM 6264 (= two 8 K x 8 CMOS SRAM chips) + - 1 x AMD 29f010 (= 1 Mbit 128Kb x8 flash memory) + + - 4 rectangular PAL chips with port / address / glue logic + + Codes printed on PCB (compatible with flash firmware) + REF1066 + 07/11/94 + An earlier design with REF1033 dated 28/10/93 is known. +--------------------------------------------------------------------------------- + Usage: individual drivers needed for real (AREPLAY.COM) and + protected mode (PROT.EXE; incompatible with EMM386 or QEMM). + + Hard disk space _required_ for temp.files by following sections: + 1. .\AREPLAY.CFG (vital configuration file written by initial SETUP) + + 2. file viewer (even if only disk A or B are accessed) -> C:\ARVRAM.DSK + + 3. virtual memory managed by AROS, a complex but largely + undocumented operating system (in flash ROM) -> C:\ARVRAM.RAM + + 4. the possibility array of certain trainers -> C:\POSSIBIL.ITY + + If hard disk gets unvailable or isn't present, lockups occur! + + + Freezer notes: configuration changes (DOS startup files or + altered DIP switches) usually render a freezed image unusable. + + Main problem is that Areplay.com must be at a precise location given + by (scratch RAM + offset $10) = (BIOS extension address + $3010); + at least with 4.8 software release. + + To debug crashes in unfreeze, set the DEBUG DIP switch. 'Offset' is + the freezed location of Areplay.com. It must be the same as the + offset shown when the Areplay TSR is loaded into memory. + + Always keep a 1:1 copy of CONFIG.SYS, AUTOEXEC.BAT and notes about + DIPs + environment (a write protected boot disk would be ideal). + + HIMEM.SYS parameters matter. Mess 0.186 with 'ct486' liked: + device=HIMEM.SYS /MACHINE:AT /TESTMEM:ON /CPUCLOCK:ON + Second 8K BIOS chunk must not be detected as available RAM! + (check with Msd.exe supplied by DOS 6.x) + + Software caches like DoubleSpace, Smartdrive are incompatible w.freeze + because of possible hard disk corruption (a flush is done though). + *************************************************************************** + Hardware / logic + ---------------- + GAL/PAL logic is undumped. Yet well-documented C-64 * Replay + cartridges give an idea of some basic principles (similar + ROM/RAM banking, flip-flops). + + There is no NMI on the ISA card and no attempt was made to + hide the card from evil minded programmers. + + A constant Irq heartbeat is generated after the card is + correctly initialized (when the Areplay.com driver loaded). + + Cyclic led flashes (on the freezer extension outside the PC case) + indicate activity. + + Byte watch, slow motion, key polling and most other functions are + implemented in software (via interrupt hooks). + + Bit 3 in the single read register indicates if the interrupt came + from the card or originated elsewhere. The bit is used to + decide whether to invoke Action Replay - or skip functionality + altogether. + Assumption: reset upon each read and set by external logic. + + The BIOS extension segment comprises 16K from C8000 on: + ------------- ROM location set by DIP switches -------------------- + 8 K ROM segment (determined by write to port 1, usually $0280) + + 4 K RAM bank (1-1F; determined by write to port 2, usually $0281) + + 4 K scratch RAM (always present, never banked; for vital variables) + ------------------------------------------------------------------- + + + Firmware revisions + ------------------ + First revision had a non-upgradeable ROM <= V4.0 (undumped). + + Newer hardware was equipped with AM29010 EEPROM (*) + + Firmware (say V48ROM.ROM with V4 switch) is loaded and banked + in before boot - just like any other BIOS extension. + + Datel drivers refuse to load if the BIOS area chosen is empty + or the API level of driver and internal (AR)OS do not match. + + As a rule of thumb, versions 4.1 - 4.8 of the TSR run on V4 hardware + + Driver revisions known are + 4.1 (outdated; described in German manual, about 1994), + 4.8 (widely available, ca. 1995) and 5.0 (when released?) + + Before distribution via mailbox, flash files (labeled VxxROM.ROM) + were obfuscated by adding 4,3,2,1 to byte 0,1,2,3. + + This driver accepts encrypted or unencrypted VxxROM.ROM files. + + TODO: + (*) unblock flash upgrades, currently intelflash doesn't cooperate + + - XMS / EMS memory not really tested + + - compatibility with * official * 5.0 binaries (if they exist) + + - improve banking; add missing / secret bits. + Bit 7 in read register seems to be added in later V5 hardware. + + No 'magic bits' to disable ROM/RAM or cart were found. Bit 5 + in first write register is set when freezer menu is active. +*********************************************************************************/ +#include "emu.h" +#include "areplay.h" + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** +DEFINE_DEVICE_TYPE(ISA8_AREPLAY, isa8_areplay_device, "areplay", "Action Replay for DOS - DATEL UK 1994") + +//------------------------------------------------- +// ROM( action replay ) V4.8 or V5.0 +//------------------------------------------------- + +// - official V4.8 firmware = CRC(6c4a64ce) +// - encoded V5.0 beta firmware = CRC(0ff4d5ef) +ROM_START( areplay ) + ROM_REGION(0x20000,"v48rom.rom",0) + ROM_LOAD( "v48rom.rom", 0x0000, 0x20000, CRC(6c4a64ce) SHA1(fac68327a4c8b3cb03def2a69e5930c845a09047) ) + + ROM_REGION(0x40000,"v50rom.rom",0) + ROM_LOAD( "v50rom.rom", 0x0000, 0x20000, CRC(0ff4d5ef) SHA1(eb4cd4f71ebf6d604b8e7924141389585b92ff5e) ) +ROM_END + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- +const tiny_rom_entry *isa8_areplay_device::device_rom_region() const +{ + return ROM_NAME( areplay ); +} + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- +MACHINE_CONFIG_START(isa8_areplay_device::device_add_mconfig) + MCFG_TIMER_DRIVER_ADD_PERIODIC("heartbeat", isa8_areplay_device, heartbeat_timer, attotime::from_hz(18)) +MACHINE_CONFIG_END +//************************************************************************** + +//------------------------------------------------- +// isa8_areplay_device - constructor +//------------------------------------------------- +isa8_areplay_device::isa8_areplay_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, ISA8_AREPLAY, tag, owner, clock), + device_isa8_card_interface(mconfig, *this) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- +void isa8_areplay_device::device_start() +{ + set_isa_device(); + + one_shot_timer = timer_alloc(0); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- +void isa8_areplay_device::device_reset() +{ + uint32_t bank_start; + uint32_t bank_end; + + uint8_t *dest; + uint16_t io_port_end; + + m_write_port1 = 0; // bit 5 set = no IRQs --------- correct? + + m_timer_fired = true; + force_irq_to(false); // side effect on global variable: m_is_heartbeat_present = false + + m_current_irq_selected = 5; + + m_current_rom_page = 0; + m_current_RAM_PAGE = INITIAL_RAM_BANK_NO; + + int rom = ioport("ROM_ADDRESS")->read(); + switch(rom) + { + default: + m_current_rom_start = 0xc8000 + (rom * 0x4000); + break; + + case 3: + m_current_rom_start = 0xd2000; + break; + + case 4: + case 5: + case 6: + m_current_rom_start = 0xd4000 + ( (rom - 4) * 0x4000); + break; + } + + int port = ioport("PORT")->read(); + + uint16_t io_port = 0x280; // default + if( (port >= 0) && (port <= 3) ) + io_port = 0x280 + (port * 0x10); + + int i_no = ioport("IRQ")->read(); + if( (i_no >= 2) && (i_no <= 7) ) + m_current_irq_selected = i_no; + + // Unmap all 16K (8 K + two independent RAM banks a 4 K): + m_isa->unmap_bank(m_current_rom_start, m_current_rom_start + SIZE_ROM_WINDOW + (SIZE_RAM_BANK * 2) - 1 ); + + char str[40]; + sprintf(str, "v%drom.rom", 48 + (2 * ioport("COMPATIBILITY")->read()) ); // load "v48rom.rom" + + m_isa->install_rom(this, m_current_rom_start, m_current_rom_start + SIZE_ROM_WINDOW - 1, str, str); + + memcpy(&m_banked_flash[0], memregion(str)->base(), sizeof(m_banked_flash)); // copy to memory. + + if(m_banked_flash[0] != 0x55) // if blob is unbootable, decode and keep copy in banked_flash - + { + uint8_t table[4]; + table[0]=4; table[1]=3; table[2]=2; table[3]=1; + + for(uint32_t i = 0; i < sizeof(m_banked_flash); i++) + m_banked_flash[i] -= table[i & 3]; + } + + dest = machine().root_device().memregion("isa")->base() + m_current_rom_start - 0xc0000; + memcpy(dest, &m_banked_flash[0], SIZE_ROM_WINDOW); // place bootable BIOS extension (8 K) + + // RAM range: install (4 K chunks) + bank_start = m_current_rom_start + SIZE_ROM_WINDOW; + bank_end = bank_start + SIZE_RAM_BANK -1; + + m_isa->install_bank(bank_start, + bank_end, "banked_ram", &m_board_ram[INITIAL_RAM_BANK_NO * SIZE_RAM_BANK]); + + // Scratch RAM has a seperate space never banked out. Reserve an extra space - + bank_start = bank_end + 1; + bank_end = bank_start + SIZE_RAM_BANK -1; + + m_isa->install_bank(bank_start, + bank_end , "banked_ram", &m_board_ram[SCRATCH_RAM_BANK_NO * SIZE_RAM_BANK]); + + // Protect entire bank to deter Himem.sys from detecting free memory - + m_isa->writeprotect_bank(bank_start, bank_end); + + // If only address 0-3 is covered and port changes occur, Unfreeze hangs (one cause of several) + if( ioport("DEBUG")->read()) // [DEBUG]: cover complete I-O range from 0280 - 02b3. + { + io_port = 0x280; + io_port_end = io_port + 0x33; + } + else + { // cover only offset 0 - 3 (+ io_port) + io_port_end = io_port + 3; + } + m_isa->install_device(io_port, io_port_end, read8_delegate(FUNC(isa8_areplay_device::ar_read), this), write8_delegate(FUNC(isa8_areplay_device::ar_write), this)); + + if(dest[0]==0x55) // if 'bootable' signature present... + { + if ( (ioport("FREEZE_BUTTON")->read() > 0) || (ioport("SLOMO_SWITCH")->read() > 0) ) + popmessage("FREEZE and SLOMO should be disabled before startup!\n[Action Replay]"); + else + popmessage("Action Replay %1.1f from %x to %x / PORT %x - %x/ IRQ %x", 4.8f + (0.2f * ioport("COMPATIBILITY")->read()), + m_current_rom_start, bank_end, + io_port, io_port_end, + m_current_irq_selected + ); + } +} + + +// ----------- ONE READ REGISTER (bits must match when probing for ports. Expected value of bit 7 varies with firmware) +// bit 0 : (unkown). Must be 1 at startup +// bit 1 : FREEZE BUTTON (if bit is 0 then 1 is poked to scratch RAM offset + 0xD0 on firmware version 5) * active low * +// bit 2 : (unkown). Must be 0 at startup +// bit 3 : ACTION_REPLAY_IRQ. Must be 0 at startup! * active high * +// bit 4 : (unknown). Must be 1 at startup +// bit 5 : (unknown). Must be 0 at startup +// bit 6 : SLOW MOTION SWITCH. Must be 0 at startup * active high * +// bit 7 : (unknown). Must be 1 at startup of V5.0 firmware and 0 at startup of V4.8. +READ8_MEMBER(isa8_areplay_device::ar_read) +{ + offset = offset & 1; + + int data = 1 | 16; // unknown bits 0 and 4 must be 1 at all times + + if ( ioport("FREEZE_BUTTON")->read() == 0 ) // * active low * [bit 1] + data |= FREEZE_BUTTON_BIT; + + if (m_is_heartbeat_present) // * active high * [bit 3] + { + force_irq_to(false); // sets 'm_is_heartbeat_present' to false + data |= ACTION_REPLAY_IRQ; // status bit needed for sensing of IRQ (1 = AR activity) + } + + if (ioport("SLOMO_SWITCH")->read() > 0) // ? active high ? [bit 6] 0x40 + data |= SLOMO_SWITCH_BIT; + + if (ioport("COMPATIBILITY")->read() > 0) + data |= 0x80; // V5.0 'port sensing' appears to need it + + return data; +} + +// ----------- TWO WRITE EGISTERS : + +// --- WRITE PORT #1: [ROM] bank select (always in 8 K chunks; $1FFF). +// Used to communicate with the AM29010 flash chip (access patterns: see datasheet) + +// bit 0 : (set to 1 by flash routine, else 0) WRITE ENABLE ? +// bit 1 : (unknown); leftover bits from address? Usually 0. + +// bit 2 : A14 [ bits 2 - 4 select one 8 K bank in 128 K ROM space ] +// bit 3 : A15 [ bits 2 - 4 select one 8 K bank in 128 K ROM space ] +// bit 4 : A16 [ bits 2 - 4 select one 8 K bank in 128 K ROM space ] +// bit 5 : A17. Usually 0. Sense AM29010 flash 'reset' (0xf0) ? + +// bit 6 : (unknown); leftover bits from address? Usually 0. +// bit 7 : LED (bit inverted by firmware when Action Replay is ready) + +// -------------------------------------------------------------------------------- +// --- WRITE PORT #2: [RAM] bank select + +// bits 0...4 : [RAM] bank select (5 bits address 1 out of 32) pages of 4 K size. +// bits 5 + 6 : (unknown) +// Bit 7 : INTERRUPT ENABLE * active high *. Needed for IRQ sensing (pokes data 0x00, then 0x80) +// Bit 7 = 0 : "disable interrupts" +// Bit 7 = 1 : "enable interrupts " + +WRITE8_MEMBER(isa8_areplay_device::ar_write) +{ + uint32_t bank_start; + uint32_t bank_end; + + static bool old_irq_state; + static bool old_led_state; + + static uint16_t old_tsr_start, old_bank, old_port; + uint16_t tsr_start, bank, port; + + // Debug freezes which crash the host (usually because start addresses no longer match): + if( ioport("DEBUG")->read()) + { + uint8_t *scratch = machine().root_device().memregion("isa")->base() + m_current_rom_start - 0xc0000 + 3 * SIZE_RAM_BANK; + memcpy(&m_post_mortem_ram[0], scratch, 0x20); // make post mortem dump + + tsr_start= m_post_mortem_ram[0x12] | (m_post_mortem_ram[0x13] << 8); + bank = m_post_mortem_ram[7]; + port = m_post_mortem_ram[0] | (m_post_mortem_ram[1] << 8); // locations valid for 4.8 + + if( (bank != old_bank ) || + (tsr_start != old_tsr_start) || + (port != old_port) + ) + { + if(tsr_start > 0) + popmessage("Tsr start address %x\n[Action Replay]",tsr_start); + + if( (old_tsr_start > 0) && + (old_bank > 0) && + (old_port > 0) + ) + { + popmessage("Change detected: OFFSET: %x -> %x\nROM BANK: %x -> %x...\nPORT %x -> %x\n[Action Replay]", + old_tsr_start, tsr_start, + old_bank, bank, + old_port, port + ); + } + + old_tsr_start = tsr_start; + old_bank = bank; + old_port = port; + } + } + + offset = offset & 1; + + switch(offset) + { + // **************** + // WRITE PORT #1 + // **************** + case 0: // (EEP)ROM bank select ( 1FFF = 8 K chunks). Range: 0...15 (allows 128 K) + + m_write_port1 = data; + + bank_end = m_current_rom_start + SIZE_ROM_WINDOW - 1; + if(data & WRITE_ENABLE_BIT) // detect write to EEPROM area + { + m_isa->unmap_bank(m_current_rom_start, bank_end); + m_isa->install_bank(m_current_rom_start, bank_end, + "banked_flash", &m_banked_flash[ 16 * SIZE_ROM_WINDOW ] + ); + + if(data == 0x01 ) // attempt to query manufacturer ID ? + { + popmessage("EEPROM updates are unemulated!\n[Action Replay]"); + m_isa->install_bank(m_current_rom_start, bank_end, + "banked_flash", &m_banked_flash[ m_current_rom_page * SIZE_ROM_WINDOW ] + ); + } + } + else + { // ROM PAGE 0 * IS * USED. MSB BITS (11000011) ARE ALWAYS ZERO: + m_current_rom_page = (data & 0x3c) >> 2; // bits 2...5 + + m_isa->unmap_bank(m_current_rom_start, bank_end); + m_isa->install_bank(m_current_rom_start, bank_end, + "banked_flash", &m_banked_flash[ m_current_rom_page * SIZE_ROM_WINDOW ] + ); + + m_isa->writeprotect_bank(m_current_rom_start, bank_end); // *** write protect ROM bank **** + } + + if( ((data & LED_ENABLE_BIT) > 0) && (old_led_state == false) ) + if( ioport("DEBUG")->read() != 0 ) printf(" [Action Replay LED 0 > 1] "); + + if( ((data & LED_ENABLE_BIT) == 0) && (old_led_state == true) ) + if( ioport("DEBUG")->read() != 0 ) printf(" [Action Replay LED 1 > 0] "); + + old_led_state = (data & LED_ENABLE_BIT) ? true : false; // keep position of statement + + break; // (end of) case 0 + + // **************** + // WRITE PORT #2 + // **************** + case 1: // RAM bank select ( FFF = 4K size). Range: 0...31 (128 K possible; amount probed by firmware) + + if(old_irq_state == false) // Trigger IRQ only if 0 => 1 transition + { + if( ((data & PORT2_IRQ_ENABLE) > 0 ) ) + { + if(m_timer_fired == true) + { + if(m_is_heartbeat_present == false) + force_irq_to(true); + } + } + } // old irq state was ZERO. + + if( (data & PORT2_IRQ_ENABLE) == 0 ) // driver reactivates stuck IRQ by forcing the bit to 0, then 1 + force_irq_to(false); + + old_irq_state = (data & PORT2_IRQ_ENABLE) ? true : false; // keep position of statement + + bank_start = m_current_rom_start + SIZE_ROM_WINDOW; + bank_end = bank_start + SIZE_RAM_BANK - 1; + + m_isa->unmap_bank(bank_start, bank_end); + + m_current_RAM_PAGE = data & 0x1f; // Bits 0..4 + + m_isa->install_bank(bank_start, bank_end , + "banked_ram", &m_board_ram[ SIZE_RAM_BANK * m_current_RAM_PAGE ] + ); + break; // (end of) case 1 : RAM bank + } +} + +void isa8_areplay_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ + switch (tid) + { + default: + +if( ioport("DEBUG")->read() != 0 ) + printf("\nONE SHOT **** stopped ****!"); + m_timer_fired = true; + + force_irq_to(false); + break; + + } // switch (timer ID) +} + + +// Subroutine +// => side effect on global variable: m_is_heartbeat_present = flag; + +void isa8_areplay_device::force_irq_to(bool flag) +{ + m_is_heartbeat_present = flag; // flag indicates the IRQ is generated by Action Replay + + if(flag == false) + one_shot_timer->adjust(attotime::never); + else + one_shot_timer->adjust(attotime::from_msec(ONE_SHOT_TIMER_DELAY_MS)); + raise_processor_interrupt(m_current_irq_selected, flag); +} + +TIMER_DEVICE_CALLBACK_MEMBER(isa8_areplay_device::heartbeat_timer) +{ + if(!m_is_heartbeat_present) + { + m_timer_fired = false; + + force_irq_to(true); // side effect: m_is_heartbeat_present = true + } +} + +void isa8_areplay_device::raise_processor_interrupt(int ref, bool state) +{ + switch(ref) + { + case 2: + m_isa->irq2_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + + case 3: + m_isa->irq3_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + + case 4: + m_isa->irq4_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + + case 5: + m_isa->irq5_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + + case 6: + m_isa->irq6_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + + case 7: + m_isa->irq7_w(state ? ASSERT_LINE : CLEAR_LINE); + break; + } +} + +static INPUT_PORTS_START( areplay_dsw ) + PORT_START("FREEZE_BUTTON") + PORT_DIPNAME(0x01, 0x00, "Freeze (push to activate Action Replay)") PORT_IMPULSE(4) + PORT_DIPSETTING(0x00, DEF_STR(Off)) + PORT_DIPSETTING(0x01, DEF_STR(On)) + + PORT_START("SLOMO_SWITCH") + PORT_DIPNAME(0x01, 0x00, "Slow motion switch (switch OFF at startup)") PORT_TOGGLE + PORT_DIPSETTING(0x00, DEF_STR(Off)) + PORT_DIPSETTING(0x01, DEF_STR(On)) + + PORT_START("ROM_ADDRESS") + PORT_DIPNAME( 0xff, 0x00, "16K ROM bank (disable BIOS shadowing; exclude in EMM386)") + PORT_DIPSETTING( 0, "C8000 - CBFFF (at times occupied by graphics o.HD contr.)" ) + PORT_DIPSETTING( 1, "CC000 - CFFFF" ) + PORT_DIPSETTING( 2, "D0000 - D3FFF" ) + PORT_DIPSETTING( 3, "D2000 - D5FFF" ) + PORT_DIPSETTING( 4, "D4000 - D7FFF" ) + PORT_DIPSETTING( 5, "D8000 - DBFFF" ) + PORT_DIPSETTING( 6, "DC000 - DFFFF" ) + + PORT_START("PORT") + PORT_DIPNAME( 0xff, 0x00, "I/O port") + PORT_DIPSETTING( 0, "0x280" ) + PORT_DIPSETTING( 1, "0x290" ) + PORT_DIPSETTING( 2, "0x2A0" ) + PORT_DIPSETTING( 3, "0x2B0" ) + + PORT_START("IRQ") + PORT_DIPNAME( 0xff, 5, "Interrupt number") + PORT_DIPSETTING( 2, "2 (sometimes usable; timer irq / occupied by 8259A)" ) + PORT_DIPSETTING( 3, "3 (free if not occupied by COM-2 or COM-4)" ) + PORT_DIPSETTING( 4, "4 (do not use if COM-1 or COM-3 present)" ) + PORT_DIPSETTING( 5, "5 (recommended; usually LPT-2)" ) + PORT_DIPSETTING( 6, "6 (do not use if FDC controller present)" ) + PORT_DIPSETTING( 7, "7 (recommended; usually LPT-1)" ) + + PORT_START("COMPATIBILITY") + PORT_DIPNAME(0xff, 0x00, "Hardware compatibility") PORT_TOGGLE + PORT_DIPSETTING( 0, "4.8 (AMD flash)" ) + PORT_DIPSETTING( 1, "5.0 (AMD flash w.different I/O)" ) + + PORT_START("DEBUG") + PORT_DIPNAME(0xff, 0x00, "* DEBUGGING ON *") PORT_TOGGLE + PORT_DIPSETTING(0x00, DEF_STR(Off)) + PORT_DIPSETTING(0x01, DEF_STR(On)) +INPUT_PORTS_END + +//------------------------------------------------- +// isa8_areplay_device - constructor +//------------------------------------------------- +ioport_constructor isa8_areplay_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( areplay_dsw ); +} |