diff options
Diffstat (limited to 'src/mame/machine/6883sam.c')
-rw-r--r-- | src/mame/machine/6883sam.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/src/mame/machine/6883sam.c b/src/mame/machine/6883sam.c new file mode 100644 index 00000000000..cb5d0685fba --- /dev/null +++ b/src/mame/machine/6883sam.c @@ -0,0 +1,557 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + 6883sam.c + + Motorola 6883 Synchronous Address Multiplexer + + The Motorola 6883 SAM has 16 bits worth of state, but the state is changed + by writing into a 32 byte address space; odd bytes set bits and even bytes + clear bits. Here is the layout: + + 31 Set TY Map Type 0: RAM/ROM 1: All RAM + 30 Clear TY Map Type + 29 Set M1 Memory Size 00: 4K 10: 64K Dynamic + 28 Clear M1 Memory Size 01: 16K 11: 64K Static + 27 Set M0 Memory Size + 26 Clear M0 Memory Size + 25 Set R1 MPU Rate 00: Slow 10: Fast + 24 Clear R1 MPU Rate 01: Dual 11: Fast + 23 Set R0 MPU Rate + 22 Clear R0 MPU Rate + 21 Set P1 Page #1 0: Low 1: High + 20 Clear P1 Page #1 + 19 Set F6 Display Offset + 18 Clear F6 Display Offset + 17 Set F5 Display Offset + 16 Clear F5 Display Offset + 15 Set F4 Display Offset + 14 Clear F4 Display Offset + 13 Set F3 Display Offset + 12 Clear F3 Display Offset + 11 Set F2 Display Offset + 10 Clear F2 Display Offset + 9 Set F1 Display Offset + 8 Clear F1 Display Offset + 7 Set F0 Display Offset + 6 Clear F0 Display Offset + 5 Set V2 VDG Mode + 4 Clear V2 VDG Mode + 3 Set V1 VDG Mode + 2 Clear V1 VDG Mode + 1 Set V0 VDG Mode + 0 Clear V0 VDG Mode + + All parts of the SAM are fully emulated except R1/R0 (the changes in the + MPU rate are approximated) and M1/M0 + +***************************************************************************/ + + +#include "machine/6883sam.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +#define LOG_SAM 0 + +const device_type SAM6883 = &device_creator<sam6883_device>; + + + +//************************************************************************** +// DEVICE SETUP +//************************************************************************** + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +sam6883_device::sam6883_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SAM6883, "SAM6883", tag, owner, clock, "sam6883", __FILE__), + m_cpu_tag(NULL), + m_cpu_space_ref(AS_PROGRAM), + m_read_res(*this), + m_space_0000(*this), + m_space_8000(*this), + m_space_A000(*this), + m_space_C000(*this), + m_space_FF00(*this), + m_space_FF20(*this), + m_space_FF40(*this), + m_space_FF60(*this), + m_space_FFE0(*this) +{ + memset(m_banks, '\0', sizeof(m_banks)); +} + + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sam6883_device::device_start() +{ + // find the CPU + m_cpu = machine().device<cpu_device>(m_cpu_tag); + m_cpu_space = &m_cpu->space(m_cpu_space_ref); + + // resolve callbacks + m_read_res.resolve_safe(0); + + // install SAM handlers + m_cpu_space->install_read_handler(0xFFC0, 0xFFDF, 0, 0, read8_delegate(FUNC(sam6883_device::read), this)); + m_cpu_space->install_write_handler(0xFFC0, 0xFFDF, 0, 0, write8_delegate(FUNC(sam6883_device::write), this)); + + // save state support + save_item(NAME(m_sam_state)); + save_item(NAME(m_counter)); + save_item(NAME(m_counter_xdiv)); + save_item(NAME(m_counter_ydiv)); +} + + + +//------------------------------------------------- +// configure_bank - bank configuration +//------------------------------------------------- + +void sam6883_device::configure_bank(int bank, UINT8 *memory, UINT32 memory_size, bool is_read_only) +{ + configure_bank(bank, memory, memory_size, is_read_only, read8_delegate(), write8_delegate()); +} + + + +//------------------------------------------------- +// configure_bank - bank configuration +//------------------------------------------------- + +void sam6883_device::configure_bank(int bank, read8_delegate rhandler, write8_delegate whandler) +{ + configure_bank(bank, NULL, 0, false, rhandler, whandler); +} + + + +//------------------------------------------------- +// configure_bank - bank configuration +//------------------------------------------------- + +void sam6883_device::configure_bank(int bank, UINT8 *memory, UINT32 memory_size, bool is_read_only, read8_delegate rhandler, write8_delegate whandler) +{ + assert((bank >= 0) && (bank < ARRAY_LENGTH(m_banks))); + m_banks[bank].m_memory = memory; + m_banks[bank].m_memory_size = memory_size; + m_banks[bank].m_memory_read_only = is_read_only; + m_banks[bank].m_rhandler = rhandler; + m_banks[bank].m_whandler = whandler; + + /* if we're configuring a bank that never changes, update it now */ + switch(bank) + { + case 4: + m_space_FF00.point(&m_banks[4], 0x0000, 0); + break; + case 5: + m_space_FF20.point(&m_banks[5], 0x0000, 0); + break; + case 6: + m_space_FF40.point(&m_banks[6], 0x0000, 0); + break; + case 7: + m_space_FF60.point(&m_banks[7], 0x0000, 0); + break; + case 2: + m_space_FFE0.point(&m_banks[2], 0x1FE0, 0); + break; + } +} + + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void sam6883_device::device_reset() +{ + m_counter = 0; + m_counter_xdiv = 0; + m_counter_ydiv = 0; + m_sam_state = 0x0000; + update_state(); +} + + + +//------------------------------------------------- +// device_post_load - device-specific post load +//------------------------------------------------- + +void sam6883_device::device_post_load() +{ + device_t::device_post_load(); + update_state(); +} + + + +//------------------------------------------------- +// update_state +//------------------------------------------------- + +void sam6883_device::update_state(void) +{ + update_memory(); + update_cpu_clock(); +} + + + +//------------------------------------------------- +// update_memory +//------------------------------------------------- + +void sam6883_device::update_memory(void) +{ + // Memory size - allowed restricting memory accesses to something less than + // 32k + // + // This was a SAM switch that occupied 4 addresses: + // + // $FFDD (set) R1 + // $FFDC (clear) R1 + // $FFDB (set) R0 + // $FFDA (clear) R0 + // + // R1:R0 formed the following states: + // 00 - 4k + // 01 - 16k + // 10 - 64k + // 11 - static RAM (??) + // + // If something less than 64k was set, the low RAM would be smaller and + // mirror the other parts of the RAM + // + // TODO: Find out what "static RAM" is + // TODO: This should affect _all_ memory accesses, not just video ram + // TODO: Verify that the CoCo 3 ignored this + + // for now, lets assume that the caller is nice and made m_memory_size + // a power of two + UINT16 ram_mask = (UINT16) m_banks[0].m_memory_size - 1; + + // switch depending on the M1/M0 variables + bool setup_rom = true; + switch(m_sam_state & (SAM_STATE_M1|SAM_STATE_M0)) + { + case 0: + // 4K mode + m_space_0000.point(&m_banks[0], 0x0000, ram_mask); + m_counter_mask = 0x0FFF; + m_counter_or = 0x0000; + break; + + case SAM_STATE_M0: + // 16K mode + m_space_0000.point(&m_banks[0], 0x0000, ram_mask); + m_counter_mask = 0x3FFF; + m_counter_or = 0x0000; + break; + + case SAM_STATE_M1: + case SAM_STATE_M1|SAM_STATE_M0: + // 64k mode + if (m_sam_state & SAM_STATE_TY) + { + // full 64k RAM + m_space_0000.point(&m_banks[0], 0x0000, ram_mask); + m_space_8000.point(&m_banks[0], 0x8000, 0); + m_space_A000.point(&m_banks[0], 0xA000, 0); + m_space_C000.point(&m_banks[0], 0xC000, 0); + m_counter_mask = 0xFFFF; + m_counter_or = 0x0000; + setup_rom = false; + } + else + { + // ROM/RAM + UINT16 ram_base = (m_sam_state & SAM_STATE_P1) ? 0x8000 : 0x0000; + m_space_0000.point(&m_banks[0], ram_base, ram_mask); + m_counter_mask = 0x7FFF; + m_counter_or = ram_base; + } + break; + } + + if (setup_rom) + { + m_space_8000.point(&m_banks[1], m_banks[1].m_memory_offset, 0); + m_space_A000.point(&m_banks[2], m_banks[2].m_memory_offset, 0); + m_space_C000.point(&m_banks[3], m_banks[3].m_memory_offset, 0); + } + + // update $FFE0-$FFFF + m_space_FFE0.point(&m_banks[2], m_banks[2].m_memory_offset + 0x1FE0, 0); +} + + + +//------------------------------------------------- +// update_cpu_clock - adjusts the speed of the CPU +// clock +//------------------------------------------------- + +void sam6883_friend_device::update_cpu_clock(void) +{ + // The infamous speed up poke. + // + // This was a SAM switch that occupied 4 addresses: + // + // $FFD9 (set) R1 + // $FFD8 (clear) R1 + // $FFD7 (set) R0 + // $FFD6 (clear) R0 + // + // R1:R0 formed the following states: + // 00 - slow 0.89 MHz + // 01 - dual speed ??? + // 1x - fast 1.78 MHz + // + // R1 controlled whether the video addressing was speeded up and R0 + // did the same for the CPU. On pre-CoCo 3 machines, setting R1 caused + // the screen to display garbage because the M6847 could not display + // fast enough. + // + // TODO: Make the overclock more accurate. In dual speed, ROM was a fast + // access but RAM was not. I don't know how to simulate this. + + int speed = (m_sam_state & (SAM_STATE_R1|SAM_STATE_R0)) / SAM_STATE_R0; + + // the line below is weird because we are not strictly emulating the M6809E with emphasis on the 'E' + m_cpu->set_clock_scale(speed ? 2 : 1); +} + + + +//------------------------------------------------- +// set_bank_offset +//------------------------------------------------- + +void sam6883_device::set_bank_offset(int bank, offs_t offset) +{ + if (m_banks[bank].m_memory_offset != offset) + { + m_banks[bank].m_memory_offset = offset; + update_memory(); + } +} + + + +//------------------------------------------------- +// read +//------------------------------------------------- + +READ8_MEMBER( sam6883_device::read ) +{ + return 0; +} + + + +//------------------------------------------------- +// write +//------------------------------------------------- + +WRITE8_MEMBER( sam6883_device::write ) +{ + /* alter the SAM state */ + UINT16 xorval = alter_sam_state(offset); + + /* based on the mask, apply effects */ + if (xorval & (SAM_STATE_TY|SAM_STATE_M1|SAM_STATE_M0|SAM_STATE_P1)) + update_memory(); + if (xorval & (SAM_STATE_R1|SAM_STATE_R0)) + update_cpu_clock(); +} + + + +//------------------------------------------------- +// horizontal_sync +//------------------------------------------------- + +void sam6883_device::horizontal_sync(void) +{ + bool carry; + + // When horizontal sync occurs, bits B1-B3 or B1-B4 may be cleared (except in DMA mode). The catch + // is that the SAM's counter is a chain of flip-flops. Clearing the counter can cause carries to + // occur just as they can when the counter is bumped. + // + // This is critical in getting certain semigraphics modes to work correctly. Guardian uses this + // mode (see bug #1153). Special thanks to Ciaran Anscomb and Phill Harvey-Smith for figuring this + // out + switch((m_sam_state & (SAM_STATE_V2|SAM_STATE_V1|SAM_STATE_V0)) / SAM_STATE_V0) + { + case 0x01: + case 0x03: + case 0x05: + /* these SAM modes clear bits B1-B3 */ + carry = (m_counter & 0x0008) ? true : false; + m_counter &= ~0x000F; + if (carry) + counter_carry_bit3(); + break; + + case 0x00: + case 0x02: + case 0x04: + case 0x06: + /* clear bits B1-B4 */ + carry = (m_counter & 0x0010) ? true : false; + m_counter &= ~0x001F; + if (carry) + counter_carry_bit4(); + break; + + case 0x07: + /* DMA mode - do nothing */ + break; + + default: + fatalerror("Should not get here\n"); + } +} + + + +//------------------------------------------------- +// hs_w +//------------------------------------------------- + +WRITE_LINE_MEMBER( sam6883_device::hs_w ) +{ + if (state) + { + horizontal_sync(); + } +} + + + +//------------------------------------------------- +// sam_space::ctor +//------------------------------------------------- + +template<UINT16 _addrstart, UINT16 _addrend> +sam6883_device::sam_space<_addrstart, _addrend>::sam_space(sam6883_device &owner) + : m_owner(owner) +{ + m_read_bank = NULL; + m_write_bank = NULL; + m_mask = 0; +} + + + +//------------------------------------------------- +// sam_space::cpu_space +//------------------------------------------------- + +template<UINT16 _addrstart, UINT16 _addrend> +address_space &sam6883_device::sam_space<_addrstart, _addrend>::cpu_space() const +{ + assert(m_owner.m_cpu_space != NULL); + return *m_owner.m_cpu_space; +} + + + +//------------------------------------------------- +// sam_space::point +//------------------------------------------------- + +template<UINT16 _addrstart, UINT16 _addrend> +void sam6883_device::sam_space<_addrstart, _addrend>::point(const sam_bank *bank, UINT16 offset, UINT16 mask) +{ + if (LOG_SAM) + { + logerror("sam6883_device::sam_space::point(): addrstart=0x%04X addrend=0x%04X offset=0x%04X mask=0x%04X bank->m_memory=0x%p bank->m_memory_read_only=%s\n", + (unsigned) _addrstart, + (unsigned) _addrend, + (unsigned) offset, + (unsigned) mask, + bank->m_memory, + bank->m_memory_read_only ? "true" : "false"); + } + + point_specific_bank(bank, offset, mask, m_read_bank, _addrstart, _addrend, false); + point_specific_bank(bank, offset, mask, m_write_bank, _addrstart, _addrend, true); +} + + + +//------------------------------------------------- +// sam_space::point_specific_bank +//------------------------------------------------- +template<UINT16 _addrstart, UINT16 _addrend> +void sam6883_device::sam_space<_addrstart, _addrend>::point_specific_bank(const sam_bank *bank, UINT16 offset, UINT16 mask, memory_bank *&memory_bank, INT32 addrstart, INT32 addrend, bool is_write) +{ + char buffer[16]; + + if (bank->m_memory != NULL) + { + // normalize offset + if (mask != 0) + offset &= mask; + + // this bank is a memory bank - first ensure that we have a bank + if (!memory_bank || !memory_bank->matches_exactly(addrstart, addrend) || (mask != m_mask)) + { + // name the bank + snprintf(buffer, ARRAY_LENGTH(buffer), "bank%04X_%c", addrstart, is_write ? 'w' : 'r'); + + // install it + if (is_write) + cpu_space().install_write_bank(addrstart, addrend, mask, 0, buffer); + else + cpu_space().install_read_bank(addrstart, addrend, mask, 0, buffer); + m_mask = mask; + + // and get it + memory_bank = cpu_space().device().owner()->membank(buffer); + } + + // point the bank + if (memory_bank != NULL) + { + if (is_write && bank->m_memory_read_only) + memory_bank->set_base(m_owner.m_dummy); + else + memory_bank->set_base(bank->m_memory + offset); + } + } + else + { + // this bank uses handlers + assert((offset == 0) && (mask == 0)); // changes to the offset are not supported + + if (is_write) + { + if (!bank->m_whandler.isnull()) + cpu_space().install_write_handler(addrstart, addrend, 0, 0, bank->m_whandler); + } + else + { + if (!bank->m_rhandler.isnull()) + cpu_space().install_read_handler(addrstart, addrend, 0, 0, bank->m_rhandler); + } + } +} |