1 // license:LGPL-2.1+
2 // copyright-holders:Michael Zapf
3 /*******************************************************************************
4 PGRAM(+) Memory expansion and GROM simulator
5
6 The PGRAM card is a battery-buffered RAM card. It also contains a circuitry
7 to simulate GROMs (TMC0430), which can also be programmed, thus better
8 called GRAMs.
9
10 Memory chips of the board:
11
12 u21: 43256LP (SRAM 32Kx8) -- GROMs 4-7
13 u22: 6264LP (SRAM 8Kx8) -- GROM 3
14 u23: 43256LP (SRAM 32Kx8) -- 16 KiB DSR (driver) / 16 KiB RAM
15
16 The PGRAM+ is an extension of this card, offering 192 KiB memory:
17
18 u21: e.g. BS62LV1027 (SRAM 128Kx8) -- GROMs 4-7 (4 banks)
19 u22: 43256LP (SRAM 32Kx8) -- GROM 3 (4 banks)
20 u23: 43256LP (SRAM 32Kx8) -- 16 KiB DSR (driver) / 16 KiB RAM
21
22 The socket for u21 is already prepared for the larger package of the
23 128Kx8 SRAM chip (32 pins). No modifications are needed; the smaller chip
24 can simply be plugged into the larger socket. For u22, a jumper is included
25 on the board for connecting the address line to the bus (PGRAM+)
26 or to high level (PGRAM).
27
28 A real-time clock is also included on the board:
29 u1: MM58167A
30
31 The GROM address counter is implemented by four 74LS161 chips.
32
33 A 3V battery is used to buffer the SRAMs.
34
35 The 74LS259 latch (u14) stores CRU settings and controls select lines.
36
37 Memory/port mapping
38 -------------------
39 The card can be configured to respond to CRU addresses 1000-1700 with the
40 exception of 1100, which is used by most disk controller cards. The
41 address is set by one of seven switches (SW1).
42
43 CRU bits (relative to start address):
44
45 0 - Map DSR into CPU space 4000-5FFF
46 1 - Enable GRAM and RAM
47 2 - Write protect GRAM/RAM
48 3 - DSR/RAM bank selection
49 4 - DSR/RAM bank selection (see below)
50
51 The GRAM simulation responds to the usual GROM address base 9800. However,
52 only GROMs 3-7 are simulated; GROMs 0-2 are located in the console. Setting
53 the GROM address is done by writing both bytes of the 16-bit address to
54 address 9C02 (high/low). Reading the address is not possible, but this is
55 provided by the console GROMs. Reading the contents of the GRAM simulation
56 is done by reading a byte from address 9800, which also advances the address
57 counter. Writing to the GRAM is achieved by writing to address 9C00.
58
59 For the PGRAM+ card, four GROM banks are available:
60 Bank 0: 98x0/9Cx0/9Cx2
61 Bank 1: 98x4/9Cx4/9Cx4
62 Bank 2: 98x8/9Cx8/9Cx8
63 Bank 3: 98xC/9CxC/9CxC
64
65 Due to incomplete decoding, the second digit from the right may take any
66 value.
67
68 The real-time clock is mapped to addresses 8640-865E (even addresses only).
69 8640 = Register 0
70 865e = Register 15
71
72 The 32K RAM circuit (u23) is used for the DSR (device service routine, i.e.
73 the driver) and 16 KiB RAM. The DSR area is located in the lower half of the
74 address space, while the RAM area is located in the upper half.
75
76 The DSR area is mapped as two selectable 8K blocks into 4000-5FFF.
77 The RAM area is mapped as two selectable 8K blocks into 6000-7FFF.
78
79 +------------+ 0000
80 | DSR bank 0 |
81 +------------+ 2000
82 | DSR bank 1 |
83 +------------+ 4000
84 | RAM bank 0 |
85 +------------+ 6000
86 | RAM bank 1 |
87 +------------+ 7FFF
88
89 The selection of either bank of the DSR space is done by writing a value
90 to an address of 4000/4004/4008... for selecting bank 0, and to 4002/4006/...
91 for selecting bank 1. For the RAM area, the same scheme is applied to
92 6000/6004/... and 6002/6006/... The selection is buffered by the flipflop
93 u3 for both RAM and DSR, hence it should make no difference whether to
94 write to 6000 or to 4000.
95
96 CRU bits 3 and 4 are fed into the PRE* and CLR* inputs of the flipflop.
97
98 Bits 3 4
99 0 0 -> not recommended (typically, bit 4 wins)
100 0 1 -> set lower 8K
101 1 0 -> set higher 8K
102 1 1 -> allow switching by write access
103
104 The PGRAM+ card does not allow for expanding this area. This is indeed a
105 limitation, as it does not allow us to store several cartridges with
106 a GROM and ROM part into the PGRAM+.
107
108 Switch SW2 deactivates the card in case the contents prevent a proper
109 startup of the TI console.
110
111 Michael Zapf
112 March 2020
113
114 *******************************************************************************/
115
116 #include "emu.h"
117 #include "pgram.h"
118
119 #define LOG_WARN (1U<<1)
120 #define LOG_WP (1U<<2)
121 #define LOG_DSR (1U<<3)
122 #define LOG_RAM (1U<<4)
123 #define LOG_GRAM (1U<<5)
124 #define LOG_GRAMADDR (1U<<6)
125 #define LOG_CRU (1U<<7)
126 #define LOG_BANK (1U<<8)
127
128 #define VERBOSE ( LOG_GENERAL | LOG_WARN )
129
130 #include "logmacro.h"
131
132 #define GRAM4567_TAG "u21_gram4567"
133 #define GRAM3_TAG "u22_gram3"
134 #define DSRRAM_TAG "u23_dsrram"
135 #define CLOCK_TAG "u1_rtc"
136 #define BANKFF_TAG "u3_bankff"
137 #define CRULATCH_TAG "u14_latch"
138
139 #define COUNT0_TAG "u13_counter"
140 #define COUNT1_TAG "u11_counter"
141 #define COUNT2_TAG "u12_counter"
142 #define COUNT3_TAG "u10_counter"
143
144 DEFINE_DEVICE_TYPE_NS(TI99_PGRAM, bus::ti99::peb, pgram_device, "ti99_pgram", "PGRAM(+) memory card")
145
146 namespace bus { namespace ti99 { namespace peb {
147
pgram_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)148 pgram_device::pgram_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock):
149 device_t(mconfig, TI99_PGRAM, tag, owner, clock),
150 device_ti99_peribox_card_interface(mconfig, *this),
151 m_gram3(*this, GRAM3_TAG),
152 m_gram4567(*this, GRAM4567_TAG),
153 m_dsrram(*this, DSRRAM_TAG),
154 m_clock(*this, CLOCK_TAG),
155 m_crulatch(*this, CRULATCH_TAG),
156 m_bankff(*this, BANKFF_TAG),
157 m_count0(*this, COUNT0_TAG),
158 m_count1(*this, COUNT1_TAG),
159 m_count2(*this, COUNT2_TAG),
160 m_count3(*this, COUNT3_TAG),
161 m_lowbyte(false)
162 {
163 }
164
165 /*
166 Read access to the card. This comprises reading from the RAM circuit for
167 DSR and free space, for GRAM reading, and the real-time clock.
168
169 Decoding is done by the 74ls138 and 74ls139 circuits on the board.
170 */
readz(offs_t offset,uint8_t * value)171 void pgram_device::readz(offs_t offset, uint8_t *value)
172 {
173 if (!m_active) return;
174
175 // GROMs: 1001 1w.. .... ..00
176 if ((offset & 0xfc03)==0x9800)
177 {
178 gram_read((offset>>2)&0x03, value);
179 }
180 else
181 {
182 // DSR/RAM: 01.
183 if ((offset & 0xc000)==0x4000)
184 {
185 dsr_ram_read(offset & 0x3fff, value);
186 }
187 else
188 {
189 // RTC: 1000 0110 01.. ...0
190 if ((offset & 0xffc1)==0x8640)
191 {
192 uint8_t val = m_clock->read((offset >> 1) & 0x001f);
193 *value = val;
194 }
195 }
196 }
197 }
198
199 /*
200 Write access to the card. Same comments apply as for the read access.
201 */
write(offs_t offset,uint8_t data)202 void pgram_device::write(offs_t offset, uint8_t data)
203 {
204 if (!m_active) return;
205
206 // GROMs: 1001 11.. .... ..a0
207 if ((offset & 0xfc01)==0x9c00)
208 {
209 if ((offset & 0x0002)!=0)
210 set_gram_address(data);
211 else
212 gram_write((offset>>2)&0x03, data);
213 }
214 else
215 {
216 // DSR/RAM: 01.
217 if ((offset & 0xc000)==0x4000)
218 dsr_ram_write(offset & 0x3fff, data);
219 else
220 {
221 // RTC: 1000 0110 01.. ...0
222 if ((offset & 0xffc1)==0x8640)
223 m_clock->write((offset >> 1) & 0x001f, data);
224 }
225 }
226 }
227
228 /*
229 Read access to the 32K RAM circuit (u23). This one is used to provide the
230 DSR (driver) in the memory area 4000-5fff, and to provide free space in the
231 area 6000-7fff. A second bank can be selected for both DSR and RAM using
232 CRU bits 3 and 4.
233 */
dsr_ram_read(offs_t offset,uint8_t * value)234 void pgram_device::dsr_ram_read(offs_t offset, uint8_t *value)
235 {
236 // ..0. .... .... .... = DSR
237 // ..1. .... .... .... = RAM
238 bool dsr = ((offset & 0x2000)==0);
239
240 // Note: On power-up, the ls259 latch is reset, and bits 4 and 3 lead 0
241 // to both PRE* and CLR* of the 7474. This would normally cause the bank
242 // select line to be 1 (unstable).
243
244 // Reset the clock
245 m_bankff->clock_w(1);
246
247 // Latch may have changed; update FF asynchronously
248 m_bankff->clear_w(m_crulatch->q3_r());
249 m_bankff->preset_w(m_crulatch->q4_r());
250
251 uint16_t base = (offset & 0x2000)<<1; // DSR:0000, RAM:4000
252 uint16_t address = base | (offset & 0x1fff) | (m_bankff->output_r()? 0x2000 : 0);
253
254 if ((dsr && m_crulatch->q0_r()) || (!dsr && m_crulatch->q1_r()))
255 {
256 *value = m_dsrram->read(address);
257
258 if (address&1)
259 {
260 uint16_t b0 = m_dsrram->read(address&0xfffe) << 8 | *value;
261
262 if (dsr)
263 LOGMASKED(LOG_DSR, "%04x (bank %d) -> %04x\n",(offset&0xfffe)|0x4000, m_bankff->output_r()? 1:0, b0);
264 else
265 LOGMASKED(LOG_RAM, "%04x (bank %d) -> %04x\n",(offset&0xfffe)|0x4000, m_bankff->output_r()? 1:0, b0);
266 }
267 }
268 }
269
270 /*
271 Write access to the 32K RAM circuit (u23). See above for information about
272 the DSR and RAM space.
273 A flipflop (7474) is used to control access to the banks of both DSR and
274 RAM. The state of the flipflop is controlled by CRU bits 3 and 4. When both
275 bits are set, the flipflop can be set or reset by address line 14 (2^1).
276 */
dsr_ram_write(offs_t offset,uint8_t data)277 void pgram_device::dsr_ram_write(offs_t offset, uint8_t data)
278 {
279 // ..0. .... .... .... = DSR
280 // ..1. .... .... .... = RAM
281 bool dsr = ((offset & 0x2000)==0);
282
283 if ((dsr && m_crulatch->q0_r()) || (!dsr && m_crulatch->q1_r()))
284 {
285 // Get the current settings of the flipflop from the latch
286 m_bankff->clear_w(m_crulatch->q3_r());
287 m_bankff->preset_w(m_crulatch->q4_r());
288
289 int oldff = m_bankff->output_r();
290
291 // A14 is led to the flipflop D input
292 m_bankff->d_w((offset & 2)>>1); // A14
293 m_bankff->clock_w(0);
294
295 if (m_crulatch->q2_r()==0) // not write-protected
296 {
297 uint16_t base = (offset & 0x2000)<<1;
298 uint16_t address = base | (offset & 0x1fff) | (m_bankff->output_r()? 0x2000 : 0);
299
300 m_dsrram->write(address, data);
301 if (base==0)
302 LOGMASKED(LOG_DSR, "%04x (bank %d) <- %02x\n", offset|0x4000, m_bankff->output_r()? 1:0, data);
303 else
304 LOGMASKED(LOG_RAM, "%04x (bank %d) <- %02x\n", offset|0x4000, m_bankff->output_r()? 1:0, data);
305 }
306
307 m_bankff->clock_w(1);
308 if (m_bankff->output_r() != oldff)
309 LOGMASKED(LOG_BANK, "Switch to bank %d (%04x)\n", m_bankff->output_r(), offset|0x4000);
310 }
311 }
312
313 /*
314 Read access to the GRAM emulation. The GRAM space is split between
315 two memory circuits, one for G3 (8K) and one for the other four GRAMs (32K).
316 For PGRAM+, we habe 32K + 128K (four banks).
317 When the GROM address is outside the area for G3-G7, the access is ignored.
318 GRAM access requires setting CRU bit 1 before.
319 */
gram_read(offs_t offset,uint8_t * value)320 void pgram_device::gram_read(offs_t offset, uint8_t *value)
321 {
322 // Don't let the debugger mess with the GRAM emulation
323 if (machine().side_effects_disabled())
324 {
325 *value = 0;
326 return;
327 }
328
329 if (m_crulatch->q1_r())
330 {
331 // Reset the clock
332 m_bankff->clock_w(1);
333
334 clock_gram_counter(0);
335 uint16_t gaddress = get_gram_address();
336
337 // GROM3: 011. .... .... ....
338 // Checked by u9
339 if ((gaddress & 0xe000)==0x6000)
340 {
341 offs_t gfull = (gaddress & 0x1fff) | (m_pgramplus? (offset << 13) : 0);
342 *value = m_gram3->read(gfull);
343 LOGMASKED(LOG_GRAM, "gram(%04x,%04x) -> %02x\n", gaddress, gfull, *value);
344 }
345
346 // GROM4-7: 1... .... .... ....
347 // Checked by u9 + u8
348 if ((gaddress & 0x8000)!=0)
349 {
350 offs_t gfull = (gaddress & 0x7fff) | (m_pgramplus? (offset << 15) : 0);
351 *value = m_gram4567->read(gfull);
352 LOGMASKED(LOG_GRAM, "gram(%04x,%05x) -> %02x\n", gaddress, gfull, *value);
353 }
354 clock_gram_counter(1);
355 }
356 }
357
358 /*
359 Write access to the GRAM emulation. Same comments apply as for gram_read.
360 */
gram_write(offs_t offset,uint8_t data)361 void pgram_device::gram_write(offs_t offset, uint8_t data)
362 {
363 // Don't let the debugger mess with the GRAM emulation
364 if (machine().side_effects_disabled())
365 {
366 return;
367 }
368
369 if (m_crulatch->q1_r())
370 {
371 clock_gram_counter(0);
372 uint16_t gaddress = get_gram_address();
373
374 // GROM3: 011. .... .... ....
375 if ((gaddress & 0xe000)==0x6000)
376 {
377 offs_t gfull = (gaddress & 0x1fff) | (m_pgramplus? (offset << 13) : 0);
378 m_gram3->write(gfull, data);
379 LOGMASKED(LOG_GRAM, "gram(%04x,%04x) <- %02x\n", gaddress, gfull, data);
380 }
381
382 // GROM4-7: 1... .... .... ....
383 if ((gaddress & 0x8000)!=0)
384 {
385 offs_t gfull = (gaddress & 0x7fff) | (m_pgramplus? (offset << 15) : 0);
386 m_gram4567->write(gfull, data);
387 LOGMASKED(LOG_GRAM, "gram(%04x,%05x) <- %02x\n", gaddress, gfull, data);
388 }
389 clock_gram_counter(1);
390 }
391 }
392
393 /*
394 Set the address digits in the counters. Address setting is done by two
395 consecutive byte writes, where the currently stored digits in u13 and u11
396 are transferred to u12 and u10, and u13 and u11 get their new values
397 from the data bus.
398
399 D0-D3 -> u11 -> u10
400 D4-D7 -> u13 -> u12
401 */
set_gram_address(uint8_t data)402 void pgram_device::set_gram_address(uint8_t data)
403 {
404 if (m_crulatch->q1_r())
405 {
406 set_load_gram_counter(0);
407 clock_gram_counter(0);
408 m_count0->p_w(data&0x0f);
409 m_count1->p_w((data>>4)&0x0f);
410 m_count2->p_w(m_count0->output_r());
411 m_count3->p_w(m_count1->output_r());
412 clock_gram_counter(1);
413 set_load_gram_counter(1);
414 m_lowbyte = !m_lowbyte;
415 }
416 }
417
418 /*
419 Set the clock signal for all four counter chips. Propagate carry bits.
420 */
clock_gram_counter(int state)421 void pgram_device::clock_gram_counter(int state)
422 {
423 if (m_crulatch->q1_r())
424 {
425 m_count1->cet_w(m_count0->tc_r());
426 m_count2->cet_w(m_count1->tc_r());
427 m_count3->cet_w(m_count2->tc_r());
428
429 m_count0->clock_w(state);
430 m_count1->clock_w(state);
431 m_count2->clock_w(state);
432 m_count3->clock_w(state);
433
434 // Debugging
435 if (state==1 && m_lowbyte)
436 {
437 uint16_t gaddr = get_gram_address();
438 if (gaddr >= 0x6000) LOGMASKED(LOG_GRAMADDR, "gaddr=%04x\n", gaddr);
439 }
440 }
441 }
442
443 /*
444 Convenience method to retrieve the current GRAM address.
445 */
get_gram_address()446 uint16_t pgram_device::get_gram_address()
447 {
448 return (m_count3->output_r()<<12) | (m_count2->output_r()<<8) | (m_count1->output_r()<<4) | m_count0->output_r();
449 }
450
451 /*
452 Set/reset the PE* bit of all 74LS161 curcuits.
453 */
set_load_gram_counter(int state)454 void pgram_device::set_load_gram_counter(int state)
455 {
456 m_count0->pe_w(state);
457 m_count1->pe_w(state);
458 m_count2->pe_w(state);
459 m_count3->pe_w(state);
460 }
461
462 /*
463 CRU write access to the LS259 latch.
464 */
cruwrite(offs_t offset,uint8_t data)465 void pgram_device::cruwrite(offs_t offset, uint8_t data)
466 {
467 // ...1 0xxx .... ...0
468 bool selected = ((offset & 0xff01)==m_crubase);
469 if (selected)
470 {
471 uint8_t bit = (offset & 0xfe)>>1;
472 LOGMASKED(LOG_CRU, "cru %04x (bit %d) <- %d\n", offset, bit , data);
473 if (bit==3 || bit==4) LOGMASKED(LOG_BANK, "cru bit %d <- %d\n", bit, data);
474 }
475
476 m_crulatch->enable_w(selected? 0 : 1);
477 m_crulatch->write_abcd((offset>>1)&0x07, data);
478 }
479
480 /*
481 Device construction.
482 */
device_add_mconfig(machine_config & config)483 void pgram_device::device_add_mconfig(machine_config& config)
484 {
485 // 4-bit counters
486 TTL74161(config, m_count0); // u13
487 TTL74161(config, m_count1); // u11
488 TTL74161(config, m_count2); // u12
489 TTL74161(config, m_count3); // u10
490
491 // Hardwired connections
492 m_count0->set_cet_pin_value(1);
493
494 m_count0->set_cep_pin_value(1);
495 m_count1->set_cep_pin_value(1);
496 m_count2->set_cep_pin_value(1);
497 m_count3->set_cep_pin_value(1);
498
499 // CRU latch
500 LS259(config, m_crulatch); // u14
501
502 // Bank switch
503 TTL7474(config, m_bankff, 0);
504
505 // We allocate the space for PGRAM+
506 BUFF_RAM(config, GRAM4567_TAG, 0).set_size(128*1024);
507 BUFF_RAM(config, GRAM3_TAG, 0).set_size(32*1024);
508 BUFF_RAM(config, DSRRAM_TAG, 0).set_size(32*1024);
509
510 // Real-time clock
511 MM58167(config, CLOCK_TAG, 32.768_kHz_XTAL);
512 }
513
device_start()514 void pgram_device::device_start()
515 {
516 }
517
device_reset()518 void pgram_device::device_reset()
519 {
520 m_active = (ioport("SW2")->read()==1);
521 m_crubase = (ioport("SW1")->read()<<8) | 0x1000;
522 m_pgramplus = (ioport("SIZE")->read()==1);
523 }
524
INPUT_CHANGED_MEMBER(pgram_device::sw1_changed)525 INPUT_CHANGED_MEMBER( pgram_device::sw1_changed )
526 {
527 // CRU base changed
528 m_crubase = (newval << 8) | 0x1000;
529 }
530
INPUT_CHANGED_MEMBER(pgram_device::sw2_changed)531 INPUT_CHANGED_MEMBER( pgram_device::sw2_changed )
532 {
533 // Activation switch changed
534 m_active = (newval==1);
535 }
536
537 INPUT_PORTS_START( pgram_switches )
538 PORT_START( "SIZE" )
539 PORT_CONFNAME( 0x01, 0x00, "Memory size") // cannot be changed during runtime
540 PORT_CONFSETTING(0x00, "PGRAM 72K")
541 PORT_CONFSETTING(0x01, "PGRAM+ 192K")
542
543 PORT_START( "SW2" )
544 PORT_DIPNAME( 0x01, 0x01, "Activate switch" ) PORT_CHANGED_MEMBER(DEVICE_SELF, pgram_device, sw2_changed, 0)
DEF_STR(Off)545 PORT_DIPSETTING(0x00, DEF_STR( Off ))
546 PORT_DIPSETTING(0x01, DEF_STR( On ))
547
548 PORT_START( "SW1" )
549 PORT_DIPNAME( 0x07, 0x00, "CRU base" ) PORT_CHANGED_MEMBER(DEVICE_SELF, pgram_device, sw1_changed, 0)
550 PORT_DIPSETTING( 0x00, "1000")
551 PORT_DIPSETTING( 0x02, "1200")
552 PORT_DIPSETTING( 0x03, "1300")
553 PORT_DIPSETTING( 0x04, "1400")
554 PORT_DIPSETTING( 0x05, "1500")
555 PORT_DIPSETTING( 0x06, "1600")
556 PORT_DIPSETTING( 0x07, "1700")
557 INPUT_PORTS_END
558
559 ioport_constructor pgram_device::device_input_ports() const
560 {
561 return INPUT_PORTS_NAME( pgram_switches );
562 }
563
564 } } } // end namespace bus::ti99::peb
565