diff options
Diffstat (limited to 'src/devices/machine/6883sam.cpp')
-rw-r--r-- | src/devices/machine/6883sam.cpp | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/src/devices/machine/6883sam.cpp b/src/devices/machine/6883sam.cpp new file mode 100644 index 00000000000..13e5dec2dd5 --- /dev/null +++ b/src/devices/machine/6883sam.cpp @@ -0,0 +1,491 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + 6883sam.cpp + + 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 "emu.h" +#include "6883sam.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +#define LOG_FBITS (1U << 1) +#define LOG_VBITS (1U << 2) +#define LOG_PBITS (1U << 3) +#define LOG_TBITS (1U << 4) +#define LOG_MBITS (1U << 5) +#define LOG_RBITS (1U << 6) + +#define VERBOSE (0) +// #define VERBOSE (LOG_FBITS) +// #define VERBOSE (LOG_FBITS | LOG_VBITS | LOG_PBITS | LOG_TBITS | LOG_MBITS | LOG_RBITS) + +#include "logmacro.h" + +#define LOGFBITS(...) LOGMASKED(LOG_FBITS, __VA_ARGS__) +#define LOGVBITS(...) LOGMASKED(LOG_VBITS, __VA_ARGS__) +#define LOGPBITS(...) LOGMASKED(LOG_PBITS, __VA_ARGS__) +#define LOGTBITS(...) LOGMASKED(LOG_TBITS, __VA_ARGS__) +#define LOGMBITS(...) LOGMASKED(LOG_MBITS, __VA_ARGS__) +#define LOGRBITS(...) LOGMASKED(LOG_RBITS, __VA_ARGS__) + +DEFINE_DEVICE_TYPE(SAM6883, sam6883_device, "sam6883", "MC6883 SAM") + + + +//************************************************************************** +// DEVICE SETUP +//************************************************************************** + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +sam6883_device::sam6883_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SAM6883, tag, owner, clock) + , device_memory_interface(mconfig, *this) + , sam6883_friend_device_interface(mconfig, *this, 4) + , m_ram_config("ram", ENDIANNESS_BIG, 8, 16, 0) + , m_rom0_config("rom0", ENDIANNESS_BIG, 8, 13, 0) + , m_rom1_config("rom1", ENDIANNESS_BIG, 8, 13, 0) + , m_rom2_config("rom2", ENDIANNESS_BIG, 8, 14, 0) + , m_io0_config("io0", ENDIANNESS_BIG, 8, 5, 0) + , m_io1_config("io1", ENDIANNESS_BIG, 8, 5, 0) + , m_io2_config("io2", ENDIANNESS_BIG, 8, 5, 0) + , m_boot_config("boot", ENDIANNESS_BIG, 8, 7, 0) +{ +} + +sam6883_friend_device_interface::sam6883_friend_device_interface(const machine_config &mconfig, device_t &device, int divider) + : device_interface(device, "sam6883") + , m_cpu(device, finder_base::DUMMY_TAG) + , m_sam_state(0x0000) + , m_divider(divider) +{ +} + + +//------------------------------------------------- +// memory_space_config - return the configuration +// for the address spaces +//------------------------------------------------- + +device_memory_interface::space_config_vector sam6883_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(0, &m_ram_config), + std::make_pair(1, &m_rom0_config), + std::make_pair(2, &m_rom1_config), + std::make_pair(3, &m_rom2_config), + std::make_pair(4, &m_io0_config), + std::make_pair(5, &m_io1_config), + std::make_pair(6, &m_io2_config), + std::make_pair(7, &m_boot_config) + }; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sam6883_device::device_start() +{ + // get spaces + space(0).cache(m_ram_space); + for (int i = 0; i < 3; i++) + space(i + 1).cache(m_rom_space[i]); + for (int i = 0; i < 3; i++) + space(i + 4).specific(m_io_space[i]); + space(7).cache(m_boot_space); + + // save state support + save_item(NAME(m_sam_state)); + save_item(NAME(m_divider)); + save_item(NAME(m_counter_mask)); + save_item(NAME(m_counter)); + save_item(NAME(m_counter_xdiv)); + save_item(NAME(m_counter_ydiv)); +} + + +//------------------------------------------------- +// read - read from one of the eight spaces +//------------------------------------------------- + +uint8_t sam6883_device::read(offs_t offset) +{ + bool mode_64k = (m_sam_state & SAM_STATE_M1) == SAM_STATE_M1; + if (offset < (mode_64k && (m_sam_state & SAM_STATE_TY) ? 0xff00 : 0x8000)) + { + // RAM reads: 0000–7FFF or 0000–FEFF + if (mode_64k && (m_sam_state & (SAM_STATE_TY | SAM_STATE_P1)) == SAM_STATE_P1) + offset |= 0x8000; + return m_ram_space.read_byte(offset); + } + else if (offset < 0xc000 || offset >= 0xffe0) + { + // ROM spaces: 8000–9FFF and A000–BFFF + FFE0–FFFF + return m_rom_space[BIT(offset, 13)].read_byte(offset & 0x1fff); + } + else if (offset < 0xff00) + { + // ROM2 space: C000–FEFF + return m_rom_space[2].read_byte(offset & 0x3fff); + } + else if (offset < 0xff60) + { + // I/O spaces: FF00–FF1F (slow), FF20–FF3F, FF40–FF5F + return m_io_space[BIT(offset, 5, 2)].read_byte(offset & 0x1f); + } + else + { + // FF60–FFDF + return m_boot_space.read_byte(offset - 0xff60); + } +} + + +//------------------------------------------------- +// write - write to RAM, I/O or internal register +//------------------------------------------------- + +void sam6883_device::write(offs_t offset, uint8_t data) +{ + bool mode_64k = (m_sam_state & SAM_STATE_M1) == SAM_STATE_M1; + if (offset < 0x8000) + { + // RAM write space: 0000–7FFF (nominally space 7) + if (mode_64k && (m_sam_state & (SAM_STATE_TY | SAM_STATE_P1)) == SAM_STATE_P1) + offset |= 0x8000; + m_ram_space.write_byte(offset, data); + } + else if (offset < 0xc000 || offset >= 0xffe0) + { + // ROM spaces: 8000–9FFF and A000–BFFF + FFE0–FFFF (may write through to RAM) + if (offset < 0xc000 && mode_64k && (m_sam_state & SAM_STATE_TY)) + m_ram_space.write_byte(offset, data); + m_rom_space[BIT(offset, 13)].write_byte(offset & 0x1fff, data); + } + else if (offset < 0xff00) + { + // ROM2 space: C000–FEFF (may write through to RAM) + if (mode_64k && (m_sam_state & SAM_STATE_TY)) + m_ram_space.write_byte(offset, data); + m_rom_space[2].write_byte(offset & 0x3fff, data); + } + else if (offset < 0xff60) + { + // I/O spaces: FF00–FF1F (slow), FF20–FF3F, FF40–FF5F + m_io_space[BIT(offset, 5, 2)].write_byte(offset & 0x1f, data); + } + else + { + // FF60–FFDF + m_boot_space.write_byte(offset - 0xff60, data); + if (offset >= 0xffc0) + internal_write(offset & 0x1f, data); + } +} + + +//------------------------------------------------- +// 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() +{ + update_memory(); + update_cpu_clock(); +} + + + +//------------------------------------------------- +// update_memory +//------------------------------------------------- + +void sam6883_device::update_memory() +{ + // 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 + + // switch depending on the M1/M0 variables + switch(m_sam_state & (SAM_STATE_M1|SAM_STATE_M0)) + { + case 0: + // 4K mode + m_counter_mask = 0x0FFF; + break; + + case SAM_STATE_M0: + // 16K mode + m_counter_mask = 0x3FFF; + break; + + case SAM_STATE_M1: + // 64k mode (dynamic) + case SAM_STATE_M1|SAM_STATE_M0: + // 64k mode (static) + // full 64k RAM or ROM/RAM + // CoCo Max requires these two be treated the same + m_counter_mask = 0xfFFF; + break; + } +} + + + +//------------------------------------------------- +// update_cpu_clock - adjusts the speed of the CPU +// clock +//------------------------------------------------- + +void sam6883_friend_device_interface::update_cpu_clock() +{ + // 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; + m_cpu->owner()->set_unscaled_clock(device().clock() / (m_divider * (speed ? 2 : 4))); +} + + + +//------------------------------------------------- +// internal_write +//------------------------------------------------- + +void sam6883_device::internal_write(offs_t offset, uint8_t data) +{ + // data is ignored + (void)data; + + // alter the SAM state + uint16_t 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(); + + if (xorval & (SAM_STATE_F6|SAM_STATE_F5|SAM_STATE_F4|SAM_STATE_F3|SAM_STATE_F2|SAM_STATE_F1|SAM_STATE_F0)) + { + LOGFBITS("%s: SAM F Address: $%04x\n", + machine().describe_context(), + display_offset()); + } + + if (xorval & (SAM_STATE_V0|SAM_STATE_V1|SAM_STATE_V2)) + { + LOGVBITS("%s: SAM V Bits: $%02x\n", + machine().describe_context(), + (m_sam_state & (SAM_STATE_V0|SAM_STATE_V1|SAM_STATE_V2))); + } + + if (xorval & (SAM_STATE_P1)) + { + LOGPBITS("%s: SAM P1 Bit: $%02x\n", + machine().describe_context(), + (m_sam_state & (SAM_STATE_P1)) >> 10); + } + + if (xorval & (SAM_STATE_TY)) + { + LOGTBITS("%s: SAM TY Bit: $%02x\n", + machine().describe_context(), + (m_sam_state & (SAM_STATE_TY)) >> 15); + } + + if (xorval & (SAM_STATE_M0|SAM_STATE_M1)) + { + LOGMBITS("%s: SAM M Bits: $%02x\n", + machine().describe_context(), + (m_sam_state & (SAM_STATE_M0|SAM_STATE_M1)) >> 13); + } + + if (xorval & (SAM_STATE_R0|SAM_STATE_R1)) + { + LOGRBITS("%s: SAM R Bits: $%02x\n", + machine().describe_context(), + (m_sam_state & (SAM_STATE_R0|SAM_STATE_R1)) >> 11); + } +} + + + +//------------------------------------------------- +// horizontal_sync +//------------------------------------------------- + +void sam6883_device::horizontal_sync() +{ + 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 +//------------------------------------------------- + +void sam6883_device::hs_w(int state) +{ + if (state) + { + horizontal_sync(); + } +} |