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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
|
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
imm4-22 Instruction/Data Storage Module
This card has four 4002 RAMs configured as a page, sockets for four
1702A or similar 256×8 PROMs configured as a ROM page, buffers for four
4-bit input ports, and latches for four 4-bit output ports.
The 4002 RAMs may be jumpered to be selected by CM-RAM1, CM-RAM2 or
CM-RAM3. Note that the CM-RAM lines are not decoded, so the 4002 RAMs
will be mirrored. (With two or more imm4-22 cards installed, the 4002
RAMs will fight for the bus if pages 4-7 are selected.)
The PROMs and I/O ports may be jumpered to be mapped at 0x04xx...0x07xx,
0x08xx...0x0bff or 0x0cff...0x0fff. Additionally, the card may be
jumpered to enable the PROMs and I/O ports only when /ENABLE MON PROM is
asserted, or at all times.
This card may seem like a good way to expand three aspects of the system
at once, but in practice the PROMs are of limited use:
* If jumpered to enable the PROMs at all times, the PROMs will fight for
the bus if console RAM card program storage is selected.
* If jumpered to enable the PROMs only when /ENABLE MON PROM is
asserted, the PROMs become effectively inaccessible as the boot vector
is in the onboard monitor PROM region, and the onboard monitor PROM
program provides no way to jump to or read from arbitrary locations.
This card was probably most useful as the cheapest way to double RAM and
I/O ports. Without PROMs installed, mapping I/O at 0x0400...0x07ff only
when /ENABLE MON PROM is asserted would be a convenient way to provide
the necessary connections for an imm4-90 high-speed paper tape reader.
Image file for the PROMs must be an exact multiple of 256 bytes. PROMs
are assumed to be filled in order from lowest to highest address.
Jumper changes only take effect on hard reset.
P1 Universal Slot edge connector
1 2
GND 3 4 GND
5 6
7 8
9 10
MA0 11 12 MA1
MA2 13 14 MA3
MA4 15 16 MA5
MA6 17 18 MA7
C0 19 20 C1
21 22
/MDI0 23 24
/MDI1 25 26
/MDI3 27 28
/MDI2 29 30
/MDI5 31 32
/MDI4 33 34
/MDI7 35 36
/MDI6 37 38
39 40
/OUT 41 42 /ENABLE MON PROM
-10V 43 44 -10V
45 46
/CM-RAM2 47 48 /CM-RAM3
49 50 /CM-RAM1
I/O 1 51 52 I/O 0
I/O 2 53 54 /IN
55 56 I/O 3
57 58
59 60
61 62
63 64
65 66
67 68
69 70
71 72 /D3
73 74
75 76 /D2
77 78
/SYNC 79 80 /D1
81 82
/D0 83 84
85 86
87 88 /RESET-4002
89 90
91 92
/CM-ROM 93 94 C3
95 96 C2
PHASE 2 97 98 PHASE 1
+5V 99 100 +5V
P1 inputs (40-pin IDC)
ROM 4/8/12 IN0 1 2 ROM 4/8/12 IN1
ROM 4/8/12 IN2 3 4 ROM 4/8/12 IN3
GND 5 6 ROM 5/9/13 IN0
ROM 5/9/13 IN1 7 8 ROM 5/9/13 IN2
ROM 5/9/13 IN3 9 10 GND
ROM 6/10/14 IN0 11 12 ROM 6/10/14 IN1
ROM 6/10/14 IN2 13 14 ROM 6/10/14 IN3
GND 15 16 ROM 7/11/15 IN0
ROM 7/11/15 IN1 17 18 ROM 7/11/15 IN2
ROM 7/11/15 IN3 19 20 GND
21 22
23 24
25 26
27 28
29 30
31 32
33 34
35 36
37 38
39 40
P2 outputs (40-pin IDC)
ROM 4/8/12 OUT0 1 2 ROM 4/8/12 OUT1
ROM 4/8/12 OUT2 3 4 ROM 4/8/12 OUT3
GND 5 6 ROM 5/9/13 OUT0
ROM 5/9/13 OUT1 7 8 ROM 5/9/13 OUT2
ROM 5/9/13 OUT3 9 10 GND
ROM 6/10/14 OUT0 11 12 ROM 6/10/14 OUT1
ROM 6/10/14 OUT2 13 14 ROM 6/10/14 OUT3
GND 15 16 ROM 7/11/15 OUT0
ROM 7/11/15 OUT1 17 18 ROM 7/11/15 OUT2
ROM 7/11/15 OUT3 19 20 GND
RAM 4/8/12 OUT0 21 22 RAM 4/8/12 OUT1
RAM 4/8/12 OUT2 23 24 RAM 4/8/12 OUT3
GND 25 26 RAM 5/9/13 OUT0
RAM 5/9/13 OUT1 27 28 RAM 5/9/13 OUT2
RAM 5/9/13 OUT3 29 30 GND
RAM 6/10/14 OUT0 31 32 RAM 6/10/14 OUT1
RAM 6/10/14 OUT2 33 34 RAM 6/10/14 OUT3
GND 35 36 RAM 7/11/15 OUT0
RAM 7/11/15 OUT1 37 38 RAM 7/11/15 OUT2
RAM 7/11/15 OUT3 39 40 GND
*/
#include "emu.h"
#include "insdatastor.h"
namespace {
INPUT_PORTS_START(imm4_22)
PORT_START("JUMPERS")
PORT_CONFNAME( 0x03, 0x01, "4002 RAM page" )
PORT_CONFSETTING( 0x01, "1" )
PORT_CONFSETTING( 0x02, "2" )
PORT_CONFSETTING( 0x03, "3" )
PORT_CONFNAME( 0x0c, 0x04, "PROM/GPIO page" )
PORT_CONFSETTING( 0x04, "0x400-0x7FF" )
PORT_CONFSETTING( 0x08, "0x800-0xBFF" )
PORT_CONFSETTING( 0x0c, "0xC00-0xFFF" )
PORT_CONFNAME( 0x10, 0x00, "PROM/GPIO mode" )
PORT_CONFSETTING( 0x00, "All" )
PORT_CONFSETTING( 0x10, "MON only" )
INPUT_PORTS_END
class imm4_22_device
: public device_t
, public bus::intellec4::device_univ_card_interface
, public device_image_interface
{
public:
imm4_22_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
virtual image_init_result call_load() override;
virtual void call_unload() override;
virtual bool is_readable() const noexcept override { return true; }
virtual bool is_writeable() const noexcept override { return false; }
virtual bool is_creatable() const noexcept override { return false; }
virtual bool is_reset_on_load() const noexcept override { return false; }
virtual char const *file_extensions() const noexcept override { return "rom,bin"; }
virtual char const *image_type_name() const noexcept override { return "promimage"; }
virtual char const *image_brief_type_name() const noexcept override { return "prom"; }
protected:
virtual ioport_constructor device_input_ports() const override;
virtual void device_start() override;
virtual void device_reset() override;
virtual DECLARE_WRITE_LINE_MEMBER(reset_4002_in) override;
private:
void ram_out(offs_t offset, u8 data);
void rom_out(offs_t offset, u8 data);
u8 rom_in(offs_t offset);
void allocate();
void map_ram_io();
void map_prom();
void unmap_prom();
required_ioport m_jumpers;
bool m_ram_io_mapped;
u8 m_ram_page, m_rom_page;
bool m_rom_mirror;
u8 m_memory[256], m_status[64];
std::unique_ptr<u8 []> m_prom;
};
imm4_22_device::imm4_22_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
: device_t(mconfig, INTELLEC4_INST_DATA_STORAGE, tag, owner, clock)
, bus::intellec4::device_univ_card_interface(mconfig, *this)
, device_image_interface(mconfig, *this)
, m_jumpers(*this, "JUMPERS")
, m_ram_io_mapped(false)
, m_ram_page(0U), m_rom_page(0U), m_rom_mirror(false)
, m_prom()
{
std::fill(std::begin(m_memory), std::end(m_memory), 0U);
std::fill(std::begin(m_status), std::end(m_status), 0U);
}
image_init_result imm4_22_device::call_load()
{
if ((length() > 1024U) || (length() % 256U))
return image_init_result::FAIL;
allocate();
if (fread(m_prom.get(), length()) != length())
return image_init_result::FAIL;
map_prom();
return image_init_result::PASS;
}
void imm4_22_device::call_unload()
{
unmap_prom();
}
ioport_constructor imm4_22_device::device_input_ports() const
{
return INPUT_PORTS_NAME(imm4_22);
}
void imm4_22_device::device_start()
{
m_ram_io_mapped = false;
allocate();
save_item(NAME(m_ram_page));
save_item(NAME(m_rom_page));
save_item(NAME(m_rom_mirror));
save_item(NAME(m_memory));
save_item(NAME(m_status));
save_pointer(NAME(m_prom), 1024U);
}
void imm4_22_device::device_reset()
{
map_ram_io();
}
WRITE_LINE_MEMBER(imm4_22_device::reset_4002_in)
{
// FIXME: this takes several cycles to actually erase everything, and prevents writes while asserted
if (!state)
{
std::fill(std::begin(m_memory), std::end(m_memory), 0U);
std::fill(std::begin(m_status), std::end(m_status), 0U);
}
}
void imm4_22_device::ram_out(offs_t offset, u8 data)
{
// GPIO write - hooking this up would be a pain with MAME as it is
logerror("4002 A%u out %X\n", 13U + (offset & 0x03U), data & 0x0fU);
}
void imm4_22_device::rom_out(offs_t offset, u8 data)
{
// GPIO write - hooking this up would be a pain with MAME as it is
logerror("ROM %u out %X\n", (m_rom_page << 2) | ((offset >> 4) & 0x03U), data & 0x0fU);
}
u8 imm4_22_device::rom_in(offs_t offset)
{
// GPIO read - hooking this up would be a pain with MAME as it is
if (!machine().side_effects_disabled())
logerror("ROM %u in\n", (m_rom_page << 2) | ((offset >> 4) & 0x03U));
return 0x0fU;
}
void imm4_22_device::allocate()
{
if (!m_prom)
{
m_prom = std::make_unique<u8 []>(1024U);
std::fill(m_prom.get(), m_prom.get() + 1024U, 0U);
}
}
void imm4_22_device::map_ram_io()
{
if (!m_ram_io_mapped)
{
static constexpr offs_t ram_start[3][4] = {
{ 0x01U, 0x04U, 0x05U, 0x07U },
{ 0x02U, 0x04U, 0x06U, 0x07U },
{ 0x03U, 0x05U, 0x06U, 0x07U } };
m_ram_io_mapped = true;
ioport_value const jumpers(m_jumpers->read());
m_ram_page = jumpers & 0x03U;
m_rom_page = (jumpers >> 2) & 0x03U;
m_rom_mirror = BIT(~jumpers, 4);
if ((1U > m_ram_page) || (3U < m_ram_page))
throw emu_fatalerror("imm4_22_device: invalid RAM page %u", m_ram_page);
if ((1U > m_rom_page) || (3U < m_rom_page))
throw emu_fatalerror("imm4_22_device: invalid PROM/GPIO page %u", m_rom_page);
for (offs_t const start : ram_start[m_ram_page - 1U])
{
memory_space().install_ram(start << 8, (start << 8) | 0x00ffU, m_memory);
status_space().install_ram(start << 6, (start << 6) | 0x003fU, m_status);
ram_ports_space().install_write_handler(start << 2, (start << 2) | 0x03U, write8sm_delegate(*this, FUNC(imm4_22_device::ram_out)));
}
offs_t const rom_ports_start(offs_t(m_rom_page) << 6);
offs_t const rom_ports_end(rom_ports_start | 0x003fU);
offs_t const rom_ports_mirror(m_rom_mirror ? 0x1f00U : 0x0700U);
rom_ports_space().install_readwrite_handler(
rom_ports_start, rom_ports_end, 0U, rom_ports_mirror, 0U,
read8sm_delegate(*this, FUNC(imm4_22_device::rom_in)), write8sm_delegate(*this, FUNC(imm4_22_device::rom_out)));
if (is_loaded())
map_prom();
}
}
void imm4_22_device::map_prom()
{
if (m_ram_io_mapped)
{
// FIXME: gimme a cookie!
// FIXME: if mirror is on, this will fight console RAM for the bus in 0x2000 region and also appear in the "two switches" 0x3000 region
rom_space().install_rom(
offs_t(m_rom_page) << 10, offs_t((offs_t(m_rom_page) << 10) | length()), m_rom_mirror ? 0x1000U : 0x0000U,
m_prom.get());
}
}
void imm4_22_device::unmap_prom()
{
// FIXME: where's my magic cookie?
rom_space().unmap_read(offs_t(m_rom_page) << 10, (offs_t(m_rom_page) << 10) | 0x03ffU, m_rom_mirror ? 0x1000U : 0x0000U);
}
} // anonymous namespace
DEFINE_DEVICE_TYPE_PRIVATE(INTELLEC4_INST_DATA_STORAGE, bus::intellec4::device_univ_card_interface, imm4_22_device, "intlc4_imm4_22", "Intel imm4-22 Instruction/Data Storage Module")
|