1 // license:BSD-3-Clause
2 // copyright-holders:Patrick Mackinlay
3 
4 /*
5  * An implementation of the Brooktree Bt45x family (except the Bt459) of RAMDAC
6  * devices. The 453, 454 and 455 are simpler, and have no command or control
7  * registers, whereas the others in the family use these to support additional
8  * options such as multiplexing, blinking etc. Otherwise the devices are quite
9  * similar, and vary only in terms of maximum clock speed, number of color or
10  * overlay bits, and quantity and number bits of resolution of DAC output.
11  *
12  *           Max.       Input          Output
13  *   Part    Clock  Color  Overlay  Num.  Levels  Other
14  *   Bt451  165MHz  8 bit   2 bit    3     4 bit  blinking, multiplexing
15  *   Bt453   66MHz  8 bit   2 bit    3     8 bit
16  *   Bt454  170MHz  4 bit   1 bit    3     4 bit
17  *   Bt455  170MHz  4 bit   1 bit    1     4 bit
18  *   Bt457  165Mhz  8 bit   2 bit    1     8 bit  blinking, multiplexing
19  *   Bt458  165MHz  8 bit   2 bit    3     8 bit  blinking, multiplexing
20  *   Bt467  220MHz  8 bit   2 bit    3     8 bit  blinking, multiplexing [NOTE: Specs are assumed based on Bt458 compatibility)
21  *
22  * Reference: http://www.bitsavers.org/components/brooktree/_dataBooks/1991_Brooktree_Product_Databook.pdf
23  *
24  * The bt45x_mono_device_base uses the standard red/green/blue read/write
25  * cycles defined in the databook, with color data active on the green cycle.
26  *
27  * The Bt467 is specified in its datasheet as register-compatible with the Bt458.
28  * As such, it is currently implemented as a simple alias of the Bt458.
29  *
30  * TODO
31  *   - refactor to separate devices with registers
32  *   - implement blinking and rgb device overlay
33  *   - unsure about address masking when accessing overlay colors
34  */
35 
36 #include "emu.h"
37 #include "bt45x.h"
38 
39 #define LOG_READS   (1U << 0)
40 #define LOG_WRITES  (1U << 1)
41 
42 #define VERBOSE (0)
43 
44 #include "logmacro.h"
45 
46 DEFINE_DEVICE_TYPE(BT451, bt451_device, "bt451", "Brooktree Bt451 256 Color RAMDAC")
47 DEFINE_DEVICE_TYPE(BT453, bt453_device, "bt453", "Brooktree Bt453 256 Color RAMDAC")
48 DEFINE_DEVICE_TYPE(BT454, bt454_device, "bt454", "Brooktree Bt454 16 Color RAMDAC")
49 DEFINE_DEVICE_TYPE(BT455, bt455_device, "bt455", "Brooktree Bt455 16 Color RAMDAC")
50 DEFINE_DEVICE_TYPE(BT457, bt457_device, "bt457", "Brooktree Bt457 256 Color RAMDAC")
51 DEFINE_DEVICE_TYPE(BT458, bt458_device, "bt458", "Brooktree Bt458 256 Color RAMDAC")
52 DEFINE_DEVICE_TYPE(BT467, bt467_device, "bt467", "Brooktree Bt467 256 Color RAMDAC")
53 
map(address_map & map)54 void bt45x_device_base::map(address_map &map)
55 {
56 	map(0x00, 0x00).rw(FUNC(bt45x_device_base::address_r), FUNC(bt45x_device_base::address_w));
57 	map(0x01, 0x01).rw(FUNC(bt45x_device_base::palette_r), FUNC(bt45x_device_base::palette_w));
58 	map(0x02, 0x02).rw(FUNC(bt45x_device_base::register_r), FUNC(bt45x_device_base::register_w));
59 	map(0x03, 0x03).rw(FUNC(bt45x_device_base::overlay_r), FUNC(bt45x_device_base::overlay_w));
60 }
61 
map(address_map & map)62 void bt453_device::map(address_map &map)
63 {
64 	map(0x00, 0x00).rw(FUNC(bt453_device::address_r), FUNC(bt453_device::address_w));
65 	map(0x01, 0x01).rw(FUNC(bt453_device::palette_r), FUNC(bt453_device::palette_w));
66 	map(0x02, 0x02).rw(FUNC(bt453_device::address_r), FUNC(bt453_device::address_w));
67 	map(0x03, 0x03).rw(FUNC(bt453_device::overlay_r), FUNC(bt453_device::overlay_w));
68 }
69 
map(address_map & map)70 void bt454_device::map(address_map &map)
71 {
72 	map(0x00, 0x00).rw(FUNC(bt454_device::address_r), FUNC(bt454_device::address_w));
73 	map(0x01, 0x01).rw(FUNC(bt454_device::palette_r), FUNC(bt454_device::palette_w));
74 	// FIXME: not clear what happens here
75 	//map(0x02, 0x02).rw(FUNC(bt454_device::address_r), FUNC(bt454_device::address_w));
76 	map(0x03, 0x03).rw(FUNC(bt454_device::overlay_r), FUNC(bt454_device::overlay_w));
77 }
78 
map(address_map & map)79 void bt455_device::map(address_map &map)
80 {
81 	map(0x00, 0x00).rw(FUNC(bt455_device::address_r), FUNC(bt455_device::address_w));
82 	map(0x01, 0x01).rw(FUNC(bt455_device::palette_r), FUNC(bt455_device::palette_w));
83 	// FIXME: not clear what happens here
84 	//map(0x02, 0x02).rw(FUNC(bt455_device::address_r), FUNC(bt455_device::address_w));
85 	map(0x03, 0x03).rw(FUNC(bt455_device::overlay_r), FUNC(bt455_device::overlay_w));
86 }
87 
bt45x_device_base(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,const u32 palette_colors,const u32 overlay_colors)88 bt45x_device_base::bt45x_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const u32 palette_colors, const u32 overlay_colors)
89 	: device_t(mconfig, type, tag, owner, clock)
90 	, m_palette_colors(palette_colors)
91 	, m_overlay_colors(overlay_colors)
92 {
93 }
94 
bt45x_rgb_device_base(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,const u32 palette_colors,const u32 overlay_colors)95 bt45x_rgb_device_base::bt45x_rgb_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const u32 palette_colors, const u32 overlay_colors)
96 	: bt45x_device_base(mconfig, type, tag, owner, clock, palette_colors, overlay_colors)
97 	, device_palette_interface(mconfig, *this)
98 {
99 }
100 
bt45x_mono_device_base(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,const u32 palette_colors,const u32 overlay_colors)101 bt45x_mono_device_base::bt45x_mono_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const u32 palette_colors, const u32 overlay_colors)
102 	: bt45x_device_base(mconfig, type, tag, owner, clock, palette_colors, overlay_colors)
103 {
104 }
105 
bt451_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)106 bt451_device::bt451_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
107 	: bt45x_rgb_device_base(mconfig, BT451, tag, owner, clock, 256, 4)
108 {
109 }
110 
bt453_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)111 bt453_device::bt453_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
112 	: bt45x_rgb_device_base(mconfig, BT453, tag, owner, clock, 256, 4)
113 {
114 }
115 
bt454_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)116 bt454_device::bt454_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
117 	: bt45x_rgb_device_base(mconfig, BT454, tag, owner, clock, 16, 1)
118 {
119 }
120 
bt455_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)121 bt455_device::bt455_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
122 	: bt45x_mono_device_base(mconfig, BT455, tag, owner, clock, 16, 1)
123 {
124 }
bt457_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)125 bt457_device::bt457_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
126 	: bt45x_mono_device_base(mconfig, BT457, tag, owner, clock, 256, 4)
127 {
128 }
129 
bt458_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)130 bt458_device::bt458_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
131 	: bt45x_rgb_device_base(mconfig, type, tag, owner, clock, 256, 4)
132 {
133 }
134 
bt458_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)135 bt458_device::bt458_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
136 	: bt458_device(mconfig, BT458, tag, owner, clock)
137 {
138 }
139 
bt467_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)140 bt467_device::bt467_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
141 	: bt458_device(mconfig, BT467, tag, owner, clock)
142 {
143 }
144 
device_start()145 void bt45x_device_base::device_start()
146 {
147 	save_item(NAME(m_address));
148 	save_item(NAME(m_address_rgb));
149 
150 	save_item(NAME(m_read_mask));
151 	save_item(NAME(m_blink_mask));
152 	save_item(NAME(m_command));
153 	save_item(NAME(m_control));
154 
155 	save_item(NAME(m_blink_start));
156 }
157 
device_start()158 void bt45x_rgb_device_base::device_start()
159 {
160 	bt45x_device_base::device_start();
161 
162 	m_color_ram = std::make_unique<std::array<u8, 3>[]>(m_palette_colors + m_overlay_colors);
163 
164 	save_pointer(NAME(m_color_ram), m_palette_colors + m_overlay_colors);
165 }
166 
device_start()167 void bt45x_mono_device_base::device_start()
168 {
169 	bt45x_device_base::device_start();
170 
171 	m_color_ram = std::make_unique<u8[]>(m_palette_colors + m_overlay_colors);
172 
173 	save_pointer(NAME(m_color_ram), m_palette_colors + m_overlay_colors);
174 }
175 
device_reset()176 void bt45x_device_base::device_reset()
177 {
178 	m_blink_start = -1;
179 }
180 
address_r()181 u8 bt45x_device_base::address_r()
182 {
183 	LOGMASKED(LOG_READS, "%s: address_r 0x%02x\n", machine().describe_context(), m_address & (m_palette_colors - 1));
184 
185 	if (!machine().side_effects_disabled())
186 		m_address_rgb = 0;
187 
188 	return m_address & (m_palette_colors - 1);
189 }
190 
address_w(u8 data)191 void bt45x_device_base::address_w(u8 data)
192 {
193 	LOGMASKED(LOG_WRITES, "%s: address_w 0x%02x\n", machine().describe_context(), data);
194 
195 	m_address_rgb = 0;
196 
197 	m_address = data & (m_palette_colors - 1);
198 }
199 
increment_address(const bool side_effects)200 void bt45x_device_base::increment_address(const bool side_effects)
201 {
202 	if (!machine().side_effects_disabled() || side_effects)
203 	{
204 		// increment component index and address register
205 		m_address_rgb = (m_address_rgb + 1) % 3;
206 		if (m_address_rgb == 0)
207 			m_address = (m_address + 1) & (m_palette_colors - 1);
208 	}
209 }
210 
increment_address(const bool side_effects)211 void bt457_device::increment_address(const bool side_effects)
212 {
213 	if (!machine().side_effects_disabled() || side_effects)
214 	{
215 		// check for rgb mode
216 		if (m_control & RGB)
217 		{
218 			// increment component index and address register
219 			m_address_rgb = (m_address_rgb + 1) % 3;
220 			if (m_address_rgb == 0)
221 				m_address++;
222 		}
223 		else
224 			m_address++;
225 	}
226 }
227 
palette_r(address_space & space)228 u8 bt45x_rgb_device_base::palette_r(address_space &space)
229 {
230 	const u8 data = m_color_ram[m_address][m_address_rgb];
231 
232 	increment_address();
233 
234 	LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data & get_mask());
235 
236 	return data & get_mask();
237 }
238 
palette_r(address_space & space)239 u8 bt45x_mono_device_base::palette_r(address_space &space)
240 {
241 	u8 data = space.unmap();
242 
243 	if (m_address_rgb == 1)
244 		data = m_color_ram[m_address];
245 
246 	increment_address();
247 
248 	LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data & get_mask());
249 
250 	return data & get_mask();
251 }
252 
palette_r(address_space & space)253 u8 bt457_device::palette_r(address_space &space)
254 {
255 	u8 data = space.unmap();
256 
257 	// normal mode or rgb mode and selected
258 	if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb))
259 		data = m_color_ram[m_address];
260 
261 	increment_address();
262 
263 	LOGMASKED(LOG_READS, "%s: palette_r 0x%02x\n", machine().describe_context(), data);
264 
265 	return data;
266 }
267 
palette_w(u8 data)268 void bt45x_rgb_device_base::palette_w(u8 data)
269 {
270 	LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data);
271 
272 	m_color_ram[m_address][m_address_rgb] = data & get_mask();
273 
274 	// update the mame palette to match the device
275 	if (m_address_rgb == 2)
276 		set_pen_color(m_address, rgb_t(m_color_ram[m_address][0], m_color_ram[m_address][1], m_color_ram[m_address][2]));
277 
278 	increment_address(true);
279 }
280 
palette_w(u8 data)281 void bt45x_mono_device_base::palette_w(u8 data)
282 {
283 	LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data);
284 
285 	if (m_address_rgb == 1)
286 		m_color_ram[m_address] = data & get_mask();
287 
288 	increment_address(true);
289 }
290 
palette_w(u8 data)291 void bt457_device::palette_w(u8 data)
292 {
293 	LOGMASKED(LOG_WRITES, "%s: palette_w 0x%02x\n", machine().describe_context(), data);
294 
295 	// device in normal mode, or rgb mode and selected
296 	if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb))
297 		m_color_ram[m_address] = data;
298 
299 	increment_address(true);
300 }
301 
register_r(address_space & space)302 u8 bt45x_device_base::register_r(address_space &space)
303 {
304 	LOGMASKED(LOG_READS, "%s: register_r 0x%02x\n", machine().describe_context(), m_address);
305 
306 	switch (m_address)
307 	{
308 	case REG_READ_MASK:  return m_read_mask;
309 	case REG_BLINK_MASK: return m_blink_mask;
310 	case REG_COMMAND:    return m_command;
311 	case REG_CONTROL:    return m_control;
312 	}
313 
314 	return space.unmap();
315 }
316 
register_w(u8 data)317 void bt45x_device_base::register_w(u8 data)
318 {
319 	switch (m_address)
320 	{
321 	case REG_READ_MASK:
322 		LOGMASKED(LOG_WRITES, "%s: register_w: read mask 0x%02x\n", machine().describe_context(), data);
323 		m_read_mask = data;
324 		break;
325 
326 	case REG_BLINK_MASK:
327 		LOGMASKED(LOG_WRITES, "%s: register_w: blink mask 0x%02x\n", machine().describe_context(), data);
328 		m_blink_mask = data;
329 		break;
330 
331 	case REG_COMMAND:
332 		LOGMASKED(LOG_WRITES, "%s: register_w: command 0x%02x, %d:1 multiplexing, use %s, %s, OL1 %s blinking, OL0 %s blinking, OL1 display %s, OL0 display %s\n",
333 			machine().describe_context(),
334 			data,
335 			(data & CR7) ? 5 : 4,
336 			(data & CR6) ? "color palette RAM" : "overlay color 0",
337 			(data & CR54) == CR54_6464 ? "64 on 64 off (50/50)" :
338 				(data & CR54) == CR54_3232 ? "32 on 32 off (50/50)" :
339 				(data & CR54) == CR54_1616 ? "16 on 16 off (50/50)" :
340 				"16 on 48 off (25/75)",
341 			(data & CR3) ? "enable" : "disable",
342 			(data & CR2) ? "enable" : "disable",
343 			(data & CR1) ? "enable" : "disable",
344 			(data & CR0) ? "enable" : "disable");
345 		m_command = data;
346 		break;
347 
348 	case REG_CONTROL:
349 		LOGMASKED(LOG_WRITES, "%s: register_w: control 0x%02x, %s nibble%s%s%s\n",
350 			data,
351 			(data & D3) ? "low" : "high",
352 			(data & D2) ? ", blue channel enable" : "",
353 			(data & D1) ? ", green channel enable" : "",
354 			(data & D0) ? ", red channel enable" : "");
355 		m_control = data & 0xf;
356 		break;
357 	}
358 }
359 
overlay_r(address_space & space)360 u8 bt45x_rgb_device_base::overlay_r(address_space &space)
361 {
362 	// address is ignored for 1 bit overlay devices
363 	const u8 address = (m_overlay_colors == 1) ? 0 : m_address;
364 
365 	u8 data = space.unmap();
366 
367 	if (address < m_overlay_colors)
368 		data = m_color_ram[m_palette_colors + address][m_address_rgb];
369 
370 	increment_address();
371 
372 	LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data & get_mask());
373 
374 	return data & get_mask();
375 }
376 
overlay_r(address_space & space)377 u8 bt45x_mono_device_base::overlay_r(address_space &space)
378 {
379 	// address is ignored for 1 bit overlay devices
380 	const u8 address = (m_overlay_colors == 1) ? 0 : m_address;
381 
382 	u8 data = space.unmap();
383 
384 	if (address < m_overlay_colors && m_address_rgb == 1)
385 		data = m_color_ram[m_palette_colors + address];
386 
387 	increment_address();
388 
389 	LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data & get_mask());
390 
391 	return data & get_mask();
392 }
393 
overlay_r(address_space & space)394 u8 bt457_device::overlay_r(address_space &space)
395 {
396 	u8 data = space.unmap();
397 
398 	if (m_address < m_overlay_colors)
399 	{
400 		// device in normal mode, or rgb mode and selected
401 		if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb))
402 			data = m_color_ram[m_address + m_palette_colors];
403 	}
404 
405 	increment_address();
406 
407 	LOGMASKED(LOG_READS, "%s: overlay_r 0x%02x\n", machine().describe_context(), data);
408 
409 	return data;
410 }
411 
overlay_w(u8 data)412 void bt45x_rgb_device_base::overlay_w(u8 data)
413 {
414 	LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data);
415 
416 	// address is ignored for 1 bit overlay devices
417 	const u8 address = (m_overlay_colors == 1) ? 0 : m_address;
418 
419 	if (address < m_overlay_colors)
420 	{
421 		m_color_ram[m_palette_colors + address][m_address_rgb] = data & get_mask();
422 
423 		// update the mame palette to match the device
424 		if (m_address_rgb == 2)
425 			set_pen_color(m_palette_colors + address, rgb_t(
426 				m_color_ram[m_palette_colors + address][0],
427 				m_color_ram[m_palette_colors + address][1],
428 				m_color_ram[m_palette_colors + address][2]));
429 	}
430 
431 	increment_address(true);
432 }
433 
overlay_w(u8 data)434 void bt45x_mono_device_base::overlay_w(u8 data)
435 {
436 	LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data);
437 
438 	// address is ignored for 1 bit overlay devices
439 	const u8 address = (m_overlay_colors == 1) ? 0 : m_address;
440 
441 	if (address < m_overlay_colors)
442 		m_color_ram[m_palette_colors + address] = data & get_mask();
443 
444 	increment_address(true);
445 }
446 
overlay_w(u8 data)447 void bt457_device::overlay_w(u8 data)
448 {
449 	LOGMASKED(LOG_WRITES, "%s: overlay_w 0x%02x\n", machine().describe_context(), data);
450 
451 	if (m_address < m_overlay_colors)
452 	{
453 		// device in normal mode, or rgb mode and selected
454 		if (!(m_control & RGB) || (m_control & RGB) == (1 << m_address_rgb))
455 			m_color_ram[m_palette_colors + m_address] = data;
456 	}
457 
458 	increment_address(true);
459 }
460