diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 29cb6c48970..8f372635c7b 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1761,6 +1761,18 @@ if (MACHINES["LINFLASH"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/m3002.h,MACHINES["M3002"] = true +--------------------------------------------------- + +if (MACHINES["M3002"]~=null) then + files { + MAME_DIR .. "src/devices/machine/m3002.cpp", + MAME_DIR .. "src/devices/machine/m3002.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/m6m80011ap.h,MACHINES["M6M80011AP"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 854b8977c59..1c44d5df467 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -514,6 +514,7 @@ MACHINES["LDVP931"] = true MACHINES["LINFLASH"] = true MACHINES["LPCI"] = true --MACHINES["LSI53C810"] = true +--MACHINES["M3002"] = true --MACHINES["M68307"] = true --MACHINES["M68340"] = true MACHINES["M6M80011AP"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 6b427024704..139f02cb7a6 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -536,6 +536,7 @@ MACHINES["LH5810"] = true MACHINES["LINFLASH"] = true MACHINES["LPCI"] = true MACHINES["LSI53C810"] = true +MACHINES["M3002"] = true MACHINES["M68307"] = true MACHINES["M68340"] = true MACHINES["M6M80011AP"] = true diff --git a/src/devices/machine/m3002.cpp b/src/devices/machine/m3002.cpp new file mode 100644 index 00000000000..2416dc4afd6 --- /dev/null +++ b/src/devices/machine/m3002.cpp @@ -0,0 +1,490 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/********************************************************************** + + EM Microelectronic-Marin (µem) M 3002 Real Time Clock + + TODO: + - Pulse output (256 Hz, second, minute, hour) + - Test mode + +**********************************************************************/ + +#include "emu.h" +#include "m3002.h" + +#define VERBOSE 1 +#include "logmacro.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(M3002, m3002_device, "m3002", "EM M 3002 Real Time Clock") + +//************************************************************************** +// DEVICE CONSTRUCTION AND INITIALIZATION +//************************************************************************** + +ALLOW_SAVE_TYPE(m3002_device::mux_state); + +//------------------------------------------------- +// m3002_device - constructor +//------------------------------------------------- + +m3002_device::m3002_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, M3002, tag, owner, clock) + , device_nvram_interface(mconfig, *this) + , device_rtc_interface(mconfig, *this) + , m_irq_callback(*this) + , m_address(0) + , m_mux_state(mux_state::INIT) + , m_irq_active(false) + , m_update_deferred(false) + , m_second_timer(nullptr) +{ + std::fill_n(&m_ram[0], 0x10, 0); +} + + +//------------------------------------------------- +// device_resolve_objects - resolve objects that +// may be needed for other devices to set +// initial conditions at start time +//------------------------------------------------- + +void m3002_device::device_resolve_objects() +{ + m_irq_callback.resolve_safe(); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void m3002_device::device_start() +{ + // Setup timer + m_second_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(m3002_device::second_timer), this)); + + // Save internal state + save_item(NAME(m_ram)); + save_item(NAME(m_address)); + save_item(NAME(m_mux_state)); + save_item(NAME(m_irq_active)); + save_item(NAME(m_update_deferred)); +} + + +//------------------------------------------------- +// device_clock_changed - called when the +// device clock is altered in any way +//------------------------------------------------- + +void m3002_device::device_clock_changed() +{ + attotime second = clocks_to_attotime(32768); + m_second_timer->adjust(second, 0, second); +} + + +//------------------------------------------------- +// nvram_read - read NVRAM from the file +//------------------------------------------------- + +void m3002_device::nvram_read(emu_file &file) +{ + file.read(&m_ram[0], 0x10); +} + + +//------------------------------------------------- +// nvram_write - write NVRAM to the file +//------------------------------------------------- + +void m3002_device::nvram_write(emu_file &file) +{ + file.write(&m_ram[0], 0x10); +} + + +//------------------------------------------------- +// nvram_default - initialize NVRAM to its +// default state +//------------------------------------------------- + +void m3002_device::nvram_default() +{ + // Set watch, alarm and timer registers to minimum values + m_ram[0x0] = m_ram[0x8] = m_ram[0xc] = 0x00; + m_ram[0x1] = m_ram[0x9] = m_ram[0xd] = 0x00; + m_ram[0x2] = m_ram[0xa] = m_ram[0xe] = 0x00; + m_ram[0x3] = m_ram[0xb] = 0x01; + m_ram[0x4] = 0x01; + m_ram[0x5] = 0x00; + m_ram[0x6] = 0x01; + m_ram[0x7] = 0x01; + + // Allow watch to operate + m_ram[0xf] = 0x01; +} + + +//------------------------------------------------- +// rtc_clock_updated - set emulated time +//------------------------------------------------- + +void m3002_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) +{ + if (!BIT(m_ram[0xf], 0)) + return; + + m_ram[0x0] = convert_to_bcd(second); + m_ram[0x1] = convert_to_bcd(minute); + m_ram[0x2] = convert_to_bcd(hour); + m_ram[0x3] = convert_to_bcd(day); + m_ram[0x4] = convert_to_bcd(month); + m_ram[0x5] = convert_to_bcd(year); + m_ram[0x6] = convert_to_bcd(day_of_week); + + unsigned day_of_year = day - 1; + for (int n = 1; n < month; n++) + { + if (n == 2) + day_of_year += (year % 4) == 0 ? 29 : 28; + else + day_of_year += BIT(0xa50, n) ? 30 : 31; + } + m_ram[0x7] = convert_to_bcd((day_of_year + 7 - day_of_week) / 7 + 1); +} + + +//************************************************************************** +// INTERNAL UPDATE CYCLES +//************************************************************************** + +//------------------------------------------------- +// internal_busy - return true if in the middle +// of a once-per-second update +//------------------------------------------------- + +bool m3002_device::internal_busy() const +{ + // Each internal update lasts between 0.73 ms and 6 ms + return attotime_to_clocks(m_second_timer->remaining()) < 239; +} + + +//------------------------------------------------- +// bcd_increment - increment the value of one RAM +// location using BCD sequence +//------------------------------------------------- + +void m3002_device::bcd_increment(u8 location) +{ + if ((m_ram[location] & 0x09) != 0x09) + m_ram[location]++; + else + { + if ((m_ram[location] & 0x90) != 0x90) + m_ram[location] = (m_ram[location] & 0xf0) + 0x10; + else + m_ram[location] = 0x00; + } +} + + +//------------------------------------------------- +// max_date - return the maximum date (BCD) in +// the current month +//------------------------------------------------- + +u8 m3002_device::max_date() const +{ + if (m_ram[4] == 0x02) + return (m_ram[5] & 0x03) == 0x00 ? 0x29 : 0x28; + else + return BIT(0x20250, m_ram[4]) ? 0x30 : 0x31; +} + + +//------------------------------------------------- +// watch_update - update watch registers +//------------------------------------------------- + +void m3002_device::watch_update() +{ + // Count seconds + if (m_ram[0x0] != 0x59) + { + bcd_increment(0x0); + return; + } + m_ram[0x0] = 0x00; + + // Count minutes + if (m_ram[0x1] != 0x59) + { + bcd_increment(0x1); + return; + } + m_ram[0x1] = 0x00; + + // Count hours + if (m_ram[0x2] != 0x23) + { + bcd_increment(0x2); + return; + } + m_ram[0x2] = 0x00; + + // Count weekdays + if (m_ram[0x6] != 0x07) + bcd_increment(0x6); + else + { + m_ram[0x6] = 0x01; + + // Count weeks + bcd_increment(0x7); + } + + // Count date + if (m_ram[0x3] != max_date()) + { + bcd_increment(0x3); + return; + } + m_ram[0x3] = 0x01; + + // Count months + if (m_ram[0x4] != 0x12) + { + bcd_increment(0x4); + return; + } + m_ram[0x4] = 0x01; + m_ram[0x7] = 0x01; + + // Count years + bcd_increment(5); +} + + +//------------------------------------------------- +// alarm_update - compare alarm registers against +// watch registers +//------------------------------------------------- + +void m3002_device::alarm_update() +{ + // Compare seconds, minutes, hours, date + for (int r = 0x0; r <= 0x3; r++) + if (m_ram[0x8 + r] != 0xff && m_ram[0x8 + r] != m_ram[r]) + return; + + // Alarm IRQ + m_ram[0xf] |= 0x04; +} + + +//------------------------------------------------- +// timer_update - update timer registers +//------------------------------------------------- + +void m3002_device::timer_update() +{ + // Count seconds + if (m_ram[0xc] != 0x59) + { + bcd_increment(0xc); + return; + } + m_ram[0xc] = 0x00; + + // Count minutes + if (m_ram[0xd] != 0x59) + { + bcd_increment(0xd); + return; + } + m_ram[0xd] = 0x00; + + // Count hours + if (m_ram[0xe] != 0x23) + { + bcd_increment(0xe); + return; + } + m_ram[0xe] = 0x00; + + // Timer IRQ (23:59:59 -> 00:00:00) + m_ram[0xf] |= 0x08; +} + + +//------------------------------------------------- +// irq_update - update IRQ output +//------------------------------------------------- + +void m3002_device::irq_update() +{ + if (!m_irq_active && (m_ram[0xf] & 0x0c) != 0) + { + LOG("IRQ occurred\n"); + m_irq_active = true; + m_irq_callback(ASSERT_LINE); + } + else if (m_irq_active && (m_ram[0xf] & 0x0c) == 0) + { + LOG("IRQ cleared\n"); + m_irq_active = false; + m_irq_callback(CLEAR_LINE); + } +} + + +//------------------------------------------------- +// update - perform one full internal update +//------------------------------------------------- + +void m3002_device::update() +{ + if (BIT(m_ram[0xf], 0)) + { + watch_update(); + if (BIT(m_ram[0xf], 1)) + alarm_update(); + } + + if (BIT(m_ram[0xf], 4)) + timer_update(); + + irq_update(); +} + + +//------------------------------------------------- +// second_timer - called once each second +//------------------------------------------------- + +TIMER_CALLBACK_MEMBER(m3002_device::second_timer) +{ + if (m_update_deferred) + { + m_update_deferred = false; + update(); + } + else if (m_mux_state != mux_state::INIT) + m_update_deferred = true; + + if (!m_update_deferred) + update(); +} + +//************************************************************************** +// MICROPROCESSOR ACCESS +//************************************************************************** + +const char *const m3002_device::s_register_names[0x10] = +{ + "watch seconds", + "watch minutes", + "watch hours", + "watch date", + "watch month", + "watch year", + "watch weekday", + "watch week", + "alarm seconds", + "alarm minutes", + "alarm hours", + "alarm date", + "timer seconds", + "timer minutes", + "timer hours", + "status/control" +}; + +//------------------------------------------------- +// set_init_state - return the multiplexer to the +// initial state +//------------------------------------------------- + +void m3002_device::set_init_state() +{ + m_mux_state = mux_state::INIT; + if (m_update_deferred) + { + m_update_deferred = false; + update(); + } + irq_update(); +} + + +//------------------------------------------------- +// read - read data or busy status using 3-step +// access sequence +//------------------------------------------------- + +u8 m3002_device::read() +{ + u8 data = 0x0; + + switch (m_mux_state) + { + case mux_state::INIT: + if (internal_busy()) + data = 0xf; + break; + + case mux_state::TENS: + data = m_ram[m_address] >> 4; + if (!machine().side_effects_disabled()) + m_mux_state = mux_state::UNITS; + break; + + case mux_state::UNITS: + data = m_ram[m_address] & 0x0f; + if (!machine().side_effects_disabled()) + { + LOG("%s: Read %02X from %s register\n", machine().describe_context(), m_ram[m_address], s_register_names[m_address]); + set_init_state(); + } + break; + } + + return data; +} + + +//------------------------------------------------- +// write - write address and/or data using 3-step +// access sequence +//------------------------------------------------- + +void m3002_device::write(u8 data) +{ + data &= 0xf; + + switch (m_mux_state) + { + case mux_state::INIT: + m_address = data; + m_mux_state = mux_state::TENS; + break; + + case mux_state::TENS: + m_ram[m_address] = (data << 4) | (m_ram[m_address] & 0x0f); + m_mux_state = mux_state::UNITS; + break; + + case mux_state::UNITS: + m_ram[m_address] = (m_ram[m_address] & 0xf0) | data; + LOG("%s: Writing %02X to %s register\n", machine().describe_context(), m_ram[m_address], s_register_names[m_address]); + set_init_state(); + break; + } +} diff --git a/src/devices/machine/m3002.h b/src/devices/machine/m3002.h new file mode 100644 index 00000000000..d75d5e99c70 --- /dev/null +++ b/src/devices/machine/m3002.h @@ -0,0 +1,101 @@ +// license:BSD-3-Clause +// copyright-holders:AJR +/********************************************************************** + + EM Microelectronic-Marin (µem) M 3002 Real Time Clock + +*********************************************************************** + ____ ____ + Vbb 1 |* \__/ | 16 Vdd + XI 2 | | 15 _PULSE + XO 3 | | 14 _BUSY + _SYNC 4 | | 13 _IRQ + R/_W 5 | M 3002 | 12 I/O 3 + _OE 6 | | 11 I/O 2 + _CS 7 | | 10 I/O 1 + Vss 8 |____________| 9 I/O 0 + +**********************************************************************/ + +#ifndef MAME_MACHINE_M3002_H +#define MAME_MACHINE_M3002_H + +#pragma once + +#include "dirtc.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> m3002_device + +class m3002_device : public device_t, public device_nvram_interface, public device_rtc_interface +{ +public: + // device type constructor + m3002_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + // 4-bit read/write handlers + u8 read(); + void write(u8 data); + + // status output polling + DECLARE_READ_LINE_MEMBER(busy_r) { return internal_busy() ? 0 : 1; } + DECLARE_READ_LINE_MEMBER(irq_r) { return m_irq_active ? 0 : 1; } + +protected: + // device-level overrides + virtual void device_resolve_objects() override; + virtual void device_start() override; + virtual void device_clock_changed() override; + + // device_nvram_interface overrides + virtual void nvram_read(emu_file &file) override; + virtual void nvram_write(emu_file &file) override; + virtual void nvram_default() override; + + // device_rtc_interface overrides + virtual bool rtc_feature_y2k() const override { return false; } + virtual bool rtc_feature_leap_year() const override { return true; } + virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override; + +private: + static const char *const s_register_names[0x10]; + + enum class mux_state : u8 + { + INIT, + TENS, + UNITS + }; + + // internal helpers + bool internal_busy() const; + void bcd_increment(u8 location); + u8 max_date() const; + void watch_update(); + void alarm_update(); + void timer_update(); + void irq_update(); + void update(); + TIMER_CALLBACK_MEMBER(second_timer); + void set_init_state(); + + // callback objects + devcb_write_line m_irq_callback; + + // internal state + u8 m_ram[16]; + u8 m_address; + mux_state m_mux_state; + bool m_irq_active; + bool m_update_deferred; + emu_timer *m_second_timer; +}; + +// device type declaration +DECLARE_DEVICE_TYPE(M3002, m3002_device) + +#endif // MAME_MACHINE_M3002_H diff --git a/src/mame/drivers/europc.cpp b/src/mame/drivers/europc.cpp index 63a09bb56bf..572177a0005 100644 --- a/src/mame/drivers/europc.cpp +++ b/src/mame/drivers/europc.cpp @@ -34,7 +34,7 @@ #include "bus/isa/aga.h" #include "bus/isa/fdc.h" #include "machine/genpc.h" -#include "machine/nvram.h" +#include "machine/m3002.h" #include "machine/pckeybrd.h" #include "machine/ram.h" @@ -50,6 +50,7 @@ public: m_mb(*this, "mb"), m_keyboard(*this, "pc_keyboard"), m_ram(*this, RAM_TAG), + m_rtc(*this, "rtc"), m_jim_state(0), m_port61(0) { } @@ -65,6 +66,7 @@ private: required_device m_mb; required_device m_keyboard; required_device m_ram; + required_device m_rtc; DECLARE_WRITE8_MEMBER( europc_pio_w ); DECLARE_READ8_MEMBER( europc_pio_r ); @@ -73,26 +75,11 @@ private: DECLARE_READ8_MEMBER ( europc_jim_r ); DECLARE_READ8_MEMBER ( europc_jim2_r ); - DECLARE_READ8_MEMBER( europc_rtc_r ); - DECLARE_WRITE8_MEMBER( europc_rtc_w ); - - void europc_rtc_set_time(); - uint8_t m_jim_data[16]; uint8_t m_jim_state; isa8_aga_device::mode_t m_jim_mode; int m_port61; // bit 0,1 must be 0 for startup; reset? - uint8_t m_rtc_data[0x10]; - int m_rtc_reg; - int m_rtc_state; - void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; - emu_timer* m_rtc_timer; - - enum - { - TIMER_RTC - }; void europc_io(address_map &map); void europc_map(address_map &map); }; @@ -208,7 +195,7 @@ WRITE8_MEMBER( europc_pc_state::europc_jim_w ) } break; case 0xa: - europc_rtc_w(space, 0, data); + m_rtc->write(data); return; } logerror("jim write %.2x %.2x\n", offset, data); @@ -222,7 +209,7 @@ READ8_MEMBER( europc_pc_state::europc_jim_r ) { case 4: case 5: case 6: case 7: data = m_jim_data[offset]; break; case 0: case 1: case 2: case 3: data = 0; break; - case 0xa: return europc_rtc_r(space, 0); + case 0xa: return m_rtc->read(); } return data; } @@ -271,99 +258,6 @@ READ8_MEMBER( europc_pc_state::europc_jim2_r ) reg 0f: 01 status ok, when not 01 written */ -void europc_pc_state::europc_rtc_set_time() -{ - system_time systime; - - /* get the current date/time from the core */ - machine().current_datetime(systime); - - m_rtc_data[0] = dec_2_bcd(systime.utc_time.second); - m_rtc_data[1] = dec_2_bcd(systime.utc_time.minute); - m_rtc_data[2] = dec_2_bcd(systime.utc_time.hour); - - m_rtc_data[3] = dec_2_bcd(systime.utc_time.mday); - m_rtc_data[4] = dec_2_bcd(systime.utc_time.month + 1); - m_rtc_data[5] = dec_2_bcd(systime.utc_time.year % 100); -} - -void europc_pc_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - int month, year; - - switch(id) - { - case TIMER_RTC: - m_rtc_data[0]=bcd_adjust(m_rtc_data[0]+1); - if (m_rtc_data[0]>=0x60) - { - m_rtc_data[0]=0; - m_rtc_data[1]=bcd_adjust(m_rtc_data[1]+1); - if (m_rtc_data[1]>=0x60) - { - m_rtc_data[1]=0; - m_rtc_data[2]=bcd_adjust(m_rtc_data[2]+1); - if (m_rtc_data[2]>=0x24) - { - m_rtc_data[2]=0; - m_rtc_data[3]=bcd_adjust(m_rtc_data[3]+1); - month=bcd_2_dec(m_rtc_data[4]); - year=bcd_2_dec(m_rtc_data[5])+2000; // save for julian_days_in_month_calculation - if (m_rtc_data[3]> gregorian_days_in_month(month, year)) - { - m_rtc_data[3]=1; - m_rtc_data[4]=bcd_adjust(m_rtc_data[4]+1); - if (m_rtc_data[4]>0x12) - { - m_rtc_data[4]=1; - m_rtc_data[5]=bcd_adjust(m_rtc_data[5]+1)&0xff; - } - } - } - } - } - break; - } -} - -READ8_MEMBER( europc_pc_state::europc_rtc_r ) -{ - int data=0; - switch (m_rtc_state) - { - case 1: - data=(m_rtc_data[m_rtc_reg]&0xf0)>>4; - m_rtc_state++; - break; - case 2: - data=m_rtc_data[m_rtc_reg]&0xf; - m_rtc_state=0; -// logerror("rtc read %x %.2x\n",m_rtc_reg, m_rtc_data[m_rtc_reg]); - break; - } - return data; -} - -WRITE8_MEMBER( europc_pc_state::europc_rtc_w ) -{ - switch (m_rtc_state) - { - case 0: - m_rtc_reg=data; - m_rtc_state=1; - break; - case 1: - m_rtc_data[m_rtc_reg]=(m_rtc_data[m_rtc_reg]&~0xf0)|((data&0xf)<<4); - m_rtc_state++; - break; - case 2: - m_rtc_data[m_rtc_reg]=(m_rtc_data[m_rtc_reg]&~0xf)|(data&0xf); - m_rtc_state=0; -// logerror("rtc written %x %.2x\n",m_rtc_reg, m_rtc_data[m_rtc_reg]); - break; - } -} - void europc_pc_state::init_europc() { uint8_t *rom = &memregion("bios")->base()[0]; @@ -380,17 +274,6 @@ void europc_pc_state::init_europc() a += rom[i]; rom[0xffff] = 256 - a; } - - memset(&m_rtc_data,0,sizeof(m_rtc_data)); - m_rtc_reg = 0; - m_rtc_state = 0; - m_rtc_data[0xf]=1; - - m_rtc_timer = timer_alloc(); - m_rtc_timer->adjust(attotime::zero, 0, attotime(1,0)); - // europc_rtc_set_time(); - - subdevice("nvram")->set_base(m_rtc_data, sizeof(m_rtc_data)); } WRITE8_MEMBER( europc_pc_state::europc_pio_w ) @@ -591,7 +474,7 @@ void europc_pc_state::europc(machine_config &config) PC_KEYB(config, m_keyboard); m_keyboard->keypress().set("mb:pic8259", FUNC(pic8259_device::ir1_w)); - NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);; + M3002(config, m_rtc, 32.768_kHz_XTAL); /* internal ram */ // Machine came with 512K standard, 640K via expansion card, but BIOS offers 256K as well