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