summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/fidel_clockdiv.cpp
blob: 802a2d7bf6ac87cd3b07be548a84bc6e0e9ca493 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// license:BSD-3-Clause
// copyright-holders:hap
/******************************************************************************

Fidelity Electronics 6502 dynamic(offset-dependent) CPU clock divider base class.
Used to compensate slow memory chips in chess computer models: SC12, AS12, PC, EAS, EAG.

TODO:
- improve clock divider? it seems a little bit slower than the real machine.
  Currently, a dummy timer workaround is needed, or it's much worse.
  Is the problem here due to timing of CPU addressbus changes? We can only 'sense'
  the addressbus at read or write accesses.

******************************************************************************/

#include "emu.h"
#include "machine/fidel_clockdiv.h"


// machine start/reset

void fidel_clockdiv_state::machine_start()
{
	// zerofill/register for savestates
	m_div_config = 0;

	save_item(NAME(m_div_status));
	save_item(NAME(m_div_config));
	save_item(NAME(m_div_scale));

	// dummy timer for cpu divider
	m_div_timer = machine().scheduler().timer_alloc(timer_expired_delegate(), this);
}

void fidel_clockdiv_state::machine_reset()
{
	div_refresh();
}


// input ports

INPUT_PORTS_START( fidel_clockdiv_2 )
	PORT_START("div_config") // hardwired, default to /2
	PORT_CONFNAME( 0x03, 0x02, "CPU Divider" ) PORT_CHANGED_MEMBER(DEVICE_SELF, fidel_clockdiv_state, div_changed, 0)
	PORT_CONFSETTING(    0x00, "Disabled" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x03, "4" )
INPUT_PORTS_END

INPUT_PORTS_START( fidel_clockdiv_4 )
	PORT_START("div_config") // hardwired, default to /4
	PORT_CONFNAME( 0x03, 0x03, "CPU Divider" ) PORT_CHANGED_MEMBER(DEVICE_SELF, fidel_clockdiv_state, div_changed, 0)
	PORT_CONFSETTING(    0x00, "Disabled" )
	PORT_CONFSETTING(    0x02, "2" )
	PORT_CONFSETTING(    0x03, "4" )
INPUT_PORTS_END


// implementation

void fidel_clockdiv_state::div_set_cpu_freq(offs_t offset)
{
	if (offset != m_div_status)
	{
		// when a13/a14 is high, XTAL goes through divider(s)
		// (depending on factory-set jumper, either one or two 7474)
		m_maincpu->set_clock_scale(offset ? m_div_scale : 1.0);

		m_div_status = offset;
	}
}

void fidel_clockdiv_state::div_trampoline_w(offs_t offset, u8 data)
{
	if (m_div_config)
		div_set_cpu_freq(offset & 0x6000);

	m_mainmap->write8(offset, data);
}

u8 fidel_clockdiv_state::div_trampoline_r(offs_t offset)
{
	if (m_div_config && !machine().side_effects_disabled())
		div_set_cpu_freq(offset & 0x6000);

	return m_mainmap->read8(offset);
}

void fidel_clockdiv_state::div_trampoline(address_map &map)
{
	map(0x0000, 0xffff).rw(FUNC(fidel_clockdiv_state::div_trampoline_r), FUNC(fidel_clockdiv_state::div_trampoline_w));
}

void fidel_clockdiv_state::div_refresh(ioport_value val)
{
	if (val == 0xff)
	{
		// bail out if there is no cpu divider
		ioport_port *inp = ioport("div_config");
		if (inp == nullptr)
			return;

		val = inp->read();
	}

	m_maincpu->set_clock_scale(1.0);
	m_div_status = ~0;
	m_div_config = val;
	m_div_scale = (m_div_config & 1) ? 0.25 : 0.5;

	// stop high frequency background timer if cpu divider is disabled
	attotime period = (val) ? attotime::from_hz(m_maincpu->clock()) : attotime::never;
	m_div_timer->adjust(period, 0, period);
}