mirror of
https://github.com/holub/mame
synced 2025-04-25 09:50:04 +03:00
add preliminary emulation of OKI MSM5001N CMOS LCD Watch IC
This commit is contained in:
parent
74c4a0c377
commit
365b977060
@ -2652,6 +2652,18 @@ if (MACHINES["MOS6551"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/msm5001n.h,MACHINES["MSM5001N"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["MSM5001N"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/msm5001n.cpp",
|
||||
MAME_DIR .. "src/devices/machine/msm5001n.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/msm5832.h,MACHINES["MSM5832"] = true
|
||||
|
200
src/devices/machine/msm5001n.cpp
Normal file
200
src/devices/machine/msm5001n.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:hap
|
||||
/*
|
||||
|
||||
OKI MSM5001N CMOS LCD Watch IC
|
||||
|
||||
Decap shows it's not an MCU. No known documentation exists, but there are datasheets
|
||||
available for equivalent Samsung chips: KS5198, KS5199A, KS5114.
|
||||
|
||||
These kind of chips were used a lot for cheap 2-button digital wristwatches.
|
||||
|
||||
TODO:
|
||||
- add D/S inputs (other display modes, setup mode)
|
||||
- datasheets mention a 4-year calendar, does it mean it supports leap years somehow?
|
||||
- one of the Samsung datasheets mention 24hr mode, does the MSM5001N support that?
|
||||
|
||||
*/
|
||||
|
||||
#include "emu.h"
|
||||
#include "machine/msm5001n.h"
|
||||
|
||||
|
||||
DEFINE_DEVICE_TYPE(MSM5001N, msm5001n_device, "msm5001n", "OKI MSM5001N LCD Watch")
|
||||
|
||||
//-------------------------------------------------
|
||||
// constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
msm5001n_device::msm5001n_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
|
||||
device_t(mconfig, MSM5001N, tag, owner, clock),
|
||||
device_rtc_interface(mconfig, *this),
|
||||
device_nvram_interface(mconfig, *this),
|
||||
m_write_segs(*this)
|
||||
{ }
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
// allow save_item on a non-fundamental type
|
||||
ALLOW_SAVE_TYPE(msm5001n_device::mode);
|
||||
|
||||
void msm5001n_device::device_start()
|
||||
{
|
||||
m_timer = timer_alloc(FUNC(msm5001n_device::clock_tick), this);
|
||||
|
||||
m_power = true;
|
||||
initialize();
|
||||
|
||||
// register for savestates
|
||||
save_item(NAME(m_power));
|
||||
save_item(NAME(m_mode));
|
||||
save_item(NAME(m_counter));
|
||||
}
|
||||
|
||||
void msm5001n_device::initialize()
|
||||
{
|
||||
m_mode = MODE_NORMAL_HRMIN;
|
||||
m_counter = 0;
|
||||
|
||||
// 1 January, 1AM
|
||||
set_time(true, 0, 1, 1, 1, 1, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// input pins
|
||||
//-------------------------------------------------
|
||||
|
||||
void msm5001n_device::d_w(int state)
|
||||
{
|
||||
}
|
||||
|
||||
void msm5001n_device::s_w(int state)
|
||||
{
|
||||
}
|
||||
|
||||
void msm5001n_device::power_w(int state)
|
||||
{
|
||||
if (m_power && !state)
|
||||
{
|
||||
// reset chip when power goes off
|
||||
initialize();
|
||||
write_lcd(nullptr, false);
|
||||
}
|
||||
|
||||
m_power = bool(state);
|
||||
}
|
||||
|
||||
void msm5001n_device::device_clock_changed()
|
||||
{
|
||||
// smallest interval (at default frequency of 32768Hz) is LCD refresh at 32Hz
|
||||
attotime period = attotime::from_ticks(1024, clock());
|
||||
m_timer->adjust(period, 0, period);
|
||||
|
||||
// clear LCD if clock stopped
|
||||
if (clock() == 0)
|
||||
write_lcd(nullptr, false);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// process
|
||||
//-------------------------------------------------
|
||||
|
||||
void msm5001n_device::write_lcd(u8 *digits, bool colon)
|
||||
{
|
||||
u32 segs = 0;
|
||||
|
||||
if (digits)
|
||||
{
|
||||
// 0-9, A, P, none
|
||||
static const u8 lut_segs[0x10] =
|
||||
{ 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x73,0x00,0x00,0x00,0x00 };
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
segs |= lut_segs[digits[i]] << (8 * i);
|
||||
}
|
||||
segs |= colon ? 0x80 : 0;
|
||||
|
||||
// COM1: BC1,F2,A2,B2,COLON,F3,AD3,B3,F4,A4,B4
|
||||
m_write_segs(0, bitswap<11>(segs,25,21,16,17,7,13,8,9,5,0,1));
|
||||
|
||||
// COM2: D2,E2,G2,C2,D4,E3,G3,C3,E4,G4,C4
|
||||
m_write_segs(1, bitswap<11>(segs,19,20,22,18,3,12,14,10,4,6,2));
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(msm5001n_device::clock_tick)
|
||||
{
|
||||
if (!m_power)
|
||||
return;
|
||||
|
||||
m_counter++;
|
||||
if ((m_counter & 0x1f) == 0)
|
||||
advance_seconds();
|
||||
|
||||
u8 digits[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
digits[i] = 0xf;
|
||||
bool colon = false;
|
||||
|
||||
// convert current time to BCD
|
||||
u8 minute = convert_to_bcd(get_clock_register(RTC_MINUTE));
|
||||
u8 hour = get_clock_register(RTC_HOUR) % 12;
|
||||
hour = convert_to_bcd((hour == 0) ? 12 : hour);
|
||||
|
||||
switch (m_mode)
|
||||
{
|
||||
case MODE_NORMAL_HRMIN:
|
||||
digits[0] = minute & 0xf;
|
||||
digits[1] = minute >> 4;
|
||||
digits[2] = hour & 0xf;
|
||||
if (hour & 0xf0)
|
||||
digits[3] = hour >> 4;
|
||||
|
||||
colon = !BIT(m_counter, 4);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
write_lcd(digits, colon);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// nvram
|
||||
//-------------------------------------------------
|
||||
|
||||
bool msm5001n_device::nvram_write(util::write_stream &file)
|
||||
{
|
||||
size_t actual;
|
||||
u8 buf[5];
|
||||
|
||||
// current time
|
||||
for (int i = 0; i < 5; i++)
|
||||
buf[i] = get_clock_register(i);
|
||||
|
||||
if (file.write(&buf, sizeof(buf), actual) || (sizeof(buf) != actual))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool msm5001n_device::nvram_read(util::read_stream &file)
|
||||
{
|
||||
size_t actual;
|
||||
|
||||
u8 buf[5];
|
||||
if (file.read(&buf, sizeof(buf), actual) || (sizeof(buf) != actual))
|
||||
return false;
|
||||
|
||||
// current time
|
||||
for (int i = 0; i < 5; i++)
|
||||
set_clock_register(i, buf[i]);
|
||||
|
||||
return true;
|
||||
}
|
65
src/devices/machine/msm5001n.h
Normal file
65
src/devices/machine/msm5001n.h
Normal file
@ -0,0 +1,65 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:hap
|
||||
/*
|
||||
|
||||
OKI MSM5001N CMOS LCD Watch IC
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MAME_MACHINE_MSM5001N_H
|
||||
#define MAME_MACHINE_MSM5001N_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dirtc.h"
|
||||
|
||||
class msm5001n_device : public device_t, public device_rtc_interface, public device_nvram_interface
|
||||
{
|
||||
public:
|
||||
msm5001n_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
|
||||
|
||||
// configuration helpers
|
||||
auto write_segs() { return m_write_segs.bind(); } // COM in offset, SEG pins in data
|
||||
|
||||
void d_w(int state); // D button
|
||||
void s_w(int state); // S button
|
||||
void power_w(int state);
|
||||
|
||||
// set_current_time is unused here (only using dirtc for the counters)
|
||||
virtual void set_current_time(const system_time &systime) override { ; }
|
||||
|
||||
protected:
|
||||
// device_t implementation
|
||||
virtual void device_start() override;
|
||||
virtual void device_clock_changed() override;
|
||||
|
||||
// device_nvram_interface implementation
|
||||
virtual void nvram_default() override { ; }
|
||||
virtual bool nvram_read(util::read_stream &file) override;
|
||||
virtual bool nvram_write(util::write_stream &file) override;
|
||||
|
||||
virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override { ; } // unused
|
||||
|
||||
private:
|
||||
enum mode : u8
|
||||
{
|
||||
MODE_NORMAL_HRMIN = 0
|
||||
};
|
||||
|
||||
emu_timer *m_timer;
|
||||
|
||||
bool m_power;
|
||||
mode m_mode;
|
||||
u8 m_counter;
|
||||
|
||||
devcb_write16 m_write_segs;
|
||||
|
||||
TIMER_CALLBACK_MEMBER(clock_tick);
|
||||
void write_lcd(u8 *digits, bool colon);
|
||||
void initialize();
|
||||
};
|
||||
|
||||
|
||||
DECLARE_DEVICE_TYPE(MSM5001N, msm5001n_device)
|
||||
|
||||
#endif // MAME_MACHINE_MSM5001N_H
|
@ -16,7 +16,7 @@ class sensorboard_device : public device_t, public device_nvram_interface
|
||||
public:
|
||||
sensorboard_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
|
||||
|
||||
enum sb_type
|
||||
enum sb_type : u8
|
||||
{
|
||||
NOSENSORS = 0,
|
||||
BUTTONS,
|
||||
|
@ -173,7 +173,7 @@ static INPUT_PORTS_START( compan )
|
||||
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED)
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_N) PORT_NAME("New Game")
|
||||
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("Rook")
|
||||
PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Play")
|
||||
PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Play")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_B) PORT_NAME("Clear Board")
|
||||
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
|
||||
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_M) PORT_NAME("Multi Move")
|
||||
|
@ -302,7 +302,7 @@ static INPUT_PORTS_START( expchess )
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("Bishop")
|
||||
PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_T) PORT_NAME("Take Back")
|
||||
PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level / Sound")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Play / PVP")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Play / PVP")
|
||||
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("Knight")
|
||||
PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("Pawn")
|
||||
PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("Queen")
|
||||
@ -320,7 +320,7 @@ static INPUT_PORTS_START( compan2 )
|
||||
|
||||
PORT_MODIFY("IN.1")
|
||||
PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_L) PORT_NAME("Level")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_O) PORT_NAME("Play")
|
||||
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_Y) PORT_NAME("Play")
|
||||
PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_KEYPAD) PORT_CODE(KEYCODE_E) PORT_NAME("Enter Position")
|
||||
|
||||
PORT_MODIFY("IN.2")
|
||||
|
Loading…
Reference in New Issue
Block a user