Add device emulation for MM5307 Baud Rate Generator

This commit is contained in:
AJR 2019-06-14 00:31:52 -04:00
parent 70389c5762
commit e49e75c122
8 changed files with 366 additions and 29 deletions

View File

@ -1974,6 +1974,18 @@ if (MACHINES["MICROTOUCH"]~=null) then
}
end
---------------------------------------------------
--
--@src/devices/machine/mm5307.h,MACHINES["MM5307"] = true
---------------------------------------------------
if (MACHINES["MM5307"]~=null) then
files {
MAME_DIR .. "src/devices/machine/mm5307.cpp",
MAME_DIR .. "src/devices/machine/mm5307.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/mm58274c.h,MACHINES["MM58274C"] = true

View File

@ -520,6 +520,7 @@ MACHINES["MCF5206E"] = true
MACHINES["METERS"] = true
MACHINES["MICROTOUCH"] = true
--MACHINES["MIOT6530"] = true
--MACHINES["MM5307"] = true
--MACHINES["MM58167"] = true
MACHINES["MM58274C"] = true
MACHINES["MM74C922"] = true

View File

@ -524,6 +524,7 @@ MACHINES["MCCS1850"] = true
MACHINES["MCF5206E"] = true
MACHINES["MICROTOUCH"] = true
MACHINES["MIOT6530"] = true
MACHINES["MM5307"] = true
MACHINES["MM58167"] = true
MACHINES["MM58274C"] = true
MACHINES["MM74C922"] = true

View File

@ -0,0 +1,222 @@
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************
National Semiconductor MM5307 Baud Rate Generator/Programmable Divider
The MM5307 divides the frequency of a crystal oscillator or external
clock (maximum 1 MHz, ideally 921.6 kHz) to generate a single baud
rate output. (The undivided frequency is also output on another pin,
not emulated here.)
The four control inputs (A, B, C, D) select one of 15 mask-programmed
divisors or an independent external frequency. When the selected
divisor is 2N, the output has a duty cycle of exactly 50%. When the
selected divisor is 2N + 1, the high output is always one clock cycle
longer than the low output. When the selected divisor is 2N + 0.5,
the duty cycle varies slightly from phase to phase.
The MM5307 resets its output frequency when the reset input is pulsed
low or the control inputs change.
***************************************************************************/
#include "emu.h"
#include "mm5307.h"
#define VERBOSE 0
#include "logmacro.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// device type definitions
DEFINE_DEVICE_TYPE(MM5307AA, mm5307aa_device, "mm5307aa", "MM5307AA Baud Rate Generator")
DEFINE_DEVICE_TYPE(MM5307AB, mm5307ab_device, "mm5307ab", "MM5307AB Baud Rate Generator")
//**************************************************************************
// DIVISOR TABLES
//**************************************************************************
const std::array<u16, 16> mm5307aa_device::s_divisors_x2 = {
0, // external frequency (not divided)
2304, // 50 baud (divide by 1152)
1536, // 75 baud (divide by 768)
1048, // 110 baud (divide by 524)
857, // 134.5 baud (divide by 428.5)
768, // 150 baud (divide by 384)
384, // 300 baud (divide by 192)
192, // 600 baud (divide by 96)
128, // 900 baud (divide by 64)
96, // 1200 baud (divide by 48)
64, // 1800 baud (divide by 32)
48, // 2400 baud (divide by 24)
32, // 3600 baud (divide by 16)
24, // 4800 baud (divide by 12)
16, // 7200 baud (divide by 8)
12 // 9600 baud (divide by 6)
};
const std::array<u16, 16> mm5307ab_device::s_divisors_x2 = {
0, // external frequency (not divided)
2304, // 50 baud (divide by 1152)
576, // 200 baud (divide by 288)
1048, // 110 baud (divide by 524)
857, // 134.5 baud (divide by 428.5)
768, // 150 baud (divide by 384)
384, // 300 baud (divide by 192)
192, // 600 baud (divide by 96)
128, // 900 baud (divide by 64)
96, // 1200 baud (divide by 48)
64, // 1800 baud (divide by 32)
48, // 2400 baud (divide by 24)
32, // 3600 baud (divide by 16)
24, // 4800 baud (divide by 12)
1536, // 75 baud (divide by 768)
12 // 9600 baud (divide by 6)
};
//**************************************************************************
// DEVICE IMPLEMENTATION
//**************************************************************************
//-------------------------------------------------
// mm5307_device - constructor
//-------------------------------------------------
mm5307_device::mm5307_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const std::array<u16, 16> &divisors_x2)
: device_t(mconfig, type, tag, owner, clock)
, m_divisors_x2(divisors_x2)
, m_output_cb(*this)
, m_ext_freq(0)
, m_freq_control(0)
, m_phase(0)
, m_periodic_timer(nullptr)
{
}
//-------------------------------------------------
// mm5307aa_device - constructor
//-------------------------------------------------
mm5307aa_device::mm5307aa_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: mm5307_device(mconfig, MM5307AA, tag, owner, clock, s_divisors_x2)
{
}
//-------------------------------------------------
// mm5307ab_device - constructor
//-------------------------------------------------
mm5307ab_device::mm5307ab_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: mm5307_device(mconfig, MM5307AB, tag, owner, clock, s_divisors_x2)
{
}
//-------------------------------------------------
// device_resolve_objects - resolve objects that
// may be needed for other devices to set
// initial conditions at start time
//-------------------------------------------------
void mm5307_device::device_resolve_objects()
{
// Resolve callback
m_output_cb.resolve_safe();
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void mm5307_device::device_start()
{
// Create timer
m_periodic_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mm5307_device::periodic_update), this));
// Register for saving
save_item(NAME(m_freq_control));
save_item(NAME(m_phase));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void mm5307_device::device_reset()
{
// Output delay from reset
m_periodic_timer->adjust(attotime::from_usec(500) + clocks_to_attotime(4));
}
//-------------------------------------------------
// control_w - set frequency control
//-------------------------------------------------
void mm5307_device::control_w(u8 data)
{
data &= 15;
if (data != m_freq_control)
{
m_freq_control = data;
if (m_divisors_x2[data] == 0)
LOG("%s: External frequency selected\n", machine().describe_context());
else
LOG("%s: %.1f baud selected\n", machine().describe_context(), clock() / (8.0 * m_divisors_x2[data]));
// Emulate access time
m_periodic_timer->adjust(attotime::from_usec(2800) + clocks_to_attotime(13));
}
}
//-------------------------------------------------
// periodic_update - update output state and
// reset timer for next period
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(mm5307_device::periodic_update)
{
// Up to four different phases
m_phase = (m_phase + 1) & 3;
m_output_cb(BIT(m_phase, 0));
u16 divisor = m_divisors_x2[m_freq_control];
if (divisor == 0)
{
m_periodic_timer->adjust(m_ext_freq == 0 ? attotime::never : attotime::from_hz(m_ext_freq) / 2);
}
else switch (m_phase)
{
// First low output phase
case 0:
m_periodic_timer->adjust(clocks_to_attotime(divisor & ~2) / 2);
break;
// First high output phase
case 1:
m_periodic_timer->adjust(clocks_to_attotime(divisor >> 1));
break;
// Second low output phase
case 2:
m_periodic_timer->adjust(clocks_to_attotime((divisor & ~2) >> 1 | (divisor & 1)));
break;
// Second high output phase
case 3:
m_periodic_timer->adjust(clocks_to_attotime(divisor - ((divisor & 1) << 1)) / 2);
break;
}
}

