apple/f108.cpp: New device for the Apple F108 memory controller / northbridge. [R. Belmont]

apple/valkyrie.cpp: New device for Apple Valkyrie framebuffer ASIC. [R. Belmont]

apple/iosb.cpp: Added support for the PrimeTime II I/O ASIC. [R. Belmont]

New machines added as working
-----------------------------
Apple Computer Macintosh Quadra 630 [R. Belmont]

New working clones added
------------------------
Apple Computer Macintosh LC/Performa 580 [R. Belmont]
This commit is contained in:
arbee 2023-08-14 21:10:59 -04:00
parent 7bb90aa659
commit 972e35e451
8 changed files with 1144 additions and 31 deletions

211
src/mame/apple/f108.cpp Normal file
View File

@ -0,0 +1,211 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
Apple "F108" memory controller
Emulation by R. Belmont
F108 contains:
- A memory controller
- The usual Mac ROM/RAM switch so at boot the processor has vectors at 0
- An ATA bus interface
- An SCC interface
- A SCSI controller which is claimed to be "just like a 53C96". A real 53C96 seems to work fine.
*/
#include "emu.h"
#include "f108.h"
#include "softlist_dev.h"
#include "bus/nscsi/devices.h"
#include "bus/rs232/rs232.h"
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(F108, f108_device, "macf108", "Apple F108 memory controller")
//-------------------------------------------------
// ADDRESS_MAP
//-------------------------------------------------
void f108_device::map(address_map &map)
{
map(0x40000000, 0x400fffff).r(FUNC(f108_device::rom_switch_r)).mirror(0x0ff00000).nopw();
map(0x5000c000, 0x5000dfff).rw(FUNC(f108_device::scc_r), FUNC(f108_device::scc_w)).mirror(0x00fc0000);
map(0x5001a000, 0x5001a01f).rw(m_ata, FUNC(ata_interface_device::cs0_swap_r), FUNC(ata_interface_device::cs0_swap_w)).umask32(0xffff0000).mirror(0x00fc0000);
map(0x5001a000, 0x5001a003).rw(FUNC(f108_device::ata_data_r), FUNC(f108_device::ata_data_w)).mirror(0x00fc0000);
map(0x5001a020, 0x5001a03f).rw(m_ata, FUNC(ata_interface_device::cs1_swap_r), FUNC(ata_interface_device::cs1_swap_w)).umask32(0xffff0000).mirror(0x00fc0000);
// a040 is probably a configuration register based on later Apple ATA implementations, but the values written don't make sense
map(0xf9000000, 0xf90fffff).ram(); // VRAM
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
void f108_device::device_add_mconfig(machine_config &config)
{
ATA_INTERFACE(config, m_ata).options(ata_devices, "hdd", nullptr, false);
m_ata->irq_handler().set(FUNC(f108_device::ata_irq_w));
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, nullptr);
NSCSI_CONNECTOR(config, "scsi:7").option_set("ncr53c96", NCR53C96).clock(40_MHz_XTAL).machine_config(
[this] (device_t *device)
{
ncr53c96_device &adapter = downcast<ncr53c96_device &>(*device);
adapter.set_busmd(ncr53c96_device::BUSMD_1);
adapter.irq_handler_cb().set(m_primetimeii, FUNC(primetime_device::scsi_irq_w));
adapter.drq_handler_cb().set(m_primetimeii, FUNC(primetime_device::scsi_drq_w));
});
SOFTWARE_LIST(config, "hdd_list").set_original("mac_hdd");
SCC85C30(config, m_scc, 31.3344_MHz_XTAL/4);
m_scc->configure_channels(3'686'400, 3'686'400, 3'686'400, 3'686'400);
m_scc->out_int_callback().set(FUNC(f108_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));
}
//-------------------------------------------------
// f108_device - constructor
//-------------------------------------------------
f108_device::f108_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, F108, tag, owner, clock),
m_maincpu(*this, finder_base::DUMMY_TAG),
m_primetimeii(*this, finder_base::DUMMY_TAG),
m_ata(*this, "ata"),
m_scsibus(*this, "scsi"),
m_ncr1(*this, "scsi:7:ncr53c96"),
m_scc(*this, "scc"),
m_rom(*this, finder_base::DUMMY_TAG),
m_ata_irq(*this),
m_overlay(false)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void f108_device::device_start()
{
m_rom_ptr = &m_rom[0];
m_rom_size = m_rom.length() << 2;
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void f108_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 f108_device::rom_switch_r(offs_t offset)
{
// disable the overlay
if (m_overlay && !machine().side_effects_disabled())
{
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 f108_device::set_ram_info(u32 *ram, u32 size)
{
m_ram_ptr = ram;
m_ram_size = size;
}
void f108_device::ata_irq_w(int state)
{
m_ata_irq(state);
}
u32 f108_device::ata_data_r(offs_t offset, u32 mem_mask)
{
u32 retval = 0;
if (mem_mask == 0xffffffff)
{
retval = m_ata->cs0_swap_r(0, 0xffff) << 16;
retval |= m_ata->cs0_swap_r(0, 0xffff);
}
else if ((mem_mask & 0xffff0000) != 0)
{
retval = m_ata->cs0_swap_r(0, mem_mask >> 16) << 16;
}
return retval;
}
void f108_device::ata_data_w(offs_t offset, u32 data, u32 mem_mask)
{
if (mem_mask == 0xffffffff)
{
m_ata->cs0_swap_w(0, data >> 16, 0xffff);
m_ata->cs0_swap_w(0, data & 0xffff, 0xffff);
}
else if ((mem_mask & 0xffff0000) != 0)
{
m_ata->cs0_swap_w(0, data >> 16, mem_mask >> 16);
}
}
u16 f108_device::scc_r(offs_t offset)
{
m_primetimeii->via_sync();
u16 result = m_scc->dc_ab_r(offset);
return (result << 8) | result;
}
void f108_device::scc_w(offs_t offset, u16 data)
{
m_primetimeii->via_sync();
m_scc->dc_ab_w(offset, data >> 8);
}
void f108_device::scc_irq_w(int state)
{
m_primetimeii->scc_irq_w(state);
}

72
src/mame/apple/f108.h Normal file
View File

@ -0,0 +1,72 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
#ifndef MAME_APPLE_F108_H
#define MAME_APPLE_F108_H
#pragma once
#include "iosb.h"
#include "bus/ata/ataintf.h"
#include "machine/ncr53c90.h"
#include "machine/nscsi_bus.h"
#include "machine/z80scc.h"
// ======================> f108_device
/// \brief Device class for Apple F108 system controller ASIC.
///
/// F108 includes a memory controller, NCR 53C96 compatible SCSI,
/// and a single-drive ATA-1 interface.
class f108_device : public device_t
{
public:
// construction/destruction
f108_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// interface routines
virtual void map(address_map &map);
template <typename... T> void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward<T>(args)...); }
template <typename... T> void set_primetimeii_tag(T &&... args) { m_primetimeii.set_tag(std::forward<T>(args)...); }
template <typename... T> void set_rom_tag(T &&... args) { m_rom.set_tag(std::forward<T>(args)...); }
void set_ram_info(u32 *ram, u32 size);
auto write_ata_irq() { return m_ata_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;
u32 rom_switch_r(offs_t offset);
private:
void ata_irq_w(int state);
u32 ata_data_r(offs_t offset, u32 mem_mask);
void ata_data_w(offs_t offset, u32 data, u32 mem_mask);
u16 scc_r(offs_t offset);
void scc_w(offs_t offset, u16 data);
void scc_irq_w(int state);
required_device<cpu_device> m_maincpu;
required_device<primetimeii_device> m_primetimeii;
required_device<ata_interface_device> m_ata;
required_device<nscsi_bus_device> m_scsibus;
required_device<ncr53c96_device> m_ncr1;
required_device<z80scc_device> m_scc;
required_region_ptr<u32> m_rom;
devcb_write_line m_ata_irq;
bool m_overlay;
u32 *m_ram_ptr, *m_rom_ptr;
u32 m_ram_size, m_rom_size;
};
// device type definition
DECLARE_DEVICE_TYPE(F108, f108_device)
#endif // MAME_APPLE_F108_H

View File

@ -15,12 +15,12 @@
- The "Turbo SCSI" logic from the standalone versions of DAFB and DAFB II
- Support logic for various external subsystems (ADB, SCC, SONIC Ethernet)
IOSB and PrimeTime are similar to Sonora, but replace the RBV/V8/VASP/Sonora pseudo-VIA with a
real VIA core that has the timers disabled and some IER and PCR bits hard-wired to specific values.
The pseudo-VIA returns in AMIC on the PDM-class PowerMacs and goes away forever in the TNT-class.
IOSB and PrimeTime are similar to Sonora, but replace the RBV/V8/VASP/Sonora pseudo-VIA with a
real VIA core that has the timers disabled and some IER and PCR bits hard-wired to specific values.
The pseudo-VIA returns in AMIC on the PDM-class PowerMacs and goes away forever in the TNT-class.
PrimeTime swaps the GI/Microchip ADB modem interface for the standard Cuda hookup found in Sonora,
and doesn't bring out the 4 VIA ID pins. PrimeTime II is similar.
PrimeTime swaps the GI/Microchip ADB modem interface for the standard Cuda hookup found in Sonora,
and doesn't bring out the 4 VIA ID pins. PrimeTime II is similar.
*/
#include "emu.h"
@ -44,6 +44,7 @@ static constexpr u32 C15M = (C7M * 2);
DEFINE_DEVICE_TYPE(IOSB, iosb_device, "iosb", "Apple IOSB I/O ASIC")
DEFINE_DEVICE_TYPE(PRIMETIME, primetime_device, "primetime", "Apple PrimeTime I/O ASIC")
DEFINE_DEVICE_TYPE(PRIMETIMEII, primetimeii_device, "primetime2", "Apple PrimeTime II I/O ASIC")
//-------------------------------------------------
// ADDRESS_MAP
@ -125,6 +126,11 @@ iosb_base::iosb_base(const machine_config &mconfig, device_type type, const char
m_asc(*this, "asc"),
m_fdc(*this, "fdc"),
m_floppy(*this, "fdc:%d", 0U),
m_nubus_irqs(0xff),
m_via_interrupt(0),
m_via2_interrupt(0),
m_scc_interrupt(0),
m_last_taken_interrupt(-1),
m_cur_floppy(nullptr),
m_hdsel(0),
m_adb_interrupt(0),
@ -147,14 +153,26 @@ iosb_device::iosb_device(const machine_config &mconfig, const char *tag, device_
{
}
primetime_device::primetime_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
iosb_base(mconfig, PRIMETIME, tag, owner, clock),
primetime_device::primetime_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
iosb_base(mconfig, type, tag, owner, clock),
write_pb4(*this),
write_pb5(*this),
read_pb3(*this, 0)
{
}
primetime_device::primetime_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
primetime_device(mconfig, PRIMETIME, tag, owner, clock)
{
}
primetimeii_device::primetimeii_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
primetime_device(mconfig, PRIMETIMEII, tag, owner, clock),
m_ata_irq(0)
{
std::fill(std::begin(m_primetimeii_regs), std::end(m_primetimeii_regs), 0);
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@ -185,11 +203,6 @@ void iosb_base::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);
@ -293,7 +306,7 @@ void iosb_base::field_interrupts()
void iosb_base::scc_irq_w(int state)
{
m_scc_interrupt = (state == ASSERT_LINE);
m_scc_interrupt = (state == ASSERT_LINE) ? 1 : 0;
field_interrupts();
}
@ -545,6 +558,8 @@ u32 iosb_base::turboscsi_dma_r(offs_t offset, u32 mem_mask)
return 0xffff;
}
LOGMASKED(LOG_TURBOSCSI, "dma_r mask %08x (%s)\n", mem_mask, machine().describe_context());
if (mem_mask == 0xffffffff)
{
if (!m_scsi_second_half)
@ -627,7 +642,6 @@ void iosb_base::scsi_drq_w(int state)
u16 iosb_base::iosb_regs_r(offs_t offset)
{
LOGMASKED(LOG_IOSBREGS, "iosb_regs_r: @ %x\n", offset>>7);
return m_iosb_regs[offset>>7];
}
@ -671,3 +685,39 @@ void primetime_device::via_out_b(uint8_t data)
write_pb4(BIT(data, 4));
write_pb5(BIT(data, 5));
}
// ------------------------------------------- PrimeTime II device
void primetimeii_device::device_start()
{
primetime_device::device_start();
save_item(NAME(m_ata_irq));
save_item(NAME(m_primetimeii_regs));
}
void primetimeii_device::map(address_map &map)
{
iosb_base::map(map);
map(0x0001a100, 0x0001a10f).r(FUNC(primetimeii_device::ata_regs_r)).mirror(0x00f00000);
}
// This may actually be in F108, the boundaries between the two chips aren't completely clear
u16 primetimeii_device::ata_regs_r(offs_t offset)
{
switch (offset)
{
case 0: // special interrupt status: bit 6 = VBL IRQ, bit 5 = ATA IRQ
return ((m_nubus_irqs ^ 0xff) & 0x40) | (m_ata_irq << 5);
default:
LOGMASKED(LOG_IOSBREGS, "%s: Unhandled ata_regs_r @ %x\n", tag(), offset);
break;
}
return 0;
}
void primetimeii_device::ata_irq_w(int state)
{
via2_irq_w<0x10>(state);
m_ata_irq = state;
}

View File

@ -22,7 +22,7 @@ class iosb_base : public device_t
{
public:
// construction/destruction
iosb_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
iosb_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
// interface routines
auto write_adb_st() { return m_adb_st.bind(); } // ADB state
@ -34,7 +34,7 @@ public:
auto read_pa4() { return m_pa4.bind(); }
auto read_pa6() { return m_pa6.bind(); }
void map(address_map &map);
virtual void map(address_map &map);
template <typename... T> void set_maincpu_tag(T &&... args) { m_maincpu.set_tag(std::forward<T>(args)...); }
template <typename... T> void set_scsi_tag(T &&... args) { m_ncr.set_tag(std::forward<T>(args)...); }
@ -65,6 +65,9 @@ protected:
virtual uint8_t via_in_b();
virtual void via_out_b(uint8_t data);
virtual u16 iosb_regs_r(offs_t offset);
virtual void iosb_regs_w(offs_t offset, u16 data, u16 mem_mask);
devcb_write8 m_adb_st;
devcb_write_line m_cb1, m_cb2;
devcb_read_line m_pa1, m_pa2, m_pa4, m_pa6;
@ -76,6 +79,10 @@ protected:
required_device<applefdintf_device> m_fdc;
required_device_array<floppy_connector, 2> m_floppy;
u16 m_iosb_regs[0x20];
u8 m_nubus_irqs;
private:
emu_timer *m_6015_timer;
int m_via_interrupt, m_via2_interrupt, m_scc_interrupt, m_last_taken_interrupt;
@ -83,22 +90,16 @@ private:
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;
s32 m_drq, m_scsi_irq, m_asc_irq;
u32 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);
u16 mac_via_r(offs_t offset);
void mac_via_w(offs_t offset, u16 data, u16 mem_mask);
u16 mac_via2_r(offs_t offset);
void mac_via2_w(offs_t offset, u16 data, u16 mem_mask);
uint8_t via_in_a();
uint8_t via2_in_a();
@ -113,15 +114,19 @@ private:
void phases_w(uint8_t phases);
void devsel_w(uint8_t devsel);
uint16_t swim_r(offs_t offset, u16 mem_mask);
u16 swim_r(offs_t offset, u16 mem_mask);
void swim_w(offs_t offset, u16 data, u16 mem_mask);
};
/// \brief Device class for Apple IOSB I/O controller ASIC.
///
/// IOSB includes 2 VIAs, TurboSCSI logic, a SWIM 2 floppy controller,
/// and more.
class iosb_device : public iosb_base
{
public:
// construction/destruction
iosb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
iosb_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
protected:
virtual void device_add_mconfig(machine_config &config) override;
@ -138,7 +143,8 @@ class primetime_device : public iosb_base
{
public:
// construction/destruction
primetime_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
primetime_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock);
primetime_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
auto pb4_callback() { return write_pb4.bind(); }
auto pb5_callback() { return write_pb5.bind(); }
@ -154,8 +160,29 @@ protected:
private:
};
class primetimeii_device : public primetime_device
{
public:
// construction/destruction
primetimeii_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
virtual void map(address_map &map) override;
void ata_irq_w(int state);
protected:
virtual void device_start() override;
private:
u16 ata_regs_r(offs_t offset);
s32 m_ata_irq;
u16 m_primetimeii_regs[0x20];
};
// device type definition
DECLARE_DEVICE_TYPE(IOSB, iosb_device)
DECLARE_DEVICE_TYPE(PRIMETIME, primetime_device)
DECLARE_DEVICE_TYPE(PRIMETIMEII, primetimeii_device)
#endif // MAME_APPLE_IOSB_H

209
src/mame/apple/macquadra630.cpp Executable file
View File

@ -0,0 +1,209 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/****************************************************************************
drivers/macquadra630.cpp
Mac Quadra 630 ("Show and Tell")
Mac LC 580 ("Dragonkid")
By R. Belmont
These machines took the cost-reduced but still decent Quadra 605/LC 575
and made them even cheaper by replacing the full-featured DAFB video chip
with "Valkyrie". Which appears to offer only a few pre-programmed video
mode timings.
Further cost reduction occured by replacing the hard disk with an ATA/IDE
model instead of the by-then traditional Mac SCSI drive. The ATA interface
was wedged into the chipset in a somewhat odd manner that impacts VIA2
interrupt handling (including video VBL IRQs).
Known problems:
- If you don't boot an OS, the mouse pointer will stop updating when the
question-mark disk appears. If you do boot an OS, everything's fine.
- The later version boot ROM for the LC 580 can't boot a SCSI CD-ROM. It
reads 512 bytes of a 2048 byte sector and expects CyclePhase_96 to read
and discard the rest of the sector from the drive. But it sees a (pseudo)
DMA command was active and waits for DRQ, which doesn't happen because
the 53C96's transfer count is zero. The earlier ROM has the same logic as
previous (and later!) 53C96 machines and works fine.
****************************************************************************/
#include "emu.h"
#include "softlist_dev.h"
#include "cuda.h"
#include "f108.h"
#include "iosb.h"
#include "macadb.h"
#include "mactoolbox.h"
#include "valkyrie.h"
#include "cpu/m68000/m68040.h"
#include "machine/ram.h"
#include "machine/timer.h"
#define C32M 31.3344_MHz_XTAL
#define C15M (C32M/2)
#define C7M (C32M/4)
namespace {
class quadra630_state : public driver_device
{
public:
quadra630_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_f108(*this, "f108"),
m_primetimeii(*this, "primetimeii"),
m_video(*this, "valkyrie"),
m_macadb(*this, "macadb"),
m_cuda(*this, "cuda"),
m_ram(*this, RAM_TAG)
{
}
void macqd630(machine_config &config);
void maclc580(machine_config &config);
void quadra630_map(address_map &map);
void lc580_map(address_map &map);
void init_macqd630();
private:
required_device<m68040_device> m_maincpu;
required_device<f108_device> m_f108;
required_device<primetimeii_device> m_primetimeii;
required_device<valkyrie_device> m_video;
required_device<macadb_device> m_macadb;
required_device<cuda_device> m_cuda;
required_device<ram_device> m_ram;
virtual void machine_start() override;
virtual void machine_reset() override;
void cuda_reset_w(int state)
{
m_maincpu->set_input_line(INPUT_LINE_HALT, state);
m_maincpu->set_input_line(INPUT_LINE_RESET, state);
}
};
void quadra630_state::machine_start()
{
m_f108->set_ram_info((u32 *) m_ram->pointer(), m_ram->size());
}
void quadra630_state::machine_reset()
{
m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
}
void quadra630_state::init_macqd630()
{
}
/***************************************************************************
ADDRESS MAPS
***************************************************************************/
void quadra630_state::quadra630_map(address_map &map)
{
map(0x00000000, 0xffffffff).m(m_f108, FUNC(f108_device::map));
map(0x00000000, 0xffffffff).m(m_video, FUNC(valkyrie_device::map));
map(0x50000000, 0x5fffffff).m(m_primetimeii, FUNC(primetime_device::map));
// 5000a000 = SONIC if comm slot card is installed
map(0x5000a000, 0x5000bfff).noprw().mirror(0x00fc0000);
// 2252 = Q630, 225a = LC580
map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a2252; }));
}
void quadra630_state::lc580_map(address_map &map)
{
quadra630_map(map);
map(0x5ffffffc, 0x5fffffff).lr32(NAME([](offs_t offset) { return 0xa55a225a; }));
}
/***************************************************************************
DEVICE CONFIG
***************************************************************************/
static INPUT_PORTS_START( macadb )
INPUT_PORTS_END
/***************************************************************************
MACHINE DRIVERS
***************************************************************************/
void quadra630_state::macqd630(machine_config &config)
{
M68040(config, m_maincpu, 33_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &quadra630_state::quadra630_map);
m_maincpu->set_dasm_override(std::function(&mac68k_dasm_override), "mac68k_dasm_override");
F108(config, m_f108, 33_MHz_XTAL);
m_f108->set_maincpu_tag("maincpu");
m_f108->set_primetimeii_tag("primetimeii");
m_f108->set_rom_tag("bootrom");
m_f108->write_ata_irq().set(m_primetimeii, FUNC(primetimeii_device::ata_irq_w));
PRIMETIMEII(config, m_primetimeii, 33_MHz_XTAL);
m_primetimeii->set_maincpu_tag("maincpu");
m_primetimeii->set_scsi_tag("f108:scsi:7:ncr53c96");
VALKYRIE(config, m_video, C32M);
m_video->write_irq().set(m_primetimeii, FUNC(primetime_device::via2_irq_w<0x40>));
MACADB(config, m_macadb, C15M);
// TODO: recapamac.com.au's logic board photos show Cuda 2.40 for both Q630 and LC580,
// but both ROM versions have issues syncing with 2.38 and 2.40 while 2.37 works.
CUDA_V237(config, m_cuda, XTAL(32'768));
m_cuda->reset_callback().set(FUNC(quadra630_state::cuda_reset_w));
m_cuda->linechange_callback().set(m_macadb, FUNC(macadb_device::adb_linechange_w));
m_cuda->via_clock_callback().set(m_primetimeii, FUNC(primetime_device::cb1_w));
m_cuda->via_data_callback().set(m_primetimeii, FUNC(primetime_device::cb2_w));
m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
config.set_perfect_quantum(m_maincpu);
m_primetimeii->pb3_callback().set(m_cuda, FUNC(cuda_device::get_treq));
m_primetimeii->pb4_callback().set(m_cuda, FUNC(cuda_device::set_byteack));
m_primetimeii->pb5_callback().set(m_cuda, FUNC(cuda_device::set_tip));
m_primetimeii->write_cb2().set(m_cuda, FUNC(cuda_device::set_via_data));
/* internal ram */
RAM(config, m_ram);
m_ram->set_default_size("4M");
m_ram->set_extra_options("8M,16M,32M");
}
void quadra630_state::maclc580(machine_config &config)
{
macqd630(config);
m_maincpu->set_addrmap(AS_PROGRAM, &quadra630_state::lc580_map);
}
ROM_START( macqd630 )
ROM_REGION32_BE(0x100000, "bootrom", 0)
ROM_LOAD( "06684214.bin", 0x000000, 0x100000, CRC(1735e7a5) SHA1(47cd505b6a7c46e5c0ffa29f0d5037c83e94a02f) )
ROM_END
ROM_START( maclc580 )
ROM_REGION32_BE(0x100000, "bootrom", 0)
ROM_SYSTEM_BIOS(0, "older", "Version 32F1")
ROMX_LOAD("06684214.bin", 0x000000, 0x100000, CRC(1735e7a5) SHA1(47cd505b6a7c46e5c0ffa29f0d5037c83e94a02f), ROM_BIOS(0))
ROM_SYSTEM_BIOS(1, "later", "Version 32F2 (bug: can't boot CD-ROM)")
ROMX_LOAD( "064dc91d.bin", 0x000000, 0x100000, CRC(59e6960f) SHA1(f48a8adf06bce50beee033d0d814da0e5e916d08), ROM_BIOS(1))
ROM_END
} // anonymous namespace
COMP( 1994, macqd630, 0, 0, macqd630, macadb, quadra630_state, init_macqd630, "Apple Computer", "Macintosh Quadra 630", MACHINE_SUPPORTS_SAVE)
COMP( 1995, maclc580, macqd630, 0, maclc580, macadb, quadra630_state, init_macqd630, "Apple Computer", "Macintosh LC/Performa 580", MACHINE_SUPPORTS_SAVE)

473
src/mame/apple/valkyrie.cpp Normal file
View File

@ -0,0 +1,473 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
Apple "Valkyrie" - very low-cost video framebuffer
Emulation by R. Belmont
This was the bonus awfulness in the infamous Quadra 630/LC 580 machines. Only
a few monitor types are supported and the video mode timings appear to be hardcoded
into the chip.
*/
#include "emu.h"
#include "valkyrie.h"
#define LOG_MODE (1U << 1)
#define LOG_MONSENSE (1U << 2)
#define LOG_RAMDAC (1U << 3)
#define VERBOSE (0)
#include "logmacro.h"
DEFINE_DEVICE_TYPE(VALKYRIE, valkyrie_device, "valkyrie", "Apple Valkyrie video")
//-------------------------------------------------
// ADDRESS_MAP
//-------------------------------------------------
void valkyrie_device::map(address_map &map)
{
map(0x50f2a000, 0x50f2bfff).rw(FUNC(valkyrie_device::regs_r), FUNC(valkyrie_device::regs_w));
map(0x50f24000, 0x50f25fff).rw(FUNC(valkyrie_device::ramdac_r), FUNC(valkyrie_device::ramdac_w));
map(0xf9000000, 0xf90fffff).rw(FUNC(valkyrie_device::vram_r), FUNC(valkyrie_device::vram_w));
}
valkyrie_device::valkyrie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, VALKYRIE, tag, owner, clock),
m_vram_size(0x100000),
m_pixel_clock(31334400),
m_pal_address(0), m_pal_idx(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_monitor_id(0),
m_base(0), m_stride(1024), m_int_status(0), m_hres(0), m_vres(0), m_htotal(0), m_vtotal(0),
m_config(0)
{
}
void valkyrie_device::device_start()
{
m_vram = std::make_unique<u32[]>(m_vram_size);
m_vbl_timer = timer_alloc(FUNC(valkyrie_device::vbl_tick), this);
m_vbl_timer->adjust(attotime::never);
save_item(NAME(m_vram_offset));
save_item(NAME(m_mode));
save_item(NAME(m_monitor_id));
save_item(NAME(m_base));
save_item(NAME(m_stride));
save_item(NAME(m_pal_address));
save_item(NAME(m_pal_idx));
save_item(NAME(m_hres));
save_item(NAME(m_vres));
save_item(NAME(m_htotal));
save_item(NAME(m_vtotal));
save_item(NAME(m_pixel_clock));
save_item(NAME(m_config));
save_item(NAME(m_int_status));
save_pointer(NAME(m_vram), m_vram_size);
machine().save().register_postload(save_prepost_delegate(FUNC(valkyrie_device::recalc_mode), this));
}
void valkyrie_device::device_reset()
{
// zero out the palette on start, I'm not sure where the video enable is, or if there is one
/* for (int i = 0; i < 256; i++)
{
m_palette->set_pen_red_level(i, 0);
m_palette->set_pen_green_level(i, 0);
m_palette->set_pen_blue_level(i, 0);
}*/
m_enable = false;
}
void valkyrie_device::device_add_mconfig(machine_config &config)
{
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
// dot clock, htotal, hstart, hend, vtotal, vstart, vend
m_screen->set_raw(31334400, 896, 0, 640, 525, 0, 480);
m_screen->set_screen_update(FUNC(valkyrie_device::screen_update));
PALETTE(config, m_palette).set_entries(256);
}
static constexpr u8 ext(u8 bc, u8 ac, u8 ab)
{
return 0x40 | (bc << 4) | (ac << 2) | ab;
}
static INPUT_PORTS_START(monitor_config)
PORT_START("monitor")
PORT_CONFNAME(0x7f, 6, "Monitor type")
PORT_CONFSETTING(0x02, u8"Mac RGB Display (12\" 512\u00d7384)") // "Rubik" (modified IIgs AppleColor RGB)
PORT_CONFSETTING(0x06, u8"Mac Hi-Res Display (12-14\" 640\u00d7480)") // "High Res"
PORT_CONFSETTING(ext(1, 1, 3), "640x480 VGA")
PORT_CONFSETTING(ext(2, 3, 1), "832x624 16\" RGB") // "Goldfish" or "16 inch RGB"
INPUT_PORTS_END
ioport_constructor valkyrie_device::device_input_ports() const
{
return INPUT_PORTS_NAME(monitor_config);
}
u32 valkyrie_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
auto const vram8 = util::big_endian_cast<u8 const>(&m_vram[0]) + 0x1000;
const pen_t *pens = m_palette->pens();
if (!m_enable)
{
return 0;
}
const u32 stride = (m_stride << m_mode);
switch (m_mode)
{
case 0: // 1bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/8; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[(pixels>>7)&1];
*scanline++ = pens[(pixels>>6)&1];
*scanline++ = pens[(pixels>>5)&1];
*scanline++ = pens[(pixels>>4)&1];
*scanline++ = pens[(pixels>>3)&1];
*scanline++ = pens[(pixels>>2)&1];
*scanline++ = pens[(pixels>>1)&1];
*scanline++ = pens[(pixels&1)];
}
}
}
break;
case 1: // 2bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/4; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[((pixels>>6)&3)];
*scanline++ = pens[((pixels>>4)&3)];
*scanline++ = pens[((pixels>>2)&3)];
*scanline++ = pens[(pixels&3)];
}
}
}
break;
case 2: // 4bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres/2; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[(pixels>>4)];
*scanline++ = pens[(pixels&0xf)];
}
}
}
break;
case 3: // 8bpp
{
for (int y = 0; y < m_vres; y++)
{
u32 *scanline = &bitmap.pix(y);
for (int x = 0; x < m_hres; x++)
{
u8 const pixels = vram8[(y * stride) + x];
*scanline++ = pens[pixels];
}
}
}
break;
case 4: // 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;
}
u32 valkyrie_device::regs_r(offs_t offset)
{
// printf("Read regs @ %x\n", offset<<2);
switch (offset<<2)
{
case 0:
return m_video_timing;
case 4:
return m_mode;
case 0x10: // config
return m_config;
case 0x14:
return (m_screen->vblank() << 24);
case 0x1c: // monitor sense in upper nibble, write monitor sense in lower nibble
{
u8 mon = m_monitor_config->read();
u8 res;
LOGMASKED(LOG_MONSENSE, "mon = %02x, m_monitor_id = %02x\n", mon, m_monitor_id);
if (mon & 0x40)
{
res = 7;
if (m_monitor_id == 0x4)
{
res &= 4 | (BIT(mon, 5) << 1) | BIT(mon, 4);
}
if (m_monitor_id == 0x2)
{
res &= (BIT(mon, 3) << 2) | 2 | BIT(mon, 2);
}
if (m_monitor_id == 0x1)
{
res &= (BIT(mon, 1) << 2) | (BIT(mon, 0) << 1) | 1;
}
}
else
{
res = mon;
}
LOGMASKED(LOG_MONSENSE, "sense result = %x\n", res);
return res<<28;
}
break;
}
return 0;
}
void valkyrie_device::regs_w(offs_t offset, u32 data)
{
data &= 0xfff;
switch (offset << 2)
{
case 0: // video timing select (apparently from hardcoded values!)
m_video_timing = data;
break;
case 4: // video depth: 0=1bpp, 1=2bpp, 2=4bpp, 3=8bpp, 4=16bpp
LOG("Mode set to %d\n", data & 7);
m_mode = data & 7;
break;
case 0xc: // written to lock in the video timing from register 0
if (data == 0x101)
{
recalc_mode();
}
break;
case 0x10:
m_config = data;
m_int_status &= ~1;
recalc_ints();
if (data & 1) // VBL enable
{
m_vbl_timer->adjust(m_screen->time_until_pos(m_vres, 0), 0);
}
else
{
m_vbl_timer->adjust(attotime::never);
}
break;
case 0x18: // screen enable
m_enable = (data & 0x80) ? true : false;
break;
case 0x1c: // drive monitor sense lines. 1 = drive, 0 = tri-state
m_monitor_id = (data & 0x7);
LOGMASKED(LOG_MONSENSE, "%x to sense drive\n", data & 0xf);
break;
default:
LOG("Valkyrie: Unk write %08x @ %x\n", data, offset<<2);
break;
}
}
u32 valkyrie_device::ramdac_r(offs_t offset)
{
switch (offset)
{
case 0:
if (!machine().side_effects_disabled())
{
m_pal_idx = 0;
}
return m_pal_address<<24;
case 1:
{
pen_t const entry = m_palette->pen(m_pal_address);
u8 const idx = m_pal_idx;
if (!machine().side_effects_disabled())
{
m_pal_idx++;
}
switch (idx)
{
case 0:
return ((entry >> 16) & 0xff) << 24;
case 1:
return ((entry >> 8) & 0xff) << 24;
case 2:
return (entry & 0xff) << 24;
}
}
break;
}
return 0;
}
void valkyrie_device::ramdac_w(offs_t offset, u32 data)
{
data >>= 24;
switch (offset)
{
case 0:
m_pal_address = data & 0xff;
m_pal_idx = 0;
break;
case 1:
switch (m_pal_idx)
{
case 0:
m_palette->set_pen_red_level(m_pal_address, data & 0xff);
break;
case 1:
m_palette->set_pen_green_level(m_pal_address, data & 0xff);
break;
case 2:
m_palette->set_pen_blue_level(m_pal_address, data & 0xff);
break;
}
m_pal_idx++;
if (m_pal_idx == 3)
{
m_pal_idx = 0;
m_pal_address++;
}
break;
case 2:
LOGMASKED(LOG_RAMDAC, "%02x to DAC @ %x\n", data, offset);
break;
}
}
void valkyrie_device::recalc_mode()
{
// mode parameters taken from the Quadra 630 Developer Note
switch (m_video_timing)
{
case 0x101: // default
case 0x686: // 13" 640x480
m_hres = 640;
m_vres = 480;
m_htotal = 864;
m_vtotal = 525;
m_pixel_clock = 30240000;
m_stride = 80;
break;
case 0x282: // Rubik 512x384
m_hres = 512;
m_vres = 384;
m_htotal = 640;
m_vtotal = 407;
m_pixel_clock = 15670000;
m_stride = 64;
break;
case 0xb8b: // VGA 640x480
m_hres = 640;
m_vres = 480;
m_htotal = 800;
m_vtotal = 525;
m_pixel_clock = 25180000;
m_stride = 80;
break;
case 0x989: // 16" RGB 832x624?
m_hres = 832;
m_vres = 624;
m_htotal = 1072;
m_vtotal = 690;
m_pixel_clock = 50000000;
m_stride = 104;
break;
}
const double refresh = (double)m_pixel_clock / (double)(m_htotal * m_vtotal);
LOGMASKED(LOG_MODE, "hres %d vres %d htotal %d vtotal %d refresh %f stride %d mode %d\n", m_hres, m_vres, m_htotal, m_vtotal, refresh, m_stride, m_mode);
if ((m_hres != 0) && (m_vres != 0))
{
rectangle visarea(0, m_hres - 1, 0, m_vres - 1);
m_screen->configure(m_htotal, m_vtotal, visarea, attotime::from_ticks(m_htotal * m_vtotal, m_pixel_clock).as_attoseconds());
}
}
u32 valkyrie_device::vram_r(offs_t offset)
{
return m_vram[offset & (m_vram_size - 1)];
}
void valkyrie_device::vram_w(offs_t offset, u32 data, u32 mem_mask)
{
COMBINE_DATA(&m_vram[offset & (m_vram_size - 1)]);
}
void valkyrie_device::recalc_ints()
{
if (m_int_status != 0)
{
m_irq(ASSERT_LINE);
}
else
{
m_irq(CLEAR_LINE);
}
}
TIMER_CALLBACK_MEMBER(valkyrie_device::vbl_tick)
{
m_int_status |= 1;
recalc_ints();
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
}

