1 // license:BSD-3-Clause
2 // copyright-holders:Alex W. Jackson
3 /*
4 Namco Custom 117, used in System 1
5 
6 CUS117 is a simple MMU that provides a 23 bit virtual address space for a pair
7 of M6809 CPUs. The chip outputs the various enable lines for RAM, ROM, video
8 customs, etc., and bits 12-21 of the virtual address (bit 22 of the virtual
9 address is handled internally: it selects between ROM and everything else)
10 
11 Each CPU's address space is evenly divided into eight 8KB banks, and each of
12 these banks can be directed to any portion of the virtual address space
13 (however, the last bank for each CPU is effectively read only, since writes to
14 E000-FFFF control CUS117 itself)
15 
16 The main and sub CPUs share the same address and data bus, but each CPU can
17 set up its own banks independently. The main CPU can also set the sub CPU's
18 last bank (E000-FFFF, where the 6809 reset and interrupt vectors reside)
19 
20 As well as being an MMU, CUS117 serves as the interrupt controller for the
21 two 6809s and as the reset generator for the entire system.
22 */
23 
24 #include "emu.h"
25 #include "cpu/m6809/m6809.h"
26 #include "machine/c117.h"
27 
28 
29 DEFINE_DEVICE_TYPE(NAMCO_C117, namco_c117_device, "namco_c117", "Namco C117 MMU")
30 
31 
32 //-------------------------------------------------
33 //  namco_c117_device - constructor
34 //-------------------------------------------------
35 
namco_c117_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)36 namco_c117_device::namco_c117_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
37 	device_t(mconfig, NAMCO_C117, tag, owner, clock),
38 	device_memory_interface(mconfig, *this),
39 	m_subres_cb(*this),
40 	m_program_config("program", ENDIANNESS_BIG, 8, 23),
41 	m_cpuexec{ { *this, finder_base::DUMMY_TAG }, { *this, finder_base::DUMMY_TAG } },
42 	m_watchdog(*this, "watchdog")
43 {
44 }
45 
memory_space_config() const46 device_memory_interface::space_config_vector namco_c117_device::memory_space_config() const
47 {
48 	return space_config_vector {
49 		std::make_pair(AS_PROGRAM, &m_program_config)
50 	};
51 }
52 
53 //-------------------------------------------------
54 //  device_start - device-specific startup
55 //-------------------------------------------------
56 
device_start()57 void namco_c117_device::device_start()
58 {
59 	m_subres_cb.resolve_safe();
60 
61 	space(AS_PROGRAM).specific(m_program);
62 
63 	m_cpuexec[0]->space(AS_PROGRAM).cache(m_cpucache[0]);
64 	m_cpuexec[1]->space(AS_PROGRAM).cache(m_cpucache[1]);
65 
66 	memset(&m_offsets, 0, sizeof(m_offsets));
67 	m_subres = m_wdog = 0;
68 	save_item(NAME(m_offsets));
69 	save_item(NAME(m_subres));
70 	save_item(NAME(m_wdog));
71 }
72 
73 
74 //-------------------------------------------------
75 //  device_reset - device-specific reset
76 //-------------------------------------------------
77 
device_reset()78 void namco_c117_device::device_reset()
79 {
80 	// default MMU setup for main CPU
81 	m_offsets[0][0] = 0x0180 * 0x2000; // bank0 = 0x180(RAM) - evidence: wldcourt
82 	m_offsets[0][1] = 0x0180 * 0x2000; // bank1 = 0x180(RAM) - evidence: berabohm
83 	m_offsets[0][7] = 0x03ff * 0x2000; // bank7 = 0x3ff(PRG7)
84 
85 	// default MMU setup for sub CPU
86 	m_offsets[1][0] = 0x0180 * 0x2000; // bank0 = 0x180(RAM) - evidence: wldcourt
87 	m_offsets[1][7] = 0x03ff * 0x2000; // bank7 = 0x3ff(PRG7)
88 
89 	m_subres = m_wdog = 0;
90 	m_subres_cb(ASSERT_LINE);
91 
92 	// reset the main CPU so it picks up the reset vector from the correct bank
93 	m_cpuexec[0]->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
94 }
95 
96 
97 //-------------------------------------------------
98 //  device_add_mconfig - add device configuration
99 //-------------------------------------------------
100 
device_add_mconfig(machine_config & config)101 void namco_c117_device::device_add_mconfig(machine_config &config)
102 {
103 	WATCHDOG_TIMER(config, m_watchdog);
104 }
105 
106 
main_r(offs_t offset)107 uint8_t namco_c117_device::main_r(offs_t offset)
108 {
109 	return m_program.read_byte(remap(0, offset));
110 }
111 
sub_r(offs_t offset)112 uint8_t namco_c117_device::sub_r(offs_t offset)
113 {
114 	return m_program.read_byte(remap(1, offset));
115 }
116 
main_w(offs_t offset,uint8_t data)117 void namco_c117_device::main_w(offs_t offset, uint8_t data)
118 {
119 	if (offset < 0xe000)
120 		m_program.write_byte(remap(0, offset), data);
121 	else
122 		register_w(0, offset, data);
123 }
124 
sub_w(offs_t offset,uint8_t data)125 void namco_c117_device::sub_w(offs_t offset, uint8_t data)
126 {
127 	if (offset < 0xe000)
128 		m_program.write_byte(remap(1, offset), data);
129 	else
130 		register_w(1, offset, data);
131 }
132 
133 // FIXME: the sound CPU watchdog is probably in CUS121, and definitely isn't in CUS117
134 // however, until the watchdog is a device and it's possible to have two independent
135 // watchdogs in a machine, it's easiest to handle it here
sound_watchdog_w(uint8_t data)136 void namco_c117_device::sound_watchdog_w(uint8_t data)
137 {
138 	kick_watchdog(2);
139 }
140 
141 
register_w(int whichcpu,offs_t offset,uint8_t data)142 void namco_c117_device::register_w(int whichcpu, offs_t offset, uint8_t data)
143 {
144 	int reg = (offset >> 9) & 0xf;
145 	bool unknown_reg = false;
146 
147 	switch (reg)
148 	{
149 		case 0:
150 		case 1:
151 		case 2:
152 		case 3:
153 		case 4:
154 		case 5:
155 		case 6:
156 		case 7:
157 			bankswitch(whichcpu, reg, offset & 1, data);
158 			break;
159 		case 8:  // F000 - SUBRES (halt/reset everything but main CPU)
160 			if (whichcpu == 0)
161 			{
162 				m_subres = data & 1;
163 				m_subres_cb(m_subres ? CLEAR_LINE : ASSERT_LINE);
164 			}
165 			else
166 				unknown_reg = true;
167 			break;
168 		case 9:  // F200 - kick watchdog
169 			kick_watchdog(whichcpu);
170 			break;
171 //      case 10: // F400 - unknown but used
172 //          break;
173 		case 11: // F600 - IRQ ack
174 			m_cpuexec[whichcpu]->set_input_line(M6809_IRQ_LINE, CLEAR_LINE);
175 			break;
176 		case 12: // F800 - FIRQ ack
177 			m_cpuexec[whichcpu]->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
178 			break;
179 		case 13: // FA00 - assert FIRQ on sub CPU
180 			if (whichcpu == 0)
181 				m_cpuexec[1]->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE);
182 			else
183 				unknown_reg = true;
184 			break;
185 		case 14: // FC00 - set initial ROM bank for sub CPU
186 			if (whichcpu == 0)
187 				m_offsets[1][7] = 0x600000 | (data * 0x2000);
188 			else
189 				unknown_reg = true;
190 			break;
191 		default:
192 			unknown_reg = true;
193 	}
194 	if (unknown_reg)
195 		logerror("'%s' writing to unknown CUS117 register %04X = %02X\n", m_cpuexec[whichcpu].finder_tag(), offset, data);
196 }
197 
bankswitch(int whichcpu,int whichbank,int a0,uint8_t data)198 void namco_c117_device::bankswitch(int whichcpu, int whichbank, int a0, uint8_t data)
199 {
200 	uint32_t &bank = m_offsets[whichcpu][whichbank];
201 
202 	// even writes set a22-a21; odd writes set a20-a13
203 	if (a0 == 0)
204 		bank = (bank & 0x1fe000) | ((data & 0x03) * 0x200000);
205 	else
206 		bank = (bank & 0x600000) | (data * 0x2000);
207 }
208 
kick_watchdog(int whichcpu)209 void namco_c117_device::kick_watchdog(int whichcpu)
210 {
211 	// FIXME: change to 3 once sound CPU watchdog is separated from this device
212 	static const int ALL_CPU_MASK = 7;
213 
214 	m_wdog |= (1 << whichcpu);
215 
216 	if (m_wdog == ALL_CPU_MASK || !m_subres)
217 	{
218 		m_wdog = 0;
219 		m_watchdog->watchdog_reset();
220 	}
221 }
222