View File

@ -0,0 +1,99 @@
// license:BSD-3-Clause
// copyright-holders:AJR
/***************************************************************************
National Semiconductor MM5307 Baud Rate Generator/Programmable Divider
****************************************************************************
___ ___
EXTERNAL FREQ 1 |* \__/ | 14 ϕOUT
NC 2 | | 13 RESET
OUTPUT 3 | | 12 Vgg
Vss 4 | MM5307 | 11 A
EXTERNAL CLOCK 5 | | 10 B
CRYSTAL 6 | | 9 C
CRYSTAL 7 |__________| 8 D
***************************************************************************/
#ifndef MAME_MACHINE_MM5307_H
#define MAME_MACHINE_MM5307_H 1
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> mm5307_device
class mm5307_device : public device_t
{
public:
// configuration
void set_ext_freq(u32 freq) { m_ext_freq = freq; }
void set_ext_freq(const XTAL &freq) { m_ext_freq = freq.value(); }
auto output_cb() { return m_output_cb.bind(); }
// frequency control
void control_w(u8 data);
protected:
// base class constructor
mm5307_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, const std::array<u16, 16> &divisors_x2);
// device-specific overrides
virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
private:
// timed update callback
TIMER_CALLBACK_MEMBER(periodic_update);
// internal divisors
const std::array<u16, 16> &m_divisors_x2;
// callbacks
devcb_write_line m_output_cb;
// external input frequency
u32 m_ext_freq;
// internal state
u8 m_freq_control;
u8 m_phase;
// timer
emu_timer *m_periodic_timer;
};
// ======================> mm5307aa_device
class mm5307aa_device : public mm5307_device
{
public:
// construction/destruction
mm5307aa_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
private:
static const std::array<u16, 16> s_divisors_x2;
};
// ======================> mm5307ab_device
class mm5307ab_device : public mm5307_device
{
public:
// construction/destruction
mm5307ab_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
private:
static const std::array<u16, 16> s_divisors_x2;
};
// device type declarations
DECLARE_DEVICE_TYPE(MM5307AA, mm5307aa_device)
DECLARE_DEVICE_TYPE(MM5307AB, mm5307ab_device)
#endif // MAME_MACHINE_MM5307_H

