From a381012159b217b4f5bf4979e0638529b434c272 Mon Sep 17 00:00:00 2001 From: arbee Date: Wed, 19 Jul 2023 20:16:24 -0400 Subject: New working systems ------------------- Apple Macintosh Centris 610 [R. Belmont] Apple Macintosh Centris 650 [R. Belmont] Apple Macintosh Quadra 610 [R. Belmont] Apple Macintosh Quadra 650 [R. Belmont] Apple Macintosh Quadra 800 [R. Belmont] apple/dafb.cpp: Added a child device to handle the differences in the djMEMC integrated version of DAFB. [R. Belmont] apple/iosb.cpp: Apple I/O Subsystem Buffer device, which handles I/O for second-generation 68040 machines. [R. Belmont] apple/djmemc.cpp: Apple memory controller with integrated DAFB video for second-generation 68040 systems. [R. Belmont] --- src/mame/apple/dafb.cpp | 210 ++++++++++++-- src/mame/apple/dafb.h | 49 +++- src/mame/apple/djmemc.cpp | 112 +++++++ src/mame/apple/djmemc.h | 58 ++++ src/mame/apple/iosb.cpp | 626 ++++++++++++++++++++++++++++++++++++++++ src/mame/apple/iosb.h | 128 ++++++++ src/mame/apple/macquadra800.cpp | 306 ++++++++++++++++++++ src/mame/mame.lst | 7 + 8 files changed, 1457 insertions(+), 39 deletions(-) create mode 100644 src/mame/apple/djmemc.cpp create mode 100644 src/mame/apple/djmemc.h create mode 100644 src/mame/apple/iosb.cpp create mode 100644 src/mame/apple/iosb.h create mode 100644 src/mame/apple/macquadra800.cpp diff --git a/src/mame/apple/dafb.cpp b/src/mame/apple/dafb.cpp index 476d21da93a..586e150c2fa 100644 --- a/src/mame/apple/dafb.cpp +++ b/src/mame/apple/dafb.cpp @@ -5,18 +5,20 @@ Emulation by R. Belmont Some inspiration from mv_sonora by Olivier Galibert and nubus_48gc by Vas Crabb - DAFB (officially Direct Access Frame Buffer, internally Dave's Awesome Frame Buffer) was the on-board video + DAFB (officially Direct Access Frame Buffer, internally Dale's Awesome Frame Buffer) was the on-board video for the Quadra 700, 900, and 950. Standalone DAFB and DAFB-II include what Apple calls "Turbo SCSI", which is an interface for up to 2 5394/5396 chips that adds a configurable wait state to each access and can hold off /DTACK on pseudo-DMA reads and writes. Shipping configurations: - DAFB - original standalone chip, Quadra 700 and 900 (returns versions 0 and 1) - DAFB II - revised standalone chip with 15 bpp support added (uses AC843 CODEC instead of AC842) (returns version 2) - Used in Quadra 950. - MEMC - DAFB II without the Turbo SCSI logic, in the djMEMC and MEMCjr memory controllers. (returns version 3) - Used in LC 475, LC 575, Quadra 605, Quadra 610, Quadra 650, and Quadra 800. - This version uses a DP8533 timing generator instead of the DP8531. + DAFB - original standalone chip, Quadra 700 and 900 (returns versions 0 and 1) + DAFB II - revised standalone chip with 15 bpp support added (uses AC842a CODEC instead of AC842) (returns version 2) + Used in Quadra 950. + MEMC - DAFB II without the Turbo SCSI logic, in the djMEMC and MEMCjr memory controllers. (returns version 3) + Used in LC 475, LC 575, Quadra 605, Quadra 610, Quadra 650, and Quadra 800. + This version uses a DP8534 timing generator instead of the DP8531 and an AC842a DAC instead of AC842. + DaMFB - DAFB II with a PowerPC bus interface instead of 68040. Used in the HPV card for the PowerMac 6100/7100/8100. + Platinum - DAFB II with 4 MB VRAM support and a blitter bolted on. The Turbo SCSI block moved into the IOSB and PrimeTime I/O ASICs for the machines where DAFB moved into the memory controller. It was enhanced slightly to allow longword pseudo-DMA transfers. @@ -45,15 +47,14 @@ #define LOG_SWATCH (1U << 1) #define LOG_CLOCKGEN (1U << 2) #define LOG_MONSENSE (1U << 3) -#define LOG_AC842 (1U << 4) +#define LOG_RAMDAC (1U << 4) #define LOG_TURBOSCSI (1U << 5) #define VERBOSE (0) #include "logmacro.h" -static constexpr int VRAM_SIZE = 0x200000 / sizeof(u32); - DEFINE_DEVICE_TYPE(DAFB, dafb_device, "macdafb", "Apple DAFB video") +DEFINE_DEVICE_TYPE(DAFB_MEMC, dafb_memc_device, "macdafb_memc", "Apple DAFB II video (djMEMC integrated)") //------------------------------------------------- // ADDRESS_MAP @@ -63,19 +64,22 @@ void dafb_base::map(address_map &map) { map(0x00000000, 0x000000ff).rw(FUNC(dafb_base::dafb_r), FUNC(dafb_base::dafb_w)); map(0x00000100, 0x000001ff).rw(FUNC(dafb_base::swatch_r), FUNC(dafb_base::swatch_w)); - map(0x00000200, 0x000002ff).rw(FUNC(dafb_base::ac842_r), FUNC(dafb_base::ac842_w)); + map(0x00000200, 0x000002ff).rw(FUNC(dafb_base::ramdac_r), FUNC(dafb_base::ramdac_w)); map(0x00000300, 0x000003ff).rw(FUNC(dafb_base::clockgen_r), FUNC(dafb_base::clockgen_w)); } dafb_base::dafb_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, type, tag, owner, clock), + m_vram_size(0x200000), m_dafb_version(1), + m_pixel_clock(31334400), + m_pal_address(0), m_pal_idx(0), m_ac842_pbctrl(0), m_mode(0), m_screen(*this, "screen"), m_palette(*this, "palette"), m_monitor_config(*this, "monitor"), m_irq(*this), - m_vram_offset(0), m_timing_control(0), m_mode(0), m_depth(0), m_monitor_id(0), m_pal_address(0), - m_pal_idx(0), m_ac842_pbctrl(0), m_base(0), m_stride(1024), m_test(0), m_swatch_mode(1), + m_vram_offset(0), m_timing_control(0), m_monitor_id(0), + m_base(0), m_stride(1024), m_test(0), m_swatch_mode(1), m_cursor_line(0), m_anim_line(0), m_int_status(0), m_hres(0), m_vres(0), m_htotal(0), m_vtotal(0), m_config(0), m_block_control(0), m_swatch_test(0) { @@ -95,9 +99,17 @@ dafb_device::dafb_device(const machine_config &mconfig, const char *tag, device_ m_ncr[0] = m_ncr[1] = nullptr; } +dafb_memc_device::dafb_memc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + dafb_base(mconfig, DAFB_MEMC, tag, owner, clock), + m_pcbr1(0), + m_clock_shift(0), + m_clock_params(0) +{ +} + void dafb_base::device_start() { - m_vram = std::make_unique(VRAM_SIZE); + m_vram = std::make_unique(m_vram_size); m_vbl_timer = timer_alloc(FUNC(dafb_base::vbl_tick), this); m_cursor_timer = timer_alloc(FUNC(dafb_base::cursor_tick), this); @@ -108,7 +120,6 @@ void dafb_base::device_start() save_item(NAME(m_timing_control)); save_item(NAME(m_vram_offset)); save_item(NAME(m_mode)); - save_item(NAME(m_depth)); save_item(NAME(m_monitor_id)); save_item(NAME(m_base)); save_item(NAME(m_stride)); @@ -130,7 +141,7 @@ void dafb_base::device_start() save_item(NAME(m_block_control)); save_item(NAME(m_swatch_test)); save_item(NAME(m_int_status)); - save_pointer(NAME(m_vram), VRAM_SIZE); + save_pointer(NAME(m_vram), m_vram_size); machine().save().register_postload(save_prepost_delegate(FUNC(dafb_base::recalc_mode), this)); } @@ -271,6 +282,18 @@ u32 dafb_base::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const } } break; + + case 5: // 16bpp x555 + for (int y = 0; y < m_vres; y++) + { + u32 *scanline = &bitmap.pix(y); + for (int x = 0; x < m_hres; x++) + { + u16 const pixels = (vram8[(y * stride) + (x<<1)] << 8) | vram8[(y * stride) + (x<<1) + 1]; + *scanline++ = rgb_t(((pixels >> 10) & 0x1f) << 3, ((pixels >> 5) & 0x1f) << 3, (pixels & 0x1f) << 3); + } + } + break; } return 0; @@ -326,6 +349,7 @@ u32 dafb_base::dafb_r(offs_t offset) void dafb_base::dafb_w(offs_t offset, u32 data) { + data &= 0xfff; switch (offset << 2) { case 0: // bits 20-9 of base @@ -508,6 +532,9 @@ u32 dafb_base::swatch_r(offs_t offset) void dafb_base::swatch_w(offs_t offset, u32 data) { + // registers are all 12 bits wide + data &= 0xfff; + switch (offset << 2) { case 0x0: // Swatch mode @@ -592,7 +619,7 @@ void dafb_base::swatch_w(offs_t offset, u32 data) } } -u32 dafb_base::ac842_r(offs_t offset) +u32 dafb_base::ramdac_r(offs_t offset) { switch (offset << 2) { @@ -625,11 +652,12 @@ u32 dafb_base::ac842_r(offs_t offset) return 0; } -void dafb_base::ac842_w(offs_t offset, u32 data) +void dafb_base::ramdac_w(offs_t offset, u32 data) { switch (offset << 2) { case 0: + LOGMASKED(LOG_RAMDAC, "Address to %d\n", data & 0xff); m_pal_address = data & 0xff; m_pal_idx = 0; break; @@ -670,7 +698,7 @@ void dafb_base::ac842_w(offs_t offset, u32 data) case 0x20: m_ac842_pbctrl = data; - LOGMASKED(LOG_AC842, "%02x to AC842 pixel bus control, & 0x1c = %02x\n", data, data & 0x1c); + LOGMASKED(LOG_RAMDAC, "%02x to AC842 pixel bus control, & 0x1c = %02x\n", data, data & 0x1c); switch (data & 0x1c) { case 0x00: @@ -752,17 +780,22 @@ void dafb_base::recalc_mode() } } -u32 dafb_base::clockgen_r(offs_t offset) +u8 dafb_base::clockgen_r(offs_t offset) { return 0; } -void dafb_base::clockgen_w(offs_t offset, u32 data) +void dafb_base::clockgen_w(offs_t offset, u8 data) { - m_dp8531_regs[offset>>2] = data & 0xf; - LOGMASKED(LOG_CLOCKGEN, "%s: Write %x to DP8531 at %d\n", tag(), data, offset); + if ((offset & 3) != 3) + { + return; + } + + m_dp8531_regs[offset>>4] = data & 0xf; + LOGMASKED(LOG_CLOCKGEN, "%s: Write %x to DP8531 at %d (reg %d)\n", tag(), data, offset, offset>>4); - if ((offset >> 2) == 15) + if ((offset>>4) == 15) { int r = m_dp8531_regs[6] << 8 | m_dp8531_regs[5] << 4 | m_dp8531_regs[4]; int p = (1 << m_dp8531_regs[9]); @@ -785,12 +818,12 @@ void dafb_base::clockgen_w(offs_t offset, u32 data) u32 dafb_base::vram_r(offs_t offset) { - return m_vram[offset]; + return m_vram[offset & (m_vram_size - 1)]; } void dafb_base::vram_w(offs_t offset, u32 data, u32 mem_mask) { - COMBINE_DATA(&m_vram[offset]); + COMBINE_DATA(&m_vram[offset & (m_vram_size - 1)]); } void dafb_base::recalc_ints() @@ -951,3 +984,128 @@ void dafb_device::turboscsi_drq_w(int state) template void dafb_device::turboscsi_drq_w<0>(int state); template void dafb_device::turboscsi_drq_w<1>(int state); + +// ************************************************************************ +// dafb_memc_device overrides/additions +// ************************************************************************ +void dafb_memc_device::device_start() +{ + m_vram_size = 0x100000; // all 5 MEMC machines can only have 1 MB VRAM + dafb_base::device_start(); +} + +// MEMC's DAFB uses the DP8534 clock generator, not the DP8531. +// The interface is quite different, and no datasheet seems to exist. +// This emulation is reverse-engineered from https://sourceforge.net/projects/dt3152/files/ +// which brute forces the DP8534 parameters from a given pixel clock (and also shows +// how the parameters become the output clock, as well as how the parameters fit into +// the bitstream clocked into the chip. +u8 dafb_memc_device::clockgen_r(offs_t offset) +{ + return 0; +} + +void dafb_memc_device::clockgen_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 3: // shift parameters into bit 0 one bit at a time + m_clock_shift <<= 1; + m_clock_shift |= (data & 1); + break; + + case 19: // commit parameters to memory + m_clock_params = (m_clock_shift << 2); + m_clock_shift = 0; + + const u8 param1 = bitswap<8>((m_clock_params >> 32) & 0xff, 0, 1, 2, 3, 4, 5, 6, 7); + const u8 param2 = bitswap<8>((m_clock_params >> 24) & 0xff, 0, 1, 2, 3, 4, 5, 6, 7); + const u8 param3 = bitswap<8>((m_clock_params >> 16) & 0xff, 0, 1, 2, 3, 4, 5, 6, 7); + const u8 param4 = bitswap<8>((m_clock_params >> 8) & 0xff, 0, 1, 2, 3, 4, 5, 6, 7); + const u8 param5 = bitswap<8>(m_clock_params & 0xff, 0, 1, 2, 3, 4, 5, 6, 7); + + const u8 p = (param1 >> 7) | (param2 << 1); + const u8 rcnt = (param2 >> 7) | (param3 << 1); + const u8 ncnt = (param4 >> 1) | (param5 << 7); + + const float vco_clock = (20.0 * (float)ncnt) / (float)rcnt; + const float pixel_clock = vco_clock / (float)(p+1); + m_pixel_clock = (int)(pixel_clock*1000000+.5); + + LOGMASKED(LOG_CLOCKGEN, "DP8534: P %d RCNT %d NCNT %d => VCO=%d Hz, pixel clock %d Hz\n", p, rcnt, ncnt, (int)(vco_clock*1000000+.5), m_pixel_clock); + break; + } +} + +// This is an AC842a, which has x555 16bpp mode +u32 dafb_memc_device::ramdac_r(offs_t offset) +{ + switch (offset << 2) + { + case 0x20: + if ((m_pal_address == 1) && ((m_ac842_pbctrl & 0x06) == 0x06)) + { + return m_pcbr1; + } + else + { + return dafb_base::ramdac_r(offset); + } + + default: + return dafb_base::ramdac_r(offset); + } +} + +void dafb_memc_device::ramdac_w(offs_t offset, u32 data) +{ + switch (offset << 2) + { + case 0x20: + if ((m_pal_address == 1) && ((m_ac842_pbctrl & 0x06) == 0x06)) + { + LOGMASKED(LOG_RAMDAC, "%02x to AC842a PCBR1\n", data); + m_pcbr1 = data & 0xf0; + } + else + { + LOGMASKED(LOG_RAMDAC, "%02x to AC842a PCBR0, & 0x1c = %02x\n", data, data & 0x1c); + m_ac842_pbctrl = data; + if ((m_pcbr1 & 0xc0) == 0xc0) + { + m_mode = 5; // 16 bpp (x555) + } + else + { + switch (data & 0x1c) + { + case 0x00: + m_mode = 0; // 1bpp + break; + + case 0x08: + m_mode = 1; // 2bpp + break; + + case 0x10: + m_mode = 2; // 4bpp + break; + + case 0x18: + m_mode = 3; // 8bpp + break; + + case 0x1c: + m_mode = 4; // 24bpp + break; + } + } + recalc_mode(); + } + break; + + default: + dafb_base::ramdac_w(offset, data); + break; + } +} diff --git a/src/mame/apple/dafb.h b/src/mame/apple/dafb.h index e47ef203768..326bcba948d 100644 --- a/src/mame/apple/dafb.h +++ b/src/mame/apple/dafb.h @@ -31,10 +31,25 @@ protected: virtual void device_add_mconfig(machine_config &config) override; virtual ioport_constructor device_input_ports() const override; + u32 dafb_r(offs_t offset); + void dafb_w(offs_t offset, u32 data); + u32 swatch_r(offs_t offset); + void swatch_w(offs_t offset, u32 data); + virtual u32 ramdac_r(offs_t offset); + virtual void ramdac_w(offs_t offset, u32 data); + virtual u8 clockgen_r(offs_t offset); + virtual void clockgen_w(offs_t offset, u8 data); + void recalc_ints(); + void recalc_mode(); + u16 m_scsi_ctrl[2]; int m_drq[2]; int m_scsi_read_cycles[2], m_scsi_write_cycles[2], m_scsi_dma_read_cycles[2], m_scsi_dma_write_cycles[2]; + u32 m_vram_size; int m_dafb_version; + u32 m_pixel_clock; + + u8 m_pal_address, m_pal_idx, m_ac842_pbctrl, m_mode; private: required_device m_screen; @@ -70,27 +85,15 @@ private: std::unique_ptr m_vram; emu_timer *m_vbl_timer, *m_cursor_timer; u32 m_vram_offset; - u8 m_timing_control, m_mode, m_depth, m_monitor_id; - u8 m_pal_address, m_pal_idx, m_ac842_pbctrl; + u8 m_timing_control, m_monitor_id; u32 m_base, m_stride, m_test; u32 m_horizontal_params[10], m_vertical_params[7]; u8 m_swatch_mode; u16 m_cursor_line, m_anim_line; s32 m_int_status; u8 m_dp8531_regs[16]; - u32 m_pixel_clock; u32 m_hres, m_vres, m_htotal, m_vtotal, m_config, m_block_control, m_swatch_test; - u32 dafb_r(offs_t offset); - void dafb_w(offs_t offset, u32 data); - u32 swatch_r(offs_t offset); - void swatch_w(offs_t offset, u32 data); - u32 ac842_r(offs_t offset); - void ac842_w(offs_t offset, u32 data); - u32 clockgen_r(offs_t offset); - void clockgen_w(offs_t offset, u32 data); - void recalc_ints(); - void recalc_mode(); u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); TIMER_CALLBACK_MEMBER(vbl_tick); @@ -123,6 +126,26 @@ private: ncr53c94_device *m_ncr[2]; }; +class dafb_memc_device: public dafb_base +{ +public: + dafb_memc_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); + +protected: + virtual void device_start() override; + + virtual u8 clockgen_r(offs_t offset) override; + virtual void clockgen_w(offs_t offset, u8 data) override; + virtual u32 ramdac_r(offs_t offset) override; + virtual void ramdac_w(offs_t offset, u32 data) override; + +private: + u8 m_pcbr1; + u64 m_clock_shift; + u64 m_clock_params; +}; + DECLARE_DEVICE_TYPE(DAFB, dafb_device) +DECLARE_DEVICE_TYPE(DAFB_MEMC, dafb_memc_device) #endif /* MAME_APPLE_DAFB_H */ diff --git a/src/mame/apple/djmemc.cpp b/src/mame/apple/djmemc.cpp new file mode 100644 index 00000000000..f9fa6d68f3f --- /dev/null +++ b/src/mame/apple/djmemc.cpp @@ -0,0 +1,112 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Apple "djMEMC" memory controller + Emulation by R. Belmont + + djMEMC contains the following: + - A memory controller for up to 640 MiB of RAM + - The usual Mac ROM/RAM switch so at boot the processor has vectors at 0 + - DAFB II video, minus the "Turbo SCSI" feature, which has moved to IOSB +*/ + +#include "emu.h" +#include "djmemc.h" + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(DJMEMC, djmemc_device, "djmemc", "Apple djMEMC memory controller") + +//------------------------------------------------- +// ADDRESS_MAP +//------------------------------------------------- + +void djmemc_device::map(address_map &map) +{ + map(0x40000000, 0x400fffff).r(FUNC(djmemc_device::rom_switch_r)).mirror(0x0ff00000); + map(0xf9000000, 0xf91fffff).rw(m_video, FUNC(dafb_device::vram_r), FUNC(dafb_device::vram_w)); + map(0xf9800000, 0xf98003ff).m(m_video, FUNC(dafb_device::map)); +} + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void djmemc_device::device_add_mconfig(machine_config &config) +{ + DAFB_MEMC(config, m_video); + m_video->dafb_irq().set(FUNC(djmemc_device::vbl_w)); +} + +//------------------------------------------------- +// djmemc_device - constructor +//------------------------------------------------- + +djmemc_device::djmemc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, DJMEMC, tag, owner, clock), + m_maincpu(*this, finder_base::DUMMY_TAG), + m_video(*this, "video"), + m_rom(*this, finder_base::DUMMY_TAG), + m_irq(*this), + m_overlay(false) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void djmemc_device::device_start() +{ + m_rom_ptr = &m_rom[0]; + m_rom_size = m_rom.length() << 2; +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void djmemc_device::device_reset() +{ + m_overlay = true; + + // put ROM mirror at 0 + address_space &space = m_maincpu->space(AS_PROGRAM); + const u32 memory_size = std::min((u32)0x3fffff, m_rom_size); + const u32 memory_end = memory_size - 1; + offs_t memory_mirror = memory_end & ~(memory_size - 1); + + space.unmap_write(0x00000000, memory_end); + space.install_rom(0x00000000, memory_end & ~memory_mirror, memory_mirror, m_rom_ptr); +} + +u32 djmemc_device::rom_switch_r(offs_t offset) +{ + // disable the overlay + if (m_overlay) + { + address_space &space = m_maincpu->space(AS_PROGRAM); + const u32 memory_end = m_ram_size - 1; + void *memory_data = m_ram_ptr; + offs_t memory_mirror = memory_end & ~memory_end; + + space.install_ram(0x00000000, memory_end & ~memory_mirror, memory_mirror, memory_data); + m_overlay = false; + } + + return m_rom_ptr[offset & ((m_rom_size - 1) >> 2)]; +} + +void djmemc_device::set_ram_info(u32 *ram, u32 size) +{ + m_ram_ptr = ram; + m_ram_size = size; +} + +void djmemc_device::vbl_w(int state) +{ + m_irq(state); +} + diff --git a/src/mame/apple/djmemc.h b/src/mame/apple/djmemc.h new file mode 100644 index 00000000000..500fe373f41 --- /dev/null +++ b/src/mame/apple/djmemc.h @@ -0,0 +1,58 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont + +#ifndef MAME_APPLE_DJMEMC_H +#define MAME_APPLE_DJMEMC_H + +#pragma once + +#include "dafb.h" + +// ======================> djmemc_device + +class djmemc_device : public device_t +{ +public: + // construction/destruction + djmemc_device(const machine_config &mconfig, const char *tag, device_t *owner) + : djmemc_device(mconfig, tag, owner, (uint32_t)0) + { + } + + djmemc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // interface routines + void map(address_map &map); + + template void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward(args)...); } + template void set_rom_tag(T &&... args) { m_rom.set_tag(std::forward(args)...); } + void set_ram_info(u32 *ram, u32 size); + + auto write_irq() { return m_irq.bind(); } + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + required_device m_maincpu; + required_device m_video; + required_region_ptr m_rom; + devcb_write_line m_irq; + + std::unique_ptr m_vram; + bool m_overlay; + u32 *m_ram_ptr, *m_rom_ptr; + u32 m_ram_size, m_rom_size; + + u32 rom_switch_r(offs_t offset); + + void vbl_w(int state); +}; + +// device type definition +DECLARE_DEVICE_TYPE(DJMEMC, djmemc_device) + +#endif // MAME_APPLE_DJMEMC_H diff --git a/src/mame/apple/iosb.cpp b/src/mame/apple/iosb.cpp new file mode 100644 index 00000000000..8f1164b8ea2 --- /dev/null +++ b/src/mame/apple/iosb.cpp @@ -0,0 +1,626 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/* + Apple "IOSB" I/O ASIC + Emulation by R. Belmont + + IOSB stands for "I/O Subsystem Buffer". The chip was originally called "BIOS" ("Buffered I/O Subsystem"), + but they flipped the order around, presumably to avoid the obvious IBM PC reference. + + IOSB contains the following: + - A full VIA (VIA1) and a "pseudo-VIA" for VIA2. This is not the standard pseudo-VIA found in + chips like RBV, V8, or Sonora; it acts exactly like a real VIA, just without timers or a shift register. + - A SWIM2 floppy controller + - An ASC-like 4-channel audio controller + - The "Turbo SCSI" logic from the standalone versions of DAFB and DAFB II + - Support logic for various external subsystems (ADB, SCC, SONIC Ethernet) + + The "PrimeTime" I/O ASIC is similar, and "PrimeTime II" adds an ATA controller. +*/ + +#include "emu.h" +#include "iosb.h" + +#include "formats/ap_dsk35.h" + +#define LOG_TURBOSCSI (1U << 1) +#define LOG_SCSIDRQ (1U << 2) +#define LOG_IOSBREGS (1U << 3) + +#define VERBOSE (0) +#include "logmacro.h" + +static constexpr u32 C7M = 7833600; +static constexpr u32 C15M = (C7M * 2); + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +DEFINE_DEVICE_TYPE(IOSB, iosb_device, "iosb", "Apple IOSB system ASIC") + +//------------------------------------------------- +// ADDRESS_MAP +//------------------------------------------------- + +void iosb_device::map(address_map &map) +{ + map(0x00000000, 0x00001fff).rw(FUNC(iosb_device::mac_via_r), FUNC(iosb_device::mac_via_w)).mirror(0x00fc0000); + map(0x00002000, 0x00003fff).rw(FUNC(iosb_device::mac_via2_r), FUNC(iosb_device::mac_via2_w)).mirror(0x00f00000); + map(0x00010000, 0x000100ff).rw(FUNC(iosb_device::turboscsi_r), FUNC(iosb_device::turboscsi_w)).mirror(0x00fc0000); + map(0x00010100, 0x00010103).rw(FUNC(iosb_device::turboscsi_dma_r), FUNC(iosb_device::turboscsi_dma_w)).select(0x00fc0000); + map(0x00014000, 0x00015fff).rw(m_asc, FUNC(asc_device::read), FUNC(asc_device::write)).mirror(0x00f00000); + map(0x00018000, 0x00019fff).rw(FUNC(iosb_device::iosb_regs_r), FUNC(iosb_device::iosb_regs_w)).mirror(0x00f00000); + map(0x0001e000, 0x0001ffff).rw(FUNC(iosb_device::swim_r), FUNC(iosb_device::swim_w)).mirror(0x00f00000); + + // IOSB always gives an ID of "0x2BAD", the VIA I/O pins determine the rest + map(0x0fff0000, 0x0fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2bad; })); +} + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void iosb_device::device_add_mconfig(machine_config &config) +{ + R65NC22(config, m_via1, C7M / 10); + m_via1->readpa_handler().set(FUNC(iosb_device::via_in_a)); + m_via1->readpb_handler().set(FUNC(iosb_device::via_in_b)); + m_via1->writepa_handler().set(FUNC(iosb_device::via_out_a)); + m_via1->writepb_handler().set(FUNC(iosb_device::via_out_b)); + m_via1->cb1_handler().set(FUNC(iosb_device::via_out_cb1)); + m_via1->cb2_handler().set(FUNC(iosb_device::via_out_cb2)); + m_via1->irq_handler().set(FUNC(iosb_device::via1_irq)); + + R65NC22(config, m_via2, C7M / 10); + m_via2->readpa_handler().set(FUNC(iosb_device::via2_in_a)); + m_via2->irq_handler().set(FUNC(iosb_device::via2_irq)); + + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + ASC(config, m_asc, C15M, asc_device::asc_type::SONORA); + m_asc->add_route(0, "lspeaker", 1.0); + m_asc->add_route(1, "rspeaker", 1.0); + m_asc->irqf_callback().set(FUNC(iosb_device::asc_irq)); + + SWIM2(config, m_fdc, C15M); + m_fdc->devsel_cb().set(FUNC(iosb_device::devsel_w)); + m_fdc->phases_cb().set(FUNC(iosb_device::phases_w)); + + applefdintf_device::add_35_hd(config, m_floppy[0]); + applefdintf_device::add_35_nc(config, m_floppy[1]); + + RTC3430042(config, m_rtc, XTAL(32'768)); + m_rtc->cko_cb().set(m_via1, FUNC(via6522_device::write_ca2)); +} + +//------------------------------------------------- +// iosb_device - constructor +//------------------------------------------------- + +iosb_device::iosb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, IOSB, tag, owner, clock), + m_adb_st(*this), + m_cb1(*this), + m_cb2(*this), + m_pa1(*this, 0), + m_pa2(*this, 0), + m_pa4(*this, 0), + m_pa6(*this, 0), + m_maincpu(*this, finder_base::DUMMY_TAG), + m_ncr(*this, finder_base::DUMMY_TAG), + m_via1(*this, "via1"), + m_via2(*this, "via2"), + m_asc(*this, "asc"), + m_rtc(*this,"rtc"), + m_fdc(*this, "fdc"), + m_floppy(*this, "fdc:%d", 0U), + m_cur_floppy(nullptr), + m_hdsel(0), + m_adb_interrupt(0), + m_drq(0), + m_scsi_irq(0), + m_asc_irq(0), + m_scsi_read_cycles(3), + m_scsi_write_cycles(3), + m_scsi_dma_read_cycles(3), + m_scsi_dma_write_cycles(3), + m_scsi_dma_result(0), + m_scsi_second_half(false) +{ + std::fill(std::begin(m_iosb_regs), std::end(m_iosb_regs), 0); +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void iosb_device::device_start() +{ + m_maincpu->set_emmu_enable(true); + + m_6015_timer = timer_alloc(FUNC(iosb_device::mac_6015_tick), this); + m_6015_timer->adjust(attotime::never); + + save_item(NAME(m_via_interrupt)); + save_item(NAME(m_via2_interrupt)); + save_item(NAME(m_scc_interrupt)); + save_item(NAME(m_last_taken_interrupt)); + save_item(NAME(m_hdsel)); + save_item(NAME(m_via2_ca1_hack)); + save_item(NAME(m_nubus_irqs)); + save_item(NAME(m_iosb_regs)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void iosb_device::device_reset() +{ + // start 60.15 Hz timer + m_6015_timer->adjust(attotime::from_hz(60.15), 0, attotime::from_hz(60.15)); + + m_via_interrupt = m_via2_interrupt = m_scc_interrupt = 0; + m_last_taken_interrupt = -1; + m_hdsel = 0; + m_nubus_irqs = 0xff; + + m_via2_ca1_hack = 1; + m_via2->write_ca1(1); + m_via2->write_cb1(1); + + // set defaults that make VIA2 pseudo-ish + m_via2->write(11, 0xc0); + m_via2->write(12, 0x26); + m_via2->write(13, 0x00); + m_via2->write(14, 0x80); +} + +TIMER_CALLBACK_MEMBER(iosb_device::mac_6015_tick) +{ + m_via1->write_ca1(CLEAR_LINE); + m_via1->write_ca1(ASSERT_LINE); +} + +uint8_t iosb_device::via_in_a() +{ + u8 result = m_pa1() << 1; + result |= m_pa2() << 2; + result |= m_pa4() << 4; + result |= m_pa6() << 6; + return result; +} + +uint8_t iosb_device::via_in_b() +{ + return (m_adb_interrupt ? 0 : 8) | m_rtc->data_r(); +} + +void iosb_device::via_out_cb1(int state) +{ + m_cb1(state & 1); +} + +void iosb_device::via_out_cb2(int state) +{ + m_cb2(state & 1); +} + +void iosb_device::via_out_a(uint8_t data) +{ + int hdsel = BIT(data, 5); + if (hdsel != m_hdsel) + { + if (m_cur_floppy) + { + m_cur_floppy->ss_w(hdsel); + } + } + m_hdsel = hdsel; +} + +void iosb_device::via_out_b(uint8_t data) +{ + m_adb_st((data & 0x30) >> 4); + + m_rtc->ce_w((data & 0x04)>>2); + m_rtc->data_w(data & 0x01); + m_rtc->clk_w((data >> 1) & 0x01); +} + +void iosb_device::via1_irq(int state) +{ + m_via_interrupt = state; + field_interrupts(); +} + +void iosb_device::via2_irq(int state) +{ + m_via2_interrupt = state; + field_interrupts(); +} + +void iosb_device::field_interrupts() +{ + int take_interrupt = -1; + + if (m_scc_interrupt) + { + take_interrupt = 4; + } + else if (m_via2_interrupt) + { + take_interrupt = 2; + } + else if (m_via_interrupt) + { + take_interrupt = 1; + } + + if (m_last_taken_interrupt > -1) + { + m_maincpu->set_input_line(m_last_taken_interrupt, CLEAR_LINE); + m_last_taken_interrupt = -1; + } + + if (take_interrupt > -1) + { + m_maincpu->set_input_line(take_interrupt, ASSERT_LINE); + m_last_taken_interrupt = take_interrupt; + } +} + +void iosb_device::scc_irq_w(int state) +{ + m_scc_interrupt = (state == ASSERT_LINE); + field_interrupts(); +} + +template +void iosb_device::via2_irq_w(int state) +{ + m_nubus_irqs = m_via2->read(1); + + if (state) + { + m_nubus_irqs &= ~mask; + } + else + { + m_nubus_irqs |= mask; + } + + m_nubus_irqs |= 0x86; + + if ((m_nubus_irqs & 0x79) != 0x79) + { + if (m_via2_ca1_hack == 0) + { + m_via2->write_ca1(1); + } + m_via2_ca1_hack = 0; + m_via2->write_ca1(0); + } + else + { + m_via2_ca1_hack = 1; + m_via2->write_ca1(1); + } +} + +template void iosb_device::via2_irq_w<0x40>(int state); +template void iosb_device::via2_irq_w<0x20>(int state); +template void iosb_device::via2_irq_w<0x10>(int state); +template void iosb_device::via2_irq_w<0x08>(int state); +template void iosb_device::via2_irq_w<0x01>(int state); + +u8 iosb_device::via2_in_a() +{ + return m_nubus_irqs; +} + + +void iosb_device::scsi_irq_w(int state) +{ + m_via2->write_cb2(state ^ 1); + m_scsi_irq = state; +} + +void iosb_device::asc_irq(int state) +{ + m_via2->write_cb1(state); + m_asc_irq = state; +} + +void iosb_device::cb1_w(int state) +{ + m_via1->write_cb1(state); +} + +void iosb_device::cb2_w(int state) +{ + m_via1->write_cb2(state); +} + +uint16_t iosb_device::mac_via_r(offs_t offset) +{ + uint16_t data; + + offset >>= 8; + offset &= 0x0f; + + if (!machine().side_effects_disabled()) + via_sync(); + + data = m_via1->read(offset); + + return (data & 0xff) | (data << 8); +} + +void iosb_device::mac_via_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + offset >>= 8; + offset &= 0x0f; + + via_sync(); + + if (ACCESSING_BITS_0_7) + m_via1->write(offset, data & 0xff); + if (ACCESSING_BITS_8_15) + m_via1->write(offset, (data >> 8) & 0xff); +} + +u16 iosb_device::mac_via2_r(offs_t offset) +{ + int data; + + offset >>= 8; + offset &= 0x0f; + + if (!machine().side_effects_disabled()) + via_sync(); + + data = m_via2->read(offset); + // a little more pseudo-ness: bit 0 of the IFR shows the live line states of the IRQs and DRQ + if (offset == 13) + { + data &= ~0x19; + data |= (m_drq) ? 0x01 : 0; + data |= (m_scsi_irq) ? 0x08 : 0; + data |= (m_asc_irq) ? 0x10 : 0; + } + return (data & 0xff) | (data << 8); +} + +void iosb_device::mac_via2_w(offs_t offset, u16 data, u16 mem_mask) +{ + offset >>= 8; + offset &= 0x0f; + + // what makes VIA2 "pseudo" is that regs 4-10 can't be written, and 11 and 12 have canned values + switch (offset) + { + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + return; + + case 11: + m_via2->write(11, 0xc0); + return; + + case 12: + m_via2->write(12, 0x26); + return; + } + + via_sync(); + + if (ACCESSING_BITS_0_7) + m_via2->write(offset, data & 0xff); + if (ACCESSING_BITS_8_15) + m_via2->write(offset, (data >> 8) & 0xff); +} + +void iosb_device::via_sync() +{ + // The via runs at 783.36KHz while the main cpu runs at 15MHz or + // more, so we need to sync the access with the via clock. Plus + // the whole access takes half a (via) cycle and ends when synced + // with the main cpu again. + + // Get the main cpu time + u64 cycle = m_maincpu->total_cycles(); + + // Get the number of the cycle the via is in at that time + u64 via_cycle = cycle * m_via1->clock() / m_maincpu->clock(); + + // The access is going to start at via_cycle+1 and end at + // via_cycle+1.5, compute what that means in maincpu cycles (the + // +1 rounds up, since the clocks are too different to ever be + // synced). + u64 main_cycle = (via_cycle * 2 + 3) * m_maincpu->clock() / (2 * m_via1->clock()) + 1; + + // Finally adjust the main cpu icount as needed. + m_maincpu->adjust_icount(-int(main_cycle - cycle)); +} + +uint16_t iosb_device::swim_r(offs_t offset, u16 mem_mask) +{ + if (!machine().side_effects_disabled()) + { + m_maincpu->adjust_icount(-5); + } + + u16 result = m_fdc->read((offset >> 8) & 0xf); + return result << 8; +} +void iosb_device::swim_w(offs_t offset, u16 data, u16 mem_mask) +{ + if (ACCESSING_BITS_0_7) + m_fdc->write((offset >> 8) & 0xf, data & 0xff); + else + m_fdc->write((offset >> 8) & 0xf, data >> 8); +} + +void iosb_device::phases_w(uint8_t phases) +{ + if (m_cur_floppy) + m_cur_floppy->seek_phase_w(phases); +} + +void iosb_device::devsel_w(uint8_t devsel) +{ + if (devsel == 1) + m_cur_floppy = m_floppy[0]->get_device(); + else if (devsel == 2) + m_cur_floppy = m_floppy[1]->get_device(); + else + m_cur_floppy = nullptr; + + m_fdc->set_floppy(m_cur_floppy); + if (m_cur_floppy) + m_cur_floppy->ss_w(m_hdsel); +} + +u8 iosb_device::turboscsi_r(offs_t offset) +{ + if (!machine().side_effects_disabled()) + { + m_maincpu->adjust_icount(-m_scsi_read_cycles); + } + return m_ncr->read(offset>>4); +} + +void iosb_device::turboscsi_w(offs_t offset, u8 data) +{ + LOGMASKED(LOG_TURBOSCSI, "turboscsi_w: %02x @ %x (PC=%x)\n", data, offset>>4, m_maincpu->pc()); + m_maincpu->adjust_icount(-m_scsi_write_cycles); + m_ncr->write(offset>>4, data); +} + +u32 iosb_device::turboscsi_dma_r(offs_t offset, u32 mem_mask) +{ + if (mem_mask != 0xffffffff && mem_mask != 0xffff0000) + { + fatalerror("IOSB: turboscsi_dma_r mem_mask not handled %08x\n", mem_mask); + } + + if (!machine().side_effects_disabled() && BIT(offset << 1, 18)) + { + m_maincpu->adjust_icount(-m_scsi_dma_read_cycles); + } + + if (!m_drq) + { + // The real DAFB simply holds off /DTACK here, we simulate that + // by rewinding and repeating the instruction until DRQ is asserted. + m_maincpu->restart_this_instruction(); + m_maincpu->spin_until_time(attotime::from_usec(50)); + return 0xffff; + } + + if (mem_mask == 0xffffffff) + { + if (!m_scsi_second_half) + { + m_scsi_dma_result = m_ncr->dma16_swap_r()<<16; + m_scsi_second_half = true; + } + + if (!m_drq) + { + // The real DAFB simply holds off /DTACK here, we simulate that + // by rewinding and repeating the instruction until DRQ is asserted. + m_maincpu->restart_this_instruction(); + m_maincpu->spin_until_time(attotime::from_usec(50)); + return 0xffff; + } + + m_scsi_second_half = false; + m_scsi_dma_result |= m_ncr->dma16_swap_r(); + } + else if (mem_mask == 0xffff0000) + { + m_scsi_dma_result = m_ncr->dma16_swap_r()<<16; + } + return m_scsi_dma_result; +} + +void iosb_device::turboscsi_dma_w(offs_t offset, u32 data, u32 mem_mask) +{ + if (!machine().side_effects_disabled() && BIT(offset << 1, 18)) + { + m_maincpu->adjust_icount(-m_scsi_dma_write_cycles); + } + + LOGMASKED(LOG_TURBOSCSI, "dma_w %08x (mask %04x)\n", data & mem_mask, mem_mask); + + if (!m_drq) + { + m_maincpu->restart_this_instruction(); + m_maincpu->spin_until_time(attotime::from_usec(50)); + return; + } + + if (mem_mask == 0xffffffff) + { + if (!m_scsi_second_half) + { + m_ncr->dma16_swap_w(data>>16); + } + m_scsi_second_half = true; + if (!m_drq) + { + m_maincpu->restart_this_instruction(); + m_maincpu->spin_until_time(attotime::from_usec(50)); + return; + } + m_scsi_second_half = false; + m_ncr->dma16_swap_w(data & 0xffff); + } + else if (mem_mask == 0xffff0000) + { + m_ncr->dma16_swap_w(data >> 16); + } + else if (mem_mask == 0xff000000) + { + m_ncr->dma_w(data >> 24); + } + else + { + fatalerror("IOSB: turboscsi_dma_w unhandled mask %08x\n", mem_mask); + } +} + +void iosb_device::scsi_drq_w(int state) +{ + LOGMASKED(LOG_SCSIDRQ, "SCSI DRQ %d (was %d)\n", state, m_drq); + m_drq = state; + m_via2->write_ca2(state); +} + +u16 iosb_device::iosb_regs_r(offs_t offset) +{ + LOGMASKED(LOG_IOSBREGS, "iosb_regs_r: @ %x\n", offset>>7); + return m_iosb_regs[offset>>7]; +} + +void iosb_device::iosb_regs_w(offs_t offset, u16 data, u16 mem_mask) +{ + LOGMASKED(LOG_IOSBREGS, "iosb_regs_w: %04x @ %x, mask %04x\n", data, offset>>7, mem_mask); + COMBINE_DATA(&m_iosb_regs[offset>>7]); + + if ((offset>>7) == 2) + { + const int times[4] = { 5, 5, 4, 3 }; + m_scsi_dma_read_cycles = times[(m_iosb_regs[2]>>8) & 3]; + m_scsi_dma_write_cycles = (times[(m_iosb_regs[2]>>11) & 3]); + LOGMASKED(LOG_TURBOSCSI, "SCSI DMA read cycles %d write cycles %d\n", m_scsi_dma_read_cycles, m_scsi_dma_write_cycles); + } +} diff --git a/src/mame/apple/iosb.h b/src/mame/apple/iosb.h new file mode 100644 index 00000000000..1c353c4de3b --- /dev/null +++ b/src/mame/apple/iosb.h @@ -0,0 +1,128 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont + +#ifndef MAME_APPLE_IOSB_H +#define MAME_APPLE_IOSB_H + +#pragma once + +#include "macrtc.h" + +#include "cpu/m68000/m68040.h" +#include "machine/6522via.h" +#include "machine/applefdintf.h" +#include "machine/ncr53c90.h" +#include "machine/swim2.h" +#include "sound/asc.h" +#include "speaker.h" + +// ======================> iosb_device + +class iosb_device : public device_t +{ +public: + // construction/destruction + iosb_device(const machine_config &mconfig, const char *tag, device_t *owner) + : iosb_device(mconfig, tag, owner, (uint32_t)0) + { + } + + iosb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // interface routines + auto write_adb_st() { return m_adb_st.bind(); } // ADB state + auto write_cb1() { return m_cb1.bind(); } // ADB clock + auto write_cb2() { return m_cb2.bind(); } // ADB data + + auto read_pa1() { return m_pa1.bind(); } // ID bits + auto read_pa2() { return m_pa2.bind(); } + auto read_pa4() { return m_pa4.bind(); } + auto read_pa6() { return m_pa6.bind(); } + + void map(address_map &map); + + template void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward(args)...); } + template void set_scsi_tag(T &&... args) { m_ncr.set_tag(std::forward(args)...); } + + void pb3_w(int state) { m_adb_interrupt = state; } + void cb1_w(int state); // ADB clock + void cb2_w(int state); // ADB data + void scsi_irq_w(int state); + void scc_irq_w(int state); + + template + void via2_irq_w(int state); + + void via_sync(); + + void scsi_drq_w(int state); + u8 turboscsi_r(offs_t offset); + void turboscsi_w(offs_t offset, u8 data); + u32 turboscsi_dma_r(offs_t offset, u32 mem_mask); + void turboscsi_dma_w(offs_t offset, u32 data, u32 mem_mask); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + +private: + devcb_write8 m_adb_st; + devcb_write_line m_cb1, m_cb2; + devcb_read_line m_pa1, m_pa2, m_pa4, m_pa6; + + required_device m_maincpu; + required_device m_ncr; + required_device m_via1, m_via2; + required_device m_asc; + required_device m_rtc; + required_device m_fdc; + required_device_array m_floppy; + + emu_timer *m_6015_timer; + int m_via_interrupt, m_via2_interrupt, m_scc_interrupt, m_last_taken_interrupt; + floppy_image_device *m_cur_floppy = nullptr; + int m_hdsel; + int m_adb_interrupt; + int m_via2_ca1_hack; + u8 m_nubus_irqs; + + int m_drq, m_scsi_irq, m_asc_irq; + int m_scsi_read_cycles, m_scsi_write_cycles, m_scsi_dma_read_cycles, m_scsi_dma_write_cycles; + u32 m_scsi_dma_result; + bool m_scsi_second_half; + + u16 m_iosb_regs[0x20]; + + u16 iosb_regs_r(offs_t offset); + void iosb_regs_w(offs_t offset, u16 data, u16 mem_mask); + + uint16_t mac_via_r(offs_t offset); + void mac_via_w(offs_t offset, uint16_t data, uint16_t mem_mask); + uint16_t mac_via2_r(offs_t offset); + void mac_via2_w(offs_t offset, uint16_t data, uint16_t mem_mask); + + uint8_t via_in_a(); + uint8_t via2_in_a(); + uint8_t via_in_b(); + void via_out_a(uint8_t data); + void via_out_b(uint8_t data); + void field_interrupts(); + void via_out_cb1(int state); + void via_out_cb2(int state); + void via1_irq(int state); + void via2_irq(int state); + void asc_irq(int state); + TIMER_CALLBACK_MEMBER(mac_6015_tick); + + void phases_w(uint8_t phases); + void devsel_w(uint8_t devsel); + uint16_t swim_r(offs_t offset, u16 mem_mask); + void swim_w(offs_t offset, u16 data, u16 mem_mask); +}; + +// device type definition +DECLARE_DEVICE_TYPE(IOSB, iosb_device) + +#endif // MAME_APPLE_IOSB_H diff --git a/src/mame/apple/macquadra800.cpp b/src/mame/apple/macquadra800.cpp new file mode 100644 index 00000000000..e4c00f020ac --- /dev/null +++ b/src/mame/apple/macquadra800.cpp @@ -0,0 +1,306 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/**************************************************************************** + + drivers/macquadra800.cpp + Mac Centris 610 ("WLCD") + Mac Centris 630 ("Wombat 25") + Mac Quadra 610 ("Speedbump 610") + Mac Quadra 650 ("Speedbump 650") + Mac Quadra 800 ("Wombat 33") + + By R. Belmont + + These second-generation 68040 machines shrunk the huge mass of separate + chips found in the Quadra 700 down to a pair of ASICs, djMEMC (memory controller + plus revised DAFB video) and IOSB (I/O bus adaptor with integrated VIAs, + audio, "Turbo SCSI", and SWIM2 floppy). + +****************************************************************************/ + +#include "emu.h" + +#include "adbmodem.h" +#include "djmemc.h" +#include "iosb.h" +#include "macadb.h" +#include "mactoolbox.h" + +#include "bus/nscsi/devices.h" +#include "bus/nubus/cards.h" +#include "bus/nubus/nubus.h" +#include "bus/rs232/rs232.h" +#include "cpu/m68000/m68040.h" +#include "machine/dp83932c.h" +#include "machine/ncr53c90.h" +#include "machine/nscsi_bus.h" +#include "machine/ram.h" +#include "machine/timer.h" +#include "machine/z80scc.h" + +#include "softlist_dev.h" + +#define C32M 31.3344_MHz_XTAL +#define C15M (C32M/2) +#define C7M (C32M/4) + +namespace { + +class quadra800_state : public driver_device +{ +public: + quadra800_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_djmemc(*this, "djmemc"), + m_iosb(*this, "iosb"), + m_macadb(*this, "macadb"), + m_adbmodem(*this, "adbmodem"), + m_scc(*this, "scc"), + m_ram(*this, RAM_TAG), + m_scsibus(*this, "scsi"), + m_ncr1(*this, "scsi:7:ncr53c96"), + m_sonic(*this, "sonic") + { + } + + void macqd800(machine_config &config); + void macct610(machine_config &config); + void macct650(machine_config &config); + void macqd610(machine_config &config); + void macqd650(machine_config &config); + + void quadra800_map(address_map &map); + + void init_macqd800(); + +private: + required_device m_maincpu; + required_device m_djmemc; + required_device m_iosb; + required_device m_macadb; + required_device m_adbmodem; + required_device m_scc; + required_device m_ram; + required_device m_scsibus; + required_device m_ncr1; + required_device m_sonic; + + virtual void machine_start() override; + virtual void machine_reset() override; + + u16 mac_scc_r(offs_t offset) + { + m_iosb->via_sync(); + u16 result = m_scc->dc_ab_r(offset); + return (result << 8) | result; + } + void mac_scc_2_w(offs_t offset, u16 data) { m_iosb->via_sync(); m_scc->dc_ab_w(offset, data >> 8); } +}; + +void quadra800_state::machine_start() +{ + m_djmemc->set_ram_info((u32 *) m_ram->pointer(), m_ram->size()); +} + +void quadra800_state::machine_reset() +{ +} + +void quadra800_state::init_macqd800() +{ +} + +/*************************************************************************** + ADDRESS MAPS +***************************************************************************/ +void quadra800_state::quadra800_map(address_map &map) +{ + map(0x00000000, 0xffffffff).m(m_djmemc, FUNC(djmemc_device::map)); + map(0x50000000, 0x5fffffff).m(m_iosb, FUNC(iosb_device::map)); + + // 50008000 = Ethernet MAC ID PROM + map(0x5000a000, 0x5000b0ff).m(m_sonic, FUNC(dp83932c_device::map)).umask32(0x0000ffff).mirror(0x00fc0000); + map(0x5000c000, 0x5000dfff).rw(FUNC(quadra800_state::mac_scc_r), FUNC(quadra800_state::mac_scc_2_w)).mirror(0x00fc0000); +} + +/*************************************************************************** + DEVICE CONFIG +***************************************************************************/ + +static INPUT_PORTS_START( macadb ) +INPUT_PORTS_END + +/*************************************************************************** + MACHINE DRIVERS +***************************************************************************/ + +void quadra800_state::macqd800(machine_config &config) +{ + M68040(config, m_maincpu, 33_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + + DJMEMC(config, m_djmemc, 33_MHz_XTAL); + m_djmemc->set_maincpu_tag("maincpu"); + m_djmemc->set_rom_tag("bootrom"); + m_djmemc->write_irq().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x40>)); + + IOSB(config, m_iosb, 33_MHz_XTAL); + m_iosb->set_maincpu_tag("maincpu"); + m_iosb->set_scsi_tag("scsi:7:ncr53c96"); + m_iosb->write_adb_st().set(m_adbmodem, FUNC(adbmodem_device::set_via_state)); + + // Quadra 800 ID is 0x12 + m_iosb->read_pa1().set_constant(1); + m_iosb->read_pa2().set_constant(0); + m_iosb->read_pa4().set_constant(1); + m_iosb->read_pa6().set_constant(0); + + SCC85C30(config, m_scc, C7M); + m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400); + m_scc->out_int_callback().set(m_iosb, FUNC(iosb_device::scc_irq_w)); + m_scc->out_txda_callback().set("printer", FUNC(rs232_port_device::write_txd)); + m_scc->out_txdb_callback().set("modem", FUNC(rs232_port_device::write_txd)); + + rs232_port_device &rs232a(RS232_PORT(config, "printer", default_rs232_devices, nullptr)); + rs232a.rxd_handler().set(m_scc, FUNC(z80scc_device::rxa_w)); + rs232a.dcd_handler().set(m_scc, FUNC(z80scc_device::dcda_w)); + rs232a.cts_handler().set(m_scc, FUNC(z80scc_device::ctsa_w)); + + rs232_port_device &rs232b(RS232_PORT(config, "modem", default_rs232_devices, nullptr)); + rs232b.rxd_handler().set(m_scc, FUNC(z80scc_device::rxb_w)); + rs232b.dcd_handler().set(m_scc, FUNC(z80scc_device::dcdb_w)); + rs232b.cts_handler().set(m_scc, FUNC(z80scc_device::ctsb_w)); + + // SCSI bus and devices + NSCSI_BUS(config, m_scsibus); + NSCSI_CONNECTOR(config, "scsi:0", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:1", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:2", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:3", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:4", mac_scsi_devices, "cdrom"); + NSCSI_CONNECTOR(config, "scsi:5", mac_scsi_devices, nullptr); + NSCSI_CONNECTOR(config, "scsi:6", mac_scsi_devices, "harddisk"); + NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C96).clock(40_MHz_XTAL).machine_config( + [this] (device_t *device) + { + ncr53c96_device &adapter = downcast(*device); + + adapter.set_busmd(ncr53c96_device::BUSMD_1); + adapter.irq_handler_cb().set(m_iosb, FUNC(iosb_device::scsi_irq_w)); + adapter.drq_handler_cb().set(m_iosb, FUNC(iosb_device::scsi_drq_w)); + }); + + DP83932C(config, m_sonic, 40_MHz_XTAL / 2); // clock is C20M on the schematics + m_sonic->set_bus(m_maincpu, 0); + m_sonic->out_int_cb().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x01>)); + + nubus_device &nubus(NUBUS(config, "nubus", 40_MHz_XTAL / 4)); + nubus.set_space(m_maincpu, AS_PROGRAM); + nubus.out_irqc_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x08>)); + nubus.out_irqd_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x10>)); + nubus.out_irqe_callback().set(m_iosb, FUNC(iosb_device::via2_irq_w<0x20>)); + NUBUS_SLOT(config, "nbc", "nubus", mac_nubus_cards, nullptr); + NUBUS_SLOT(config, "nbd", "nubus", mac_nubus_cards, nullptr); + NUBUS_SLOT(config, "nbe", "nubus", mac_nubus_cards, nullptr); + + ADBMODEM(config, m_adbmodem, C7M); + m_adbmodem->via_clock_callback().set(m_iosb, FUNC(iosb_device::cb1_w)); + m_adbmodem->via_data_callback().set(m_iosb, FUNC(iosb_device::cb2_w)); + m_adbmodem->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w)); + m_adbmodem->irq_callback().set(m_iosb, FUNC(iosb_device::pb3_w)); + m_iosb->write_cb2().set(m_adbmodem, FUNC(adbmodem_device::set_via_data)); + config.set_perfect_quantum(m_maincpu); + + MACADB(config, m_macadb, C15M); + m_macadb->adb_data_callback().set(m_adbmodem, FUNC(adbmodem_device::set_adb_line)); + + /* internal ram */ + RAM(config, m_ram); + m_ram->set_default_size("8M"); + m_ram->set_extra_options("16M,32M,64M,96M,128M,192M,256M,320M,384M,512M,640M"); + + SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd"); +} + +void quadra800_state::macct610(machine_config &config) +{ + macqd800(config); + + M68040(config.replace(), m_maincpu, 20_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + + // Centris 610 ID is 0x40 + m_iosb->read_pa1().set_constant(0); + m_iosb->read_pa2().set_constant(0); + m_iosb->read_pa4().set_constant(0); + m_iosb->read_pa6().set_constant(1); +} + +void quadra800_state::macct650(machine_config &config) +{ + macqd800(config); + + M68040(config.replace(), m_maincpu, 25_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + + // Centris 650 ID is 0x46 + m_iosb->read_pa1().set_constant(1); + m_iosb->read_pa2().set_constant(1); + m_iosb->read_pa4().set_constant(0); + m_iosb->read_pa6().set_constant(1); +} + +void quadra800_state::macqd610(machine_config &config) +{ + macqd800(config); + + M68040(config.replace(), m_maincpu, 25_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + + // Quadra 610 ID is 0x44 + m_iosb->read_pa1().set_constant(0); + m_iosb->read_pa2().set_constant(1); + m_iosb->read_pa4().set_constant(0); + m_iosb->read_pa6().set_constant(1); +} + +void quadra800_state::macqd650(machine_config &config) +{ + macqd800(config); + + M68040(config.replace(), m_maincpu, 33_MHz_XTAL); + m_maincpu->set_addrmap(AS_PROGRAM, &quadra800_state::quadra800_map); + m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override"); + + // Quadra 650 ID is 0x52 + m_iosb->read_pa1().set_constant(1); + m_iosb->read_pa2().set_constant(0); + m_iosb->read_pa4().set_constant(1); + m_iosb->read_pa6().set_constant(1); +} + +ROM_START( macqd800 ) + ROM_REGION32_BE(0x100000, "bootrom", 0) + ROM_SYSTEM_BIOS(0, "default", "Version 23F2") + ROMX_LOAD( "f1acad13.rom", 0x000000, 0x100000, CRC(4e70e3c0) SHA1(f2a9ce387019bf272c6e3459d961b30f28942ac5), ROM_BIOS(0) ) + ROM_SYSTEM_BIOS(1, "original", "Version 23F1") + ROMX_LOAD( "f1a6f343.rom", 0x000000, 0x100000, CRC(3318a935) SHA1(031f13bfd726a70cfe4c73c5967861bc77297a79), ROM_BIOS(1) ) +ROM_END + +#define rom_macct610 rom_macqd800 +#define rom_macct650 rom_macqd800 +#define rom_macqd610 rom_macqd800 +#define rom_macqd650 rom_macqd800 + +} // anonymous namespace + +COMP( 1993, macqd800, 0, 0, macqd800, macadb, quadra800_state, init_macqd800, "Apple Computer", "Macintosh Quadra 800", MACHINE_SUPPORTS_SAVE) +COMP( 1993, macct610, macqd800, 0, macct610, macadb, quadra800_state, init_macqd800, "Apple Computer", "Macintosh Centris 610", MACHINE_SUPPORTS_SAVE) +COMP( 1993, macct650, macqd800, 0, macct650, macadb, quadra800_state, init_macqd800, "Apple Computer", "Macintosh Centris 650", MACHINE_SUPPORTS_SAVE) +COMP( 1993, macqd610, macqd800, 0, macqd610, macadb, quadra800_state, init_macqd800, "Apple Computer", "Macintosh Quadra 610", MACHINE_SUPPORTS_SAVE) +COMP( 1993, macqd650, macqd800, 0, macqd650, macadb, quadra800_state, init_macqd800, "Apple Computer", "Macintosh Quadra 650", MACHINE_SUPPORTS_SAVE) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 16ebffcec9d..0a9f0d59db1 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -885,6 +885,13 @@ macpd210 // 1992 Apple Macintosh PowerBook Duo 210 @source:apple/macquadra700.cpp macqd700 // 1991 Apple Macintosh Quadra 700 +@source:apple/macquadra800.cpp +macqd800 // 1993 Apple Macintosh Quadra 800 +macct610 // 1993 Apple Macintosh Centris 610 +macct650 // 1993 Apple Macintosh Centris 650 +macqd610 // 1993 Apple Macintosh Quadra 610 +macqd650 // 1993 Apple Macintosh Quadra 650 + @source:apple/newton.cpp newtnotp newtonmp -- cgit v1.2.3