67
src/mame/apple/valkyrie.h Normal file
View File

@ -0,0 +1,67 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
#ifndef MAME_APPLE_VALKYRIE_H
#define MAME_APPLE_VALKYRIE_H
#pragma once
#include "cpu/m68000/m68040.h"
#include "emupal.h"
#include "screen.h"
class valkyrie_device : public device_t
{
public:
valkyrie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
virtual ~valkyrie_device() = default;
void map(address_map &map);
auto write_irq() { return m_irq.bind(); }
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
virtual ioport_constructor device_input_ports() const override;
void recalc_ints();
void recalc_mode();
u32 m_vram_size;
u32 m_pixel_clock;
u8 m_pal_address, m_pal_idx, m_mode;
private:
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_ioport m_monitor_config;
devcb_write_line m_irq;
std::unique_ptr<u32[]> m_vram;
emu_timer *m_vbl_timer;
u32 m_vram_offset;
u8 m_monitor_id;
u32 m_base, m_stride, m_video_timing;
s32 m_int_status;
u32 m_hres, m_vres, m_htotal, m_vtotal, m_config;
bool m_enable;
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
u32 regs_r(offs_t offset);
void regs_w(offs_t offset, u32 data);
u32 ramdac_r(offs_t offset);
void ramdac_w(offs_t offset, u32 data);
u32 vram_r(offs_t offset);
void vram_w(offs_t offset, u32 data, u32 mem_mask);
TIMER_CALLBACK_MEMBER(vbl_tick);
};
DECLARE_DEVICE_TYPE(VALKYRIE, valkyrie_device)
#endif /* MAME_APPLE_VALKYRIE_H */

4
src/mame/mame.lst Normal file → Executable file
View File

@ -904,6 +904,10 @@ macqd605 // 1993 Apple Macintosh Quadra 605
maclc475 // 1993 Apple Macintosh LC/Performa 475
maclc575 // 1994 Apple Macintosh LC/Performa 575
@source:apple/macquadra630.cpp
macqd630 // July 18, 1994 Apple Macintosh Quadra 630
maclc580 // April 3, 1995 Apple Macintosh LC/Performa 580
@source:apple/macquadra700.cpp
macqd700 // 1991 Apple Macintosh Quadra 700