View File

@ -69,7 +69,7 @@ void poly88_state::poly88_io(address_map &map)
{
map.unmap_value_high();
map.global_mask(0xff);
map(0x00, 0x01).rw(m_uart, FUNC(i8251_device::read), FUNC(i8251_device::write));
map(0x00, 0x01).rw(m_usart, FUNC(i8251_device::read), FUNC(i8251_device::write));
map(0x04, 0x04).w(FUNC(poly88_state::poly88_baud_rate_w));
map(0x08, 0x08).w(FUNC(poly88_state::poly88_intr_w));
map(0xf8, 0xf8).r(FUNC(poly88_state::poly88_keyboard_r));
@ -201,7 +201,7 @@ GFXDECODE_END
MACHINE_CONFIG_START(poly88_state::poly88)
/* basic machine hardware */
I8080A(config, m_maincpu, XTAL(16'588'800) / 9); // uses 8224 clock generator
I8080A(config, m_maincpu, 16.5888_MHz_XTAL / 9); // uses 8224 clock generator
m_maincpu->set_addrmap(AS_PROGRAM, &poly88_state::poly88_mem);
m_maincpu->set_addrmap(AS_IO, &poly88_state::poly88_io);
m_maincpu->set_vblank_int("screen", FUNC(poly88_state::poly88_interrupt));
@ -230,9 +230,12 @@ MACHINE_CONFIG_START(poly88_state::poly88)
m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_SPEAKER_ENABLED);
/* uart */
I8251(config, m_uart, XTAL(16'588'800) / 9);
m_uart->txd_handler().set(FUNC(poly88_state::write_cas_tx));
m_uart->rxrdy_handler().set(FUNC(poly88_state::poly88_usart_rxready));
I8251(config, m_usart, 16.5888_MHz_XTAL / 9);
m_usart->txd_handler().set(FUNC(poly88_state::write_cas_tx));
m_usart->rxrdy_handler().set(FUNC(poly88_state::poly88_usart_rxready));
MM5307AA(config, m_brg, 16.5888_MHz_XTAL / 18);
m_brg->output_cb().set(FUNC(poly88_state::cassette_txc_rxc_w));
/* snapshot */
MCFG_SNAPSHOT_ADD("snapshot", poly88_state, poly88, "img", attotime::from_seconds(2))

View File

@ -11,6 +11,7 @@
#pragma once
#include "machine/i8251.h"
#include "machine/mm5307.h"
#include "imagedev/cassette.h"
#include "imagedev/snapquik.h"
@ -20,15 +21,15 @@ public:
enum
{
TIMER_USART,
TIMER_KEYBOARD,
TIMER_CASSETTE
TIMER_KEYBOARD
};
poly88_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_video_ram(*this, "video_ram"),
m_maincpu(*this, "maincpu"),
m_uart(*this, "uart"),
m_usart(*this, "usart"),
m_brg(*this, "brg"),
m_cassette(*this, "cassette"),
m_linec(*this, "LINEC"),
m_line0(*this, "LINE0"),
@ -51,8 +52,8 @@ private:
uint8_t m_intr;
uint8_t m_last_code;
uint8_t m_int_vector;
emu_timer * m_cassette_timer;
emu_timer * m_usart_timer;
emu_timer * m_keyboard_timer;
int m_previous_level;
int m_clk_level;
int m_clk_level_tape;
@ -65,7 +66,7 @@ private:
INTERRUPT_GEN_MEMBER(poly88_interrupt);
TIMER_CALLBACK_MEMBER(poly88_usart_timer_callback);
TIMER_CALLBACK_MEMBER(keyboard_callback);
TIMER_CALLBACK_MEMBER(poly88_cassette_timer_callback);
DECLARE_WRITE_LINE_MEMBER(cassette_txc_rxc_w);
DECLARE_WRITE_LINE_MEMBER(write_cas_tx);
DECLARE_WRITE_LINE_MEMBER(poly88_usart_rxready);
IRQ_CALLBACK_MEMBER(poly88_irq_callback);
@ -77,7 +78,8 @@ private:
void poly88_mem(address_map &map);
required_device<cpu_device> m_maincpu;
required_device<i8251_device> m_uart;
required_device<i8251_device> m_usart;
required_device<mm5307_device> m_brg;
required_device<cassette_image_device> m_cassette;
required_ioport m_linec;
required_ioport m_line0;

