mirror of
https://github.com/holub/mame
synced 2025-04-21 07:52:35 +03:00
machine/i2chle.cpp: New mix-in class derived from dimm_spd that allows any device to speak I2C. [R. Belmont]
machine/dimm_spd.cpp: Refactored using the i2chle mix-in. [R. Belmont] apple/valkyrie.cpp: Collected more information, fixed the display enable, and mixed in i2chle to set the pixel clock over I2C. [R. Belmont] apple/macquadra630.cpp: Hook I2C up between Cuda and Valkyrie. [R. Belmont]
This commit is contained in:
parent
340fbec1e4
commit
492b868f03
@ -1560,6 +1560,17 @@ if (MACHINES["1MB5"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--@src/devices/machine/i2cmem.h,MACHINES["I2CHLE"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["I2CHLE"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/i2chle.cpp",
|
||||
MAME_DIR .. "src/devices/machine/i2chle.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/i2cmem.h,MACHINES["I2CMEM"] = true
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
Each DIMM contains a small EEPROM with information about the capacity and
|
||||
timings of the module. The EEPROM speaks a version of I2C called SMBus.
|
||||
|
||||
This does not attempt to be a generalized I2C/SMBus solution.
|
||||
*/
|
||||
|
||||
|
||||
@ -19,35 +17,12 @@
|
||||
#define VERBOSE (0)
|
||||
#include "logmacro.h"
|
||||
|
||||
//**************************************************************************
|
||||
// DEVICE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
DEFINE_DEVICE_TYPE(DIMM_SPD, dimm_spd_device, "dimm_spd", "DIMM Serial Presence Detect")
|
||||
|
||||
constexpr int STATE_IDLE = 0;
|
||||
constexpr int STATE_GET_ADDRESS = 1;
|
||||
constexpr int STATE_GET_SUBADDRESS = 2;
|
||||
constexpr int STATE_READ_DATA = 3;
|
||||
constexpr int STATE_WAIT_ACK = 4;
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// dimm_spd_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
dimm_spd_device::dimm_spd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
||||
device_t(mconfig, DIMM_SPD, tag, owner, clock),
|
||||
write_sda(*this)
|
||||
i2c_hle_interface(mconfig, *this, 0) // address will be overridden by set_address as before
|
||||
{
|
||||
m_data_offset = 0;
|
||||
m_sda = m_scl = 1;
|
||||
m_state = m_state_next = STATE_IDLE;
|
||||
m_last_address = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
@ -68,21 +43,6 @@ void dimm_spd_device::device_start()
|
||||
m_data[7] = 0; // data bus width high byte
|
||||
m_data[11] = 0; // non-ECC (1=parity, 2=ECC)
|
||||
m_data[62] = 0x12; // SPD version 1.2
|
||||
|
||||
m_latch = m_bit = 0;
|
||||
m_state = STATE_IDLE;
|
||||
m_sda = m_scl = 1;
|
||||
m_last_address = 0;
|
||||
m_data_offset = 0;
|
||||
|
||||
save_item(NAME(m_latch));
|
||||
save_item(NAME(m_bit));
|
||||
save_item(NAME(m_state));
|
||||
save_item(NAME(m_state_next));
|
||||
save_item(NAME(m_data_offset));
|
||||
save_item(NAME(m_just_acked));
|
||||
|
||||
write_sda(1);
|
||||
}
|
||||
|
||||
void dimm_spd_device::set_dimm_size(dimm_size_t size)
|
||||
@ -138,191 +98,13 @@ void dimm_spd_device::set_dimm_size(dimm_size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void dimm_spd_device::device_reset()
|
||||
u8 dimm_spd_device::read_data(u16 offset)
|
||||
{
|
||||
}
|
||||
|
||||
void dimm_spd_device::sda_write(int state)
|
||||
{
|
||||
if (m_size == SIZE_SLOT_EMPTY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_sda != state)
|
||||
{
|
||||
m_sda = state & 1;
|
||||
|
||||
if (m_scl)
|
||||
{
|
||||
if (m_sda)
|
||||
{
|
||||
LOG("%s: stop\n", tag());
|
||||
m_state = STATE_IDLE;
|
||||
m_last_address = 0;
|
||||
m_just_acked = false;
|
||||
m_data_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: start\n", tag());
|
||||
m_state = STATE_GET_ADDRESS;
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dimm_spd_device::scl_write(int state)
|
||||
{
|
||||
if (m_size == SIZE_SLOT_EMPTY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_scl != state)
|
||||
{
|
||||
m_scl = state & 1;
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_IDLE:
|
||||
// just ignore everything until a START
|
||||
break;
|
||||
|
||||
case STATE_GET_ADDRESS:
|
||||
case STATE_GET_SUBADDRESS:
|
||||
if (m_bit < 8)
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
m_latch <<= 1;
|
||||
m_latch |= m_sda;
|
||||
m_bit++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
m_bit++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bit == 8)
|
||||
{
|
||||
if (m_state == STATE_GET_ADDRESS)
|
||||
{
|
||||
LOG("%s: Got address %02x (ours is %02x r/w %d)\n", tag(), m_latch >> 1, m_address, m_latch & 1);
|
||||
// check if reading
|
||||
if (m_latch & 1)
|
||||
{
|
||||
if ((m_latch >> 1) == m_address)
|
||||
{
|
||||
LOG("%s: address matches, ACKing\n", tag());
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_READ_DATA;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: address doesn't match, ignoring\n", tag());
|
||||
m_state = STATE_IDLE;
|
||||
write_sda(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: write: getting subaddress\n", tag());
|
||||
m_last_address = m_latch >> 1;
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_GET_SUBADDRESS;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
}
|
||||
else if (m_state == STATE_GET_SUBADDRESS)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: subaddress is %02x\n", tag(), m_latch);
|
||||
m_data_offset = m_latch;
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_IDLE; // is this correct?
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_WAIT_ACK:
|
||||
if (!m_scl)
|
||||
{
|
||||
m_state = m_state_next;
|
||||
write_sda(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READ_DATA:
|
||||
if (m_bit < 8)
|
||||
{
|
||||
if (!m_scl)
|
||||
{
|
||||
m_bit++;
|
||||
write_sda(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bit == 0)
|
||||
{
|
||||
m_latch = m_data[m_data_offset++];
|
||||
m_data_offset &= 0xff;
|
||||
LOGMASKED(LOG_DATAOUT, "%s: outputting byte %02x\n", tag(), m_latch);
|
||||
}
|
||||
|
||||
write_sda(BIT(m_latch, 7));
|
||||
m_latch <<= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
// did the master ACK or NACK?
|
||||
if (m_sda)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: master NACK\n", tag());
|
||||
m_state = STATE_IDLE;
|
||||
write_sda(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: master ACK\n", tag());
|
||||
m_just_acked = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write_sda(1);
|
||||
if (m_just_acked)
|
||||
{
|
||||
m_bit = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// i2c_hle_interface auto-increments the address but doesn't wrap it around
|
||||
if (offset >= m_size)
|
||||
{
|
||||
m_data_offset = 0;
|
||||
}
|
||||
|
||||
return m_data[offset];
|
||||
}
|
||||
|
@ -5,13 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// ======================> dimm_spd_device
|
||||
|
||||
class dimm_spd_device : public device_t
|
||||
#include "machine/i2chle.h"
|
||||
class dimm_spd_device : public device_t, public i2c_hle_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
@ -22,16 +17,6 @@ public:
|
||||
|
||||
dimm_spd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
// inline configuration helpers
|
||||
void set_address(u16 address) { m_address = address; }
|
||||
|
||||
auto sda_callback() { return write_sda.bind(); }
|
||||
|
||||
void sda_write(int state);
|
||||
void scl_write(int state);
|
||||
|
||||
devcb_write_line write_sda;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SIZE_SLOT_EMPTY = 0,
|
||||
@ -47,20 +32,14 @@ public:
|
||||
void set_dimm_size(dimm_size_t size);
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
// device_t overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
// i2c_hle_interface overrides
|
||||
virtual u8 read_data(u16 offset) override;
|
||||
virtual const char *get_tag() override { return tag(); }
|
||||
|
||||
private:
|
||||
u8 m_data[256];
|
||||
u8 m_latch;
|
||||
u8 m_bit;
|
||||
u16 m_address;
|
||||
u16 m_last_address;
|
||||
int m_sda, m_scl;
|
||||
u32 m_state, m_state_next;
|
||||
u16 m_data_offset;
|
||||
bool m_just_acked;
|
||||
dimm_size_t m_size;
|
||||
};
|
||||
|
||||
|
276
src/devices/machine/i2chle.cpp
Normal file
276
src/devices/machine/i2chle.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:R. Belmont
|
||||
/*
|
||||
I2C HLE mix-in
|
||||
by R. Belmont
|
||||
|
||||
A mix-in to allow devices to speak I2C without reimplementing
|
||||
the protocol themselves.
|
||||
|
||||
If the device returns data over I2C, override read_data.
|
||||
If the device is written data over I2C, override write_data.
|
||||
If you want the mix-in's logging to show your device name, override
|
||||
get_tag() with something that just returns your tag().
|
||||
|
||||
This mix-in will auto-increment the address on repeated reads/writes,
|
||||
it's up to your class that consumes this mix-in
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "i2chle.h"
|
||||
|
||||
#define LOG_DATAOUT (1U << 1)
|
||||
|
||||
#define VERBOSE (0)
|
||||
|
||||
#define LOG_OUTPUT_FUNC osd_printf_info
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
constexpr int STATE_IDLE = 0;
|
||||
constexpr int STATE_GET_ADDRESS = 1;
|
||||
constexpr int STATE_GET_SUBADDRESS = 2;
|
||||
constexpr int STATE_READ_DATA = 3;
|
||||
constexpr int STATE_WAIT_ACK = 4;
|
||||
constexpr int STATE_GET_WRITE_DATA = 5;
|
||||
|
||||
i2c_hle_interface::i2c_hle_interface(const machine_config &mconfig, device_t &device, u16 address) :
|
||||
device_interface(device, "i2chle"),
|
||||
write_sda(*this),
|
||||
m_address(address)
|
||||
{
|
||||
m_data_offset = 0;
|
||||
m_sda = m_scl = 1;
|
||||
m_state = m_state_next = STATE_IDLE;
|
||||
m_last_address = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
|
||||
i2c_hle_interface::~i2c_hle_interface()
|
||||
{
|
||||
}
|
||||
|
||||
void i2c_hle_interface::interface_post_start()
|
||||
{
|
||||
m_latch = m_bit = 0;
|
||||
m_state = STATE_IDLE;
|
||||
m_sda = m_scl = 1;
|
||||
m_last_address = 0;
|
||||
m_data_offset = 0;
|
||||
|
||||
device().save_item(NAME(m_latch));
|
||||
device().save_item(NAME(m_bit));
|
||||
device().save_item(NAME(m_state));
|
||||
device().save_item(NAME(m_state_next));
|
||||
device().save_item(NAME(m_data_offset));
|
||||
device().save_item(NAME(m_just_acked));
|
||||
|
||||
write_sda(1);
|
||||
}
|
||||
|
||||
void i2c_hle_interface::sda_write(int state)
|
||||
{
|
||||
if (m_sda != state)
|
||||
{
|
||||
m_sda = state & 1;
|
||||
|
||||
if (m_scl)
|
||||
{
|
||||
if (m_sda)
|
||||
{
|
||||
LOG("%s: stop\n", get_tag());
|
||||
m_state = STATE_IDLE;
|
||||
m_last_address = 0;
|
||||
m_just_acked = false;
|
||||
m_data_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: start\n", get_tag());
|
||||
m_state = STATE_GET_ADDRESS;
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void i2c_hle_interface::scl_write(int state)
|
||||
{
|
||||
if (m_scl != state)
|
||||
{
|
||||
m_scl = state & 1;
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case STATE_IDLE:
|
||||
// just ignore everything until a START
|
||||
break;
|
||||
|
||||
case STATE_GET_ADDRESS:
|
||||
case STATE_GET_SUBADDRESS:
|
||||
case STATE_GET_WRITE_DATA:
|
||||
if (m_bit < 8)
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
m_latch <<= 1;
|
||||
m_latch |= m_sda;
|
||||
m_bit++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
m_bit++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bit == 8)
|
||||
{
|
||||
if (m_state == STATE_GET_ADDRESS)
|
||||
{
|
||||
LOG("%s: Got address %02x (ours is %02x r/w %d)\n", get_tag(), m_latch >> 1, m_address, m_latch & 1);
|
||||
// check if reading
|
||||
if (m_latch & 1)
|
||||
{
|
||||
if ((m_latch >> 1) == m_address)
|
||||
{
|
||||
LOG("%s: address matches, ACKing\n", get_tag());
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_READ_DATA;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: address doesn't match, ignoring\n", get_tag());
|
||||
m_state = STATE_IDLE;
|
||||
write_sda(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_latch >> 1) == m_address)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: write: getting subaddress\n", get_tag());
|
||||
m_last_address = m_latch >> 1;
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_GET_SUBADDRESS;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("%s: address doesn't match, ignoring\n", get_tag());
|
||||
m_state = STATE_IDLE;
|
||||
write_sda(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_state == STATE_GET_SUBADDRESS)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: subaddress is %02x\n", get_tag(), m_latch);
|
||||
m_data_offset = m_latch;
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_GET_WRITE_DATA;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
else if (m_state == STATE_GET_WRITE_DATA)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: got write data %02x for address %02x\n", get_tag(), m_latch, m_data_offset);
|
||||
write_data(m_data_offset, m_latch);
|
||||
m_data_offset++;
|
||||
write_sda(0);
|
||||
m_bit = 0;
|
||||
m_latch = 0;
|
||||
m_state_next = STATE_GET_WRITE_DATA;
|
||||
m_state = STATE_WAIT_ACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_WAIT_ACK:
|
||||
if (!m_scl)
|
||||
{
|
||||
m_state = m_state_next;
|
||||
write_sda(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READ_DATA:
|
||||
if (m_bit < 8)
|
||||
{
|
||||
if (!m_scl)
|
||||
{
|
||||
m_bit++;
|
||||
write_sda(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bit == 0)
|
||||
{
|
||||
m_latch = read_data(m_data_offset);
|
||||
m_data_offset++;
|
||||
m_data_offset &= 0xff;
|
||||
LOGMASKED(LOG_DATAOUT, "%s: outputting byte %02x\n", get_tag(), m_latch);
|
||||
}
|
||||
|
||||
write_sda(BIT(m_latch, 7));
|
||||
m_latch <<= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_scl)
|
||||
{
|
||||
// did the master ACK or NACK?
|
||||
if (m_sda)
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: master NACK\n", get_tag());
|
||||
m_state = STATE_IDLE;
|
||||
write_sda(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGMASKED(LOG_DATAOUT, "%s: master ACK\n", get_tag());
|
||||
m_just_acked = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write_sda(1);
|
||||
if (m_just_acked)
|
||||
{
|
||||
m_bit = 0;
|
||||
m_just_acked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u8 i2c_hle_interface::read_data(u16 offset)
|
||||
{
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void i2c_hle_interface::write_data(u16 offset, u8 data)
|
||||
{
|
||||
}
|
||||
|
||||
static const char i2chle_name[] = "i2chle";
|
||||
const char *i2c_hle_interface::get_tag()
|
||||
{
|
||||
return i2chle_name;
|
||||
}
|
46
src/devices/machine/i2chle.h
Normal file
46
src/devices/machine/i2chle.h
Normal file
@ -0,0 +1,46 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:R. Belmont
|
||||
#ifndef MAME_MACHINE_I2CHLE_H
|
||||
#define MAME_MACHINE_I2CHLE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
class i2c_hle_interface : public device_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
i2c_hle_interface(const machine_config &mconfig, device_t &device, u16 address);
|
||||
virtual ~i2c_hle_interface();
|
||||
|
||||
void set_address(u16 address) { m_address = address; }
|
||||
|
||||
auto sda_callback() { return write_sda.bind(); }
|
||||
|
||||
void sda_write(int state);
|
||||
void scl_write(int state);
|
||||
|
||||
devcb_write_line write_sda;
|
||||
|
||||
protected:
|
||||
void interface_post_start() override ATTR_COLD;
|
||||
|
||||
// override this to read out data
|
||||
virtual u8 read_data(u16 offset);
|
||||
virtual void write_data(u16 offset, u8 data);
|
||||
|
||||
// override this to properly identify your device in logging
|
||||
virtual const char *get_tag();
|
||||
|
||||
u16 m_data_offset;
|
||||
|
||||
private:
|
||||
u8 m_latch;
|
||||
u8 m_bit;
|
||||
u16 m_address;
|
||||
u16 m_last_address;
|
||||
int m_sda, m_scl;
|
||||
u32 m_state, m_state_next;
|
||||
bool m_just_acked;
|
||||
};
|
||||
|
||||
#endif // MAME_MACHINE_I2CHLE_H
|
@ -43,6 +43,7 @@
|
||||
#include "bus/nubus/cards.h"
|
||||
#include "bus/nubus/nubus.h"
|
||||
#include "cpu/m68000/m68040.h"
|
||||
#include "machine/input_merger.h"
|
||||
#include "machine/ram.h"
|
||||
#include "machine/timer.h"
|
||||
|
||||
@ -161,7 +162,7 @@ void quadra630_state::macqd630(machine_config &config)
|
||||
m_primetimeii->set_maincpu_tag("maincpu");
|
||||
m_primetimeii->set_scsi_tag("f108:scsi:7:ncr53c96");
|
||||
|
||||
VALKYRIE(config, m_video, C32M);
|
||||
valkyrie_device &valkyrie(VALKYRIE(config, m_video, C32M));
|
||||
m_video->write_irq().set(m_primetimeii, FUNC(primetime_device::via2_irq_w<0x40>));
|
||||
|
||||
MACADB(config, m_macadb, C15M);
|
||||
@ -177,6 +178,15 @@ void quadra630_state::macqd630(machine_config &config)
|
||||
m_macadb->adb_data_callback().set(m_cuda, FUNC(cuda_device::set_adb_line));
|
||||
config.set_perfect_quantum(m_maincpu);
|
||||
|
||||
input_merger_device &sda_merger(INPUT_MERGER_ALL_HIGH(config, "sda"));
|
||||
sda_merger.output_handler().append(m_cuda, FUNC(cuda_device::set_iic_sda));
|
||||
|
||||
m_cuda->iic_sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<0>));
|
||||
m_cuda->iic_sda_callback().append(valkyrie, FUNC(valkyrie_device::sda_write));
|
||||
m_cuda->iic_scl_callback().set(valkyrie, FUNC(valkyrie_device::scl_write));
|
||||
|
||||
valkyrie.sda_callback().set(sda_merger, FUNC(input_merger_device::in_w<1>));
|
||||
|
||||
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));
|
||||
|
@ -1,12 +1,26 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:R. Belmont
|
||||
/*
|
||||
Apple "Valkyrie" - very low-cost video framebuffer
|
||||
Emulation by R. Belmont
|
||||
Apple "Valkyrie" - very low-cost video framebuffer
|
||||
Emulation by R. Belmont
|
||||
|
||||
This was the bonus awfulness in the Quadra 630/LC 580 machines. While the
|
||||
ROM software and MacOS don't support many monitor types, the Linux driver indicates
|
||||
that's more of a software problem.
|
||||
|
||||
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/video/fbdev/valkyriefb.c?h=v6.2.10
|
||||
|
||||
Also valkyrie.c in the Marathon Infinity open source release from Bungie. (They use Valkyrie's video upscaler
|
||||
to only have to render 320x240!)
|
||||
|
||||
TODO:
|
||||
- YUV video-in support
|
||||
- Determine if the I2C clock is internal or external
|
||||
|
||||
Video in buffer 0 is at 0xf9300000
|
||||
Video in buffer 1 is at 0xf9340000
|
||||
Both buffers are 320x240 with a stride of 1024 bytes, data is 16 bits per pixel (YUV?)
|
||||
|
||||
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"
|
||||
@ -15,6 +29,7 @@
|
||||
#define LOG_MODE (1U << 1)
|
||||
#define LOG_MONSENSE (1U << 2)
|
||||
#define LOG_RAMDAC (1U << 3)
|
||||
#define LOG_CLOCKGEN (1U << 4)
|
||||
|
||||
#define VERBOSE (0)
|
||||
#include "logmacro.h"
|
||||
@ -35,6 +50,7 @@ void valkyrie_device::map(address_map &map)
|
||||
|
||||
valkyrie_device::valkyrie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
|
||||
device_t(mconfig, VALKYRIE, tag, owner, clock),
|
||||
i2c_hle_interface(mconfig, *this, 0x28),
|
||||
m_vram_size(0x100000),
|
||||
m_pixel_clock(31334400),
|
||||
m_pal_address(0), m_pal_idx(0), m_mode(0),
|
||||
@ -43,8 +59,9 @@ valkyrie_device::valkyrie_device(const machine_config &mconfig, const char *tag,
|
||||
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)
|
||||
m_base(0), m_stride(1024), m_video_timing(0x80), m_int_status(0), m_hres(0), m_vres(0), m_htotal(0), m_vtotal(0),
|
||||
m_config(0),
|
||||
m_M(1), m_N(1), m_P(1)
|
||||
{
|
||||
}
|
||||
|
||||
@ -55,6 +72,7 @@ void valkyrie_device::device_start()
|
||||
m_vbl_timer = timer_alloc(FUNC(valkyrie_device::vbl_tick), this);
|
||||
m_vbl_timer->adjust(attotime::never);
|
||||
|
||||
save_item(NAME(m_video_timing));
|
||||
save_item(NAME(m_vram_offset));
|
||||
save_item(NAME(m_mode));
|
||||
save_item(NAME(m_monitor_id));
|
||||
@ -69,6 +87,9 @@ void valkyrie_device::device_start()
|
||||
save_item(NAME(m_pixel_clock));
|
||||
save_item(NAME(m_config));
|
||||
save_item(NAME(m_int_status));
|
||||
save_item(NAME(m_M));
|
||||
save_item(NAME(m_N));
|
||||
save_item(NAME(m_P));
|
||||
save_pointer(NAME(m_vram), m_vram_size);
|
||||
|
||||
machine().save().register_postload(save_prepost_delegate(FUNC(valkyrie_device::recalc_mode), this));
|
||||
@ -76,7 +97,15 @@ void valkyrie_device::device_start()
|
||||
|
||||
void valkyrie_device::device_reset()
|
||||
{
|
||||
m_enable = false;
|
||||
m_video_timing = 0x80;
|
||||
|
||||
// clear the MAME default palette so we don't get a weird red screen on initial startup
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void valkyrie_device::device_add_mconfig(machine_config &config)
|
||||
@ -98,9 +127,10 @@ 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(0x06, u8"Mac Hi-Res Display (12-14\" 640\u00d7480)") // "High Res" 640x480 @ 67 Hz
|
||||
PORT_CONFSETTING(ext(1, 1, 3), "640x480 VGA") // VGA 640x480 @ 60 Hz
|
||||
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
|
||||
@ -113,7 +143,7 @@ u32 valkyrie_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap,
|
||||
auto const vram8 = util::big_endian_cast<u8 const>(&m_vram[0]) + 0x1000;
|
||||
const pen_t *pens = m_palette->pens();
|
||||
|
||||
if (!m_enable)
|
||||
if (m_video_timing & 0x80)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -209,7 +239,6 @@ u32 valkyrie_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap,
|
||||
|
||||
u8 valkyrie_device::regs_r(offs_t offset)
|
||||
{
|
||||
// printf("Read regs @ %x\n", offset);
|
||||
switch (offset)
|
||||
{
|
||||
case 0:
|
||||
@ -263,8 +292,12 @@ void valkyrie_device::regs_w(offs_t offset, u8 data)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0: // video timing select (apparently from hardcoded values!)
|
||||
m_video_timing = data;
|
||||
case 0: // video mode. hardcoded!
|
||||
m_video_timing = data & 0xff;
|
||||
if (!(data & 0x80))
|
||||
{
|
||||
recalc_mode();
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // video depth: 0=1bpp, 1=2bpp, 2=4bpp, 3=8bpp, 4=16bpp
|
||||
@ -272,7 +305,7 @@ void valkyrie_device::regs_w(offs_t offset, u8 data)
|
||||
m_mode = data & 7;
|
||||
break;
|
||||
|
||||
case 0xc: // written to lock in the video timing from register 0
|
||||
case 0xc: // subsystem configuration register
|
||||
if (data == 0x1)
|
||||
{
|
||||
recalc_mode();
|
||||
@ -292,15 +325,6 @@ void valkyrie_device::regs_w(offs_t offset, u8 data)
|
||||
// at startup, the screen isn't wanted on until 0x81 is written.
|
||||
// if the Video Startup extension is installed, it later sets this to 0x02.
|
||||
if ((data & 0x80) || (data == 0x02))
|
||||
{
|
||||
m_enable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enable = false;
|
||||
}
|
||||
// start VBL interrupts when the screen is enabled
|
||||
if (m_enable)
|
||||
{
|
||||
m_vbl_timer->adjust(m_screen->time_until_pos(m_vres, 0), 0);
|
||||
}
|
||||
@ -315,6 +339,33 @@ void valkyrie_device::regs_w(offs_t offset, u8 data)
|
||||
LOGMASKED(LOG_MONSENSE, "%x to sense drive\n", data & 0xf);
|
||||
break;
|
||||
|
||||
case 0x20: // video in control
|
||||
break;
|
||||
|
||||
case 0x60: // video window X position
|
||||
case 0x61:
|
||||
break;
|
||||
|
||||
case 0x64: // video window Y position
|
||||
case 0x65:
|
||||
break;
|
||||
|
||||
case 0x70: // video window width
|
||||
case 0x71:
|
||||
break;
|
||||
|
||||
case 0x74: // video window height
|
||||
case 0x75:
|
||||
break;
|
||||
|
||||
case 0x80: // video field starting X pixel
|
||||
case 0x81:
|
||||
break;
|
||||
|
||||
case 0x84: // video field starting Y pixel
|
||||
case 0x85:
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG("Valkyrie: Unk write %08x @ %x\n", data, offset<<2);
|
||||
break;
|
||||
@ -396,48 +447,59 @@ void valkyrie_device::ramdac_w(offs_t offset, u32 data)
|
||||
void valkyrie_device::recalc_mode()
|
||||
{
|
||||
// mode parameters taken from the Quadra 630 Developer Note
|
||||
switch (m_video_timing)
|
||||
{
|
||||
case 0x01: // default
|
||||
case 0x86: // 13" 640x480
|
||||
m_hres = 640;
|
||||
m_vres = 480;
|
||||
m_htotal = 864;
|
||||
m_vtotal = 525;
|
||||
m_pixel_clock = 30240000;
|
||||
m_stride = 80;
|
||||
break;
|
||||
// m_video_timing numbers:
|
||||
// 2 = 512x384, 60 Hz ("Rubik")
|
||||
// 6 = 640x480, 72 Hz (Mac 13")
|
||||
// 9 = 832x624, 75 Hz (Mac 16")
|
||||
// 11 = 640x480, 60 Hz (VGA)
|
||||
// 12 = 800x600, 60 Hz (SVGA)
|
||||
// 13 = 800x600, 72 Hz (SVGA)
|
||||
// 14 = 1024x768, 60 Hz (SVGA)
|
||||
// 15 = 1024x768, 72 Hz (SVGA)
|
||||
|
||||
case 0x82: // Rubik 512x384
|
||||
if (m_video_timing & 0x80)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_video_timing & 0x7f)
|
||||
{
|
||||
case 2: // Rubik 512x384
|
||||
m_hres = 512;
|
||||
m_vres = 384;
|
||||
m_htotal = 640;
|
||||
m_vtotal = 407;
|
||||
m_pixel_clock = 15670000;
|
||||
m_stride = 64;
|
||||
break;
|
||||
|
||||
case 0x8b: // VGA 640x480
|
||||
case 6: // 13" 640x480
|
||||
m_hres = 640;
|
||||
m_vres = 480;
|
||||
m_htotal = 800;
|
||||
m_htotal = 864;
|
||||
m_vtotal = 525;
|
||||
m_pixel_clock = 25180000;
|
||||
m_stride = 80;
|
||||
break;
|
||||
|
||||
case 0x89: // 16" RGB 832x624?
|
||||
case 9: // 16" RGB 832x624?
|
||||
m_hres = 832;
|
||||
m_vres = 624;
|
||||
m_htotal = 1072;
|
||||
m_vtotal = 690;
|
||||
m_pixel_clock = 50000000;
|
||||
m_stride = 104;
|
||||
break;
|
||||
|
||||
case 11: // VGA 640x480
|
||||
m_hres = 640;
|
||||
m_vres = 480;
|
||||
m_htotal = 800;
|
||||
m_vtotal = 525;
|
||||
m_stride = 80;
|
||||
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);
|
||||
@ -474,3 +536,38 @@ TIMER_CALLBACK_MEMBER(valkyrie_device::vbl_tick)
|
||||
|
||||
m_vbl_timer->adjust(m_screen->time_until_pos(480, 0), 0);
|
||||
}
|
||||
|
||||
void valkyrie_device::write_data(u16 offset, u8 data)
|
||||
{
|
||||
switch (offset)
|
||||
{
|
||||
case 0:
|
||||
return;
|
||||
|
||||
case 1:
|
||||
m_M = data;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_N = data;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
m_P = data;
|
||||
break;
|
||||
}
|
||||
|
||||
const double clock = (3986400.0f * (double)(1 << m_P) * (double)m_N) / (double)m_M;
|
||||
m_pixel_clock = (u32)clock;
|
||||
|
||||
// TODO: Apple documents these machines as supporting the 512x384 monitor, but selecting it
|
||||
// programs garbage to the clock generator. Allow that configuration to work until we determine
|
||||
// if the Apple developer note is lying.
|
||||
if ((m_M == 0) && (m_N == 0) && (m_P = 98))
|
||||
{
|
||||
m_pixel_clock = 15670000;
|
||||
}
|
||||
|
||||
LOGMASKED(LOG_CLOCKGEN, "Valkyrie: M = %d %02x, N = %d %02x P = %d, pixel clock %d\n", m_M, m_M, m_N, m_N, m_P, m_pixel_clock);
|
||||
recalc_mode();
|
||||
}
|
||||
|
@ -7,11 +7,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "cpu/m68000/m68040.h"
|
||||
#include "machine/i2chle.h"
|
||||
|
||||
#include "emupal.h"
|
||||
#include "screen.h"
|
||||
|
||||
class valkyrie_device : public device_t
|
||||
class valkyrie_device : public device_t, public i2c_hle_interface
|
||||
{
|
||||
public:
|
||||
valkyrie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
@ -22,10 +23,14 @@ public:
|
||||
auto write_irq() { return m_irq.bind(); }
|
||||
|
||||
protected:
|
||||
// device_t overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
// i2c_hle_interface overrides
|
||||
virtual ioport_constructor device_input_ports() const override;
|
||||
virtual void write_data(u16 offset, u8 data) override;
|
||||
virtual const char *get_tag() override { return tag(); }
|
||||
|
||||
void recalc_ints();
|
||||
void recalc_mode();
|
||||
@ -48,7 +53,7 @@ private:
|
||||
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;
|
||||
u8 m_M, m_N, m_P;
|
||||
|
||||
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user