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