m3002: New device

This commit is contained in:
AJR 2020-04-07 19:39:17 -04:00
parent 4f08f64bd7
commit 2d68e6dae9
6 changed files with 611 additions and 123 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

101
src/devices/machine/m3002.h Normal file
View File

@ -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

View File

@ -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<pc_noppi_mb_device> m_mb;
required_device<pc_keyboard_device> m_keyboard;
required_device<ram_device> m_ram;
required_device<m3002_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_device>("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