View File

@ -24,9 +24,6 @@ void poly88_state::device_timer(emu_timer &timer, device_timer_id id, int param,
case TIMER_KEYBOARD:
keyboard_callback(ptr, param);
break;
case TIMER_CASSETTE:
poly88_cassette_timer_callback(ptr, param);
break;
default:
assert_always(false, "Unknown id in poly88_state::device_timer");
}
@ -42,9 +39,10 @@ TIMER_CALLBACK_MEMBER(poly88_state::poly88_usart_timer_callback)
WRITE8_MEMBER(poly88_state::poly88_baud_rate_w)
{
logerror("poly88_baud_rate_w %02x\n",data);
m_usart_timer = timer_alloc(TIMER_USART);
m_usart_timer->adjust(attotime::zero, 0, attotime::from_hz(300));
m_brg->control_w(data & 15);
// FIXME
m_usart_timer->adjust(attotime::zero, 0, attotime::from_hz(300));
}
uint8_t poly88_state::row_number(uint8_t code) {
@ -149,8 +147,6 @@ TIMER_CALLBACK_MEMBER(poly88_state::keyboard_callback)
} else {
m_last_code = key_code;
}
timer_set(attotime::from_hz(24000), TIMER_KEYBOARD);
}
IRQ_CALLBACK_MEMBER(poly88_state::poly88_irq_callback)
@ -163,7 +159,7 @@ WRITE_LINE_MEMBER(poly88_state::write_cas_tx)
m_cas_tx = state;
}
TIMER_CALLBACK_MEMBER(poly88_state::poly88_cassette_timer_callback)
WRITE_LINE_MEMBER(poly88_state::cassette_txc_rxc_w)
{
int data;
int current_level;
@ -186,13 +182,13 @@ TIMER_CALLBACK_MEMBER(poly88_state::poly88_cassette_timer_callback)
{
data = (!m_previous_level && current_level) ? 1 : 0;
//data = current_level;
m_uart->write_rxd(data);
m_usart->write_rxd(data);
m_clk_level_tape = 1;
}
}
m_uart->write_rxc(m_clk_level_tape);
m_usart->write_rxc(m_clk_level_tape);
}
/* tape writing */
@ -203,15 +199,14 @@ TIMER_CALLBACK_MEMBER(poly88_state::poly88_cassette_timer_callback)
m_cassette->output(data&0x01 ? 1 : -1);
m_clk_level_tape = m_clk_level_tape ? 0 : 1;
m_uart->write_txc(m_clk_level_tape);
m_usart->write_txc(m_clk_level_tape);
return;
}
m_clk_level_tape = 1;
m_clk_level = m_clk_level ? 0 : 1;
m_uart->write_txc(m_clk_level);
m_usart->write_txc(state);
// }
}
@ -219,17 +214,19 @@ TIMER_CALLBACK_MEMBER(poly88_state::poly88_cassette_timer_callback)
void poly88_state::init_poly88()
{
m_previous_level = 0;
m_clk_level = m_clk_level_tape = 1;
m_cassette_timer = timer_alloc(TIMER_CASSETTE);
m_cassette_timer->adjust(attotime::zero, 0, attotime::from_hz(600));
m_clk_level_tape = 1;
timer_set(attotime::from_hz(24000), TIMER_KEYBOARD);
m_usart_timer = timer_alloc(TIMER_USART);
m_keyboard_timer = timer_alloc(TIMER_KEYBOARD);
m_keyboard_timer->adjust(attotime::from_hz(24000), 0, attotime::from_hz(24000));
}
void poly88_state::machine_reset()
{
m_intr = 0;
m_last_code = 0;
m_brg->control_w(0);
}
INTERRUPT_GEN_MEMBER(poly88_state::poly88_interrupt)
@ -327,6 +324,6 @@ SNAPSHOT_LOAD_MEMBER( poly88_state, poly88 )
}
pos+=recordLen;
}
m_uart->reset();
m_usart->reset();
return image_init_result::PASS;
}