From 492b868f03b65ad8622c9ff3493425619caa4f5a Mon Sep 17 00:00:00 2001 From: arbee Date: Sun, 19 May 2024 12:34:31 -0400 Subject: 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] --- scripts/src/machine.lua | 11 ++ src/devices/machine/dimm_spd.cpp | 230 +------------------------------- src/devices/machine/dimm_spd.h | 33 +---- src/devices/machine/i2chle.cpp | 276 +++++++++++++++++++++++++++++++++++++++ src/devices/machine/i2chle.h | 46 +++++++ src/mame/apple/macquadra630.cpp | 12 +- src/mame/apple/valkyrie.cpp | 179 +++++++++++++++++++------ src/mame/apple/valkyrie.h | 9 +- 8 files changed, 501 insertions(+), 295 deletions(-) create mode 100644 src/devices/machine/i2chle.cpp create mode 100644 src/devices/machine/i2chle.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 3953786b204..ace32673389 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -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 diff --git a/src/devices/machine/dimm_spd.cpp b/src/devices/machine/dimm_spd.cpp index b6736e71ade..54788c3d977 100644 --- a/src/devices/machine/dimm_spd.cpp +++ b/src/devices/machine/dimm_spd.cpp @@ -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() -{ -} - -void dimm_spd_device::sda_write(int state) +u8 dimm_spd_device::read_data(u16 offset) { - if (m_size == SIZE_SLOT_EMPTY) + // i2c_hle_interface auto-increments the address but doesn't wrap it around + if (offset >= m_size) { - return; + m_data_offset = 0; } - 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; - } - - } + return m_data[offset]; } diff --git a/src/devices/machine/dimm_spd.h b/src/devices/machine/dimm_spd.h index 7fc42844bd5..0160e392e28 100644 --- a/src/devices/machine/dimm_spd.h +++ b/src/devices/machine/dimm_spd.h @@ -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; }; diff --git a/src/devices/machine/i2chle.cpp b/src/devices/machine/i2chle.cpp new file mode 100644 index 00000000000..1e9f6cc0f59 --- /dev/null +++ b/src/devices/machine/i2chle.cpp @@ -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; +} diff --git a/src/devices/machine/i2chle.h b/src/devices/machine/i2chle.h new file mode 100644 index 00000000000..0e6d8b859ef --- /dev/null +++ b/src/devices/machine/i2chle.h @@ -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 diff --git a/src/mame/apple/macquadra630.cpp b/src/mame/apple/macquadra630.cpp index 472fdead1b1..b742e84d0bc 100644 --- a/src/mame/apple/macquadra630.cpp +++ b/src/mame/apple/macquadra630.cpp @@ -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)); diff --git a/src/mame/apple/valkyrie.cpp b/src/mame/apple/valkyrie.cpp index b91c53224f4..2fab3518d37 100644 --- a/src/mame/apple/valkyrie.cpp +++ b/src/mame/apple/valkyrie.cpp @@ -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(&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) + // 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) + + if (m_video_timing & 0x80) { - 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; + return; + } - case 0x82: // Rubik 512x384 + 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(); +} diff --git a/src/mame/apple/valkyrie.h b/src/mame/apple/valkyrie.h index cd815c2265e..77cf17b3b7e 100644 --- a/src/mame/apple/valkyrie.h +++ b/src/mame/apple/valkyrie.h @@ -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); -- cgit v1.2.3