summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/fidel_clockdiv.cpp
blob: 13ec96c2ee113d0ce51c81366deef022204b5445 (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
// 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 (doing it with
  a synchronize() will make it even 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

void fidel_clockdiv_state::machine_start()
{
	// dummy timer for cpu divider
	m_div_timer = machine().scheduler().timer_alloc(timer_expired_delegate(), this);
}


// 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_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_scale = (val & 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);

	// set up memory passthroughs
	if (m_read_tap)
	{
		m_read_tap->remove();
		m_write_tap->remove();
		m_read_tap = nullptr;
		m_write_tap = nullptr;
	}

	if (val)
	{
		address_space &program = m_maincpu->space(AS_PROGRAM);

		m_read_tap = program.install_read_tap(0x0000, 0xffff, "program_div_r",[this](offs_t offset, u8 &data, u8 mem_mask)
		{
			if (!machine().side_effects_disabled())
				div_set_cpu_freq(offset & 0x6000);
			return data;
		});
		m_write_tap = program.install_write_tap(0x0000, 0xffff, "program_div_w",[this](offs_t offset, u8 &data, u8 mem_mask)
		{
			div_set_cpu_freq(offset & 0x6000);
			return data;
		});
	}
}