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