summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/c117.cpp
blob: d8af377d751f53ec05cace0a695c888166ad3f3d (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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// license:BSD-3-Clause
// copyright-holders:Alex W. Jackson
/*
Namco Custom 117, used in System 1

CUS117 is a simple MMU that provides a 23 bit virtual address space for a pair
of M6809 CPUs. The chip outputs the various enable lines for RAM, ROM, video
customs, etc., and bits 12-21 of the virtual address (bit 22 of the virtual
address is handled internally: it selects between ROM and everything else)

Each CPU's address space is evenly divided into eight 8KB banks, and each of
these banks can be directed to any portion of the virtual address space
(however, the last bank for each CPU is effectively read only, since writes to
E000-FFFF control CUS117 itself)

The main and sub CPUs share the same address and data bus, but each CPU can
set up its own banks independently. The main CPU can also set the sub CPU's
last bank (E000-FFFF, where the 6809 reset and interrupt vectors reside)

As well as being an MMU, CUS117 serves as the interrupt controller for the
two 6809s and as the reset generator for the entire system.
*/

#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/c117.h"


DEFINE_DEVICE_TYPE(NAMCO_C117, namco_c117_device, "namco_c117", "Namco C117 MMU")


//-------------------------------------------------
//  namco_c117_device - constructor
//-------------------------------------------------

namco_c117_device::namco_c117_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, NAMCO_C117, tag, owner, clock),
	device_memory_interface(mconfig, *this),
	m_subres_cb(*this),
	m_program_config("program", ENDIANNESS_BIG, 8, 23),
	m_cpuexec{ { *this, finder_base::DUMMY_TAG }, { *this, finder_base::DUMMY_TAG } },
	m_watchdog(*this, "watchdog")
{
}

device_memory_interface::space_config_vector namco_c117_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_PROGRAM, &m_program_config)
	};
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void namco_c117_device::device_start()
{
	m_subres_cb.resolve_safe();

	m_program = &space(AS_PROGRAM);

	m_cpucache[0] = m_cpuexec[0]->space(AS_PROGRAM).cache<0, 0, ENDIANNESS_BIG>();
	m_cpucache[1] = m_cpuexec[1]->space(AS_PROGRAM).cache<0, 0, ENDIANNESS_BIG>();

	memset(&m_offsets, 0, sizeof(m_offsets));
	m_subres = m_wdog = 0;
	save_item(NAME(m_offsets));
	save_item(NAME(m_subres));
	save_item(NAME(m_wdog));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void namco_c117_device::device_reset()
{
	// default MMU setup for main CPU
	m_offsets[0][0] = 0x0180 * 0x2000; // bank0 = 0x180(RAM) - evidence: wldcourt
	m_offsets[0][1] = 0x0180 * 0x2000; // bank1 = 0x180(RAM) - evidence: berabohm
	m_offsets[0][7] = 0x03ff * 0x2000; // bank7 = 0x3ff(PRG7)

	// default MMU setup for sub CPU
	m_offsets[1][0] = 0x0180 * 0x2000; // bank0 = 0x180(RAM) - evidence: wldcourt
	m_offsets[1][7] = 0x03ff * 0x2000; // bank7 = 0x3ff(PRG7)

	m_subres = m_wdog = 0;
	m_subres_cb(ASSERT_LINE);

	// reset the main CPU so it picks up the reset vector from the correct bank
	m_cpuexec[0]->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
}


//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

void namco_c117_device::device_add_mconfig(machine_config &config)
{
	WATCHDOG_TIMER(config, m_watchdog);
}


READ8_MEMBER(namco_c117_device::main_r)
{
	return m_program->read_byte(remap(0, offset));
}

READ8_MEMBER(namco_c117_device::sub_r)
{
	return m_program->read_byte(remap(1, offset));
}

WRITE8_MEMBER(namco_c117_device::main_w)
{
	if (offset < 0xe000)
		m_program->write_byte(remap(0, offset), data);
	else
		register_w(0, offset, data);
}

WRITE8_MEMBER(namco_c117_device::sub_w)
{
	if (offset < 0xe000)
		m_program->write_byte(remap(1, offset), data);
	else
		register_w(1, offset, data);
}

// FIXME: the sound CPU watchdog is probably in CUS121, and definitely isn't in CUS117
// however, until the watchdog is a device and it's possible to have two independent
// watchdogs in a machine, it's easiest to handle it here
WRITE8_MEMBER(namco_c117_device::sound_watchdog_w)
{
	kick_watchdog(2);
}


void namco_c117_device::register_w(int whichcpu, offs_t offset, uint8_t data)
{
	int reg = (offset >> 9) & 0xf;
	bool unknown_reg = false;

	switch (reg)
	{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
			bankswitch(whichcpu, reg, offset & 1, data);
			break;
		case 8:  // F000 - SUBRES (halt/reset everything but main CPU)
			if (whichcpu == 0)
			{
				m_subres = data & 1;
				m_subres_cb(m_subres ? CLEAR_LINE : ASSERT_LINE);
			}
			else
				unknown_reg = true;
			break;
		case 9:  // F200 - kick watchdog
			kick_watchdog(whichcpu);
			break;
//      case 10: // F400 - unknown but used
//          break;
		case 11: // F600 - IRQ ack
			m_cpuexec[whichcpu]->set_input_line(M6809_IRQ_LINE, CLEAR_LINE);
			break;
		case 12: // F800 - FIRQ ack
			m_cpuexec[whichcpu]->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
			break;
		case 13: // FA00 - assert FIRQ on sub CPU
			if (whichcpu == 0)
				m_cpuexec[1]->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE);
			else
				unknown_reg = true;
			break;
		case 14: // FC00 - set initial ROM bank for sub CPU
			if (whichcpu == 0)
				m_offsets[1][7] = 0x600000 | (data * 0x2000);
			else
				unknown_reg = true;
			break;
		default:
			unknown_reg = true;
	}
	if (unknown_reg)
		logerror("'%s' writing to unknown CUS117 register %04X = %02X\n", m_cpuexec[whichcpu].finder_tag(), offset, data);
}

void namco_c117_device::bankswitch(int whichcpu, int whichbank, int a0, uint8_t data)
{
	uint32_t &bank = m_offsets[whichcpu][whichbank];

	// even writes set a22-a21; odd writes set a20-a13
	if (a0 == 0)
		bank = (bank & 0x1fe000) | ((data & 0x03) * 0x200000);
	else
		bank = (bank & 0x600000) | (data * 0x2000);
}

void namco_c117_device::kick_watchdog(int whichcpu)
{
	// FIXME: change to 3 once sound CPU watchdog is separated from this device
	static const int ALL_CPU_MASK = 7;

	m_wdog |= (1 << whichcpu);

	if (m_wdog == ALL_CPU_MASK || !m_subres)
	{
		m_wdog = 0;
		m_watchdog->watchdog_reset();
	}
}