1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol,Nigel Barnes
3 /**********************************************************************
4
5 Motorola MC6845 and compatible CRT controller emulation
6
7 The following variations exist that are different in
8 functionality and not just in speed rating(1):
9 * Motorola 6845, 6845-1
10 * Hitachi 6845 (= 46505R), 6845S (= 46505S), 6345/6445
11 * Rockwell 6545, 6545-1 (= Synertek SY6545-1)
12 * MOS Technology 6545-1
13
14 (1) as per the document at
15 http://www.6502.org/users/andre/hwinfo/crtc/diffs.html
16
17 The various speed rated devices are usually identified by a
18 letter, e.g. MC68A45, MC68B45. Hitachi's older HD46505 numbering
19 identifies speed by numerical suffixes (-1, -2), which other
20 manufacturers use to identify functional variants instead.
21
22 The chip is originally designed by Hitachi, not by Motorola.
23
24 **********************************************************************/
25
26 /*
27
28 TODO:
29
30 - Change device video emulation x/y offsets when "show border color"
31 is true
32 - Support 'interlace and video' mode
33
34 - mos8563
35 - horizontal scroll
36 - vertical scroll
37 - bitmap modes
38 - display enable begin/end
39
40 - hd6345
41 - smooth scrolling
42 - second cursor
43 - interrupt request
44
45 */
46
47 #include "emu.h"
48 #include "mc6845.h"
49
50 #include "screen.h"
51
52 #define LOG_SETUP (1 << 1U)
53 #define LOG_REGS (1 << 2U)
54 #define LOG_CONF (1 << 3U)
55
56 //#define VERBOSE (LOG_SETUP|LOG_CONF|LOG_REGS)
57 //#define LOG_OUTPUT_STREAM std::cout
58
59 #include "logmacro.h"
60
61 #define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
62 #define LOGREGS(...) LOGMASKED(LOG_REGS, __VA_ARGS__)
63 #define LOGCONF(...) LOGMASKED(LOG_CONF, __VA_ARGS__)
64
65 DEFINE_DEVICE_TYPE(MC6845, mc6845_device, "mc6845", "Motorola MC6845 CRTC")
66 DEFINE_DEVICE_TYPE(MC6845_1, mc6845_1_device, "mc6845_1", "Motorola MC6845-1 CRTC")
67 DEFINE_DEVICE_TYPE(R6545_1, r6545_1_device, "r6545_1", "Rockwell R6545-1 CRTC")
68 DEFINE_DEVICE_TYPE(C6545_1, c6545_1_device, "c6545_1", "C6545-1 CRTC")
69 DEFINE_DEVICE_TYPE(HD6845S, hd6845s_device, "hd6845s", "Hitachi HD6845S CRTC") // same as HD46505S
70 DEFINE_DEVICE_TYPE(SY6545_1, sy6545_1_device, "sy6545_1", "Synertek SY6545-1 CRTC")
71 DEFINE_DEVICE_TYPE(SY6845E, sy6845e_device, "sy6845e", "Synertek SY6845E CRTC")
72 DEFINE_DEVICE_TYPE(HD6345, hd6345_device, "hd6345", "Hitachi HD6345 CRTC-II")
73 DEFINE_DEVICE_TYPE(AMS40489, ams40489_device, "ams40489", "AMS40489 ASIC (CRTC)")
74 DEFINE_DEVICE_TYPE(MOS8563, mos8563_device, "mos8563", "MOS 8563 VDC")
75 DEFINE_DEVICE_TYPE(MOS8568, mos8568_device, "mos8568", "MOS 8568 VDC")
76
77
78 /* mode macros */
79 #define MODE_TRANSPARENT ((m_mode_control & 0x08) != 0)
80 #define MODE_TRANSPARENT_PHI2 ((m_mode_control & 0x88) == 0x88)
81 /* FIXME: not supported yet */
82 #define MODE_TRANSPARENT_BLANK ((m_mode_control & 0x88) == 0x08)
83 #define MODE_UPDATE_STROBE ((m_mode_control & 0x40) != 0)
84 #define MODE_CURSOR_SKEW ((m_mode_control & 0x20) != 0)
85 #define MODE_DISPLAY_ENABLE_SKEW ((m_mode_control & 0x10) != 0)
86 #define MODE_ROW_COLUMN_ADDRESSING ((m_mode_control & 0x04) != 0)
87 #define MODE_INTERLACE_AND_VIDEO ((m_mode_control & 0x03) == 3)
88
89 #define VSS_CBRATE BIT(m_vert_scroll, 5)
90 #define VSS_RVS BIT(m_vert_scroll, 6)
91 #define VSS_COPY BIT(m_vert_scroll, 7)
92
93 #define HSS_DBL BIT(m_horiz_scroll, 4)
94 #define HSS_SEMI BIT(m_horiz_scroll, 5)
95 #define HSS_ATTR BIT(m_horiz_scroll, 6)
96 #define HSS_TEXT BIT(m_horiz_scroll, 7)
97
98 #define ATTR_COLOR (attr & 0x0f)
99 #define ATTR_BACKGROUND (attr & 0x0f)
100 #define ATTR_FOREGROUND (attr >> 4)
101 #define ATTR_BLINK BIT(attr, 4)
102 #define ATTR_UNDERLINE BIT(attr, 5)
103 #define ATTR_REVERSE BIT(attr, 6)
104 #define ATTR_ALTERNATE_CHARSET BIT(attr, 7)
105
106
mc6845_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)107 mc6845_device::mc6845_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
108 : device_t(mconfig, type, tag, owner, clock)
109 , device_video_interface(mconfig, *this, false)
110 , m_show_border_area(true)
111 , m_noninterlace_adjust(0)
112 , m_interlace_adjust(0)
113 , m_clk_scale(1)
114 , m_visarea_adjust_min_x(0)
115 , m_visarea_adjust_max_x(0)
116 , m_visarea_adjust_min_y(0)
117 , m_visarea_adjust_max_y(0)
118 , m_hpixels_per_column(0)
119 , m_reconfigure_cb(*this)
120 , m_begin_update_cb(*this)
121 , m_update_row_cb(*this)
122 , m_end_update_cb(*this)
123 , m_on_update_addr_changed_cb(*this)
124 , m_out_de_cb(*this)
125 , m_out_cur_cb(*this)
126 , m_out_hsync_cb(*this)
127 , m_out_vsync_cb(*this)
128 {
129 }
130
mc6845_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)131 mc6845_device::mc6845_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
132 : mc6845_device(mconfig, MC6845, tag, owner, clock)
133 {
134 }
135
136
device_post_load()137 void mc6845_device::device_post_load()
138 {
139 recompute_parameters(true);
140 }
141
142
device_clock_changed()143 void mc6845_device::device_clock_changed()
144 {
145 recompute_parameters(true);
146 }
147
148
call_on_update_address(int strobe)149 void mc6845_device::call_on_update_address(int strobe)
150 {
151 if (!m_on_update_addr_changed_cb.isnull())
152 m_upd_trans_timer->adjust(attotime::zero, (m_update_addr << 8) | strobe);
153 else
154 fatalerror("M6845: transparent memory mode without handler\n");
155 }
156
157
address_w(uint8_t data)158 void mc6845_device::address_w(uint8_t data)
159 {
160 m_register_address_latch = data & 0x1f;
161 }
162
163
status_r()164 uint8_t mc6845_device::status_r()
165 {
166 uint8_t ret = 0;
167
168 /* VBLANK bit */
169 if (m_supports_status_reg_d5 && !m_line_enable_ff)
170 ret = ret | 0x20;
171
172 /* light pen latched */
173 if (m_supports_status_reg_d6 && m_light_pen_latched)
174 ret = ret | 0x40;
175
176 /* UPDATE ready */
177 if (m_supports_status_reg_d7 && m_update_ready_bit)
178 ret = ret | 0x80;
179
180 return ret;
181 }
182
183
transparent_update()184 void mc6845_device::transparent_update()
185 {
186 if (m_supports_transparent && MODE_TRANSPARENT)
187 {
188 if (MODE_TRANSPARENT_PHI2)
189 {
190 m_update_addr++;
191 m_update_addr &= 0x3fff;
192 call_on_update_address(MODE_UPDATE_STROBE);
193 }
194 else
195 {
196 /* MODE_TRANSPARENT_BLANK */
197 if (m_update_ready_bit)
198 {
199 m_update_ready_bit = false;
200 update_upd_adr_timer();
201 }
202 }
203 }
204 }
205
206
register_r()207 uint8_t mc6845_device::register_r()
208 {
209 uint8_t ret = 0;
210
211 switch (m_register_address_latch)
212 {
213 case 0x0c: ret = m_supports_disp_start_addr_r ? (m_disp_start_addr >> 8) & 0xff : 0; break;
214 case 0x0d: ret = m_supports_disp_start_addr_r ? (m_disp_start_addr >> 0) & 0xff : 0; break;
215 case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
216 case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
217 // FIXME: status flag should not be reset if LPEN input is held high
218 case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = false; break;
219 case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = false; break;
220 case 0x1f: transparent_update(); break;
221
222 /* all other registers are write only and return 0 */
223 default: break;
224 }
225
226 return ret;
227 }
228
229
register_w(uint8_t data)230 void mc6845_device::register_w(uint8_t data)
231 {
232 LOGREGS("%s:M6845 reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
233
234 /* Omits LOGSETUP logs of cursor registers as they tend to be spammy */
235 if (m_register_address_latch < 0x0e &&
236 m_register_address_latch != 0x0a &&
237 m_register_address_latch != 0x0b) LOGSETUP(" * %02x <= %3u [%02x] %s\n", m_register_address_latch,
238 data, data, std::array<char const *, 16>
239 {{ "R0 - Horizontal Total", "R1 - Horizontal Displayed", "R2 - Horizontal Sync Position",
240 "R3 - Sync Width", "R4 - Vertical Total", "R5 - Vertical Total Adjust",
241 "R6 - Vertical Displayed", "R7 - Vertical Sync Position", "R8 - Interlace & Skew",
242 "R9 - Maximum Raster Address", "R10 - Cursor Start Raster", "R11 - Cursor End Raster",
243 "R12 - Start Address (H)", "R13 - Start Address (L)", "R14 - Cursor (H)",
244 "R15 - Cursor (L)" }}[(m_register_address_latch & 0x0f)]);
245
246 switch (m_register_address_latch)
247 {
248 case 0x00: m_horiz_char_total = data & 0xff; break;
249 case 0x01: m_horiz_disp = data & 0xff; break;
250 case 0x02: m_horiz_sync_pos = data & 0xff; break;
251 case 0x03: m_sync_width = data & 0xff; break;
252 case 0x04: m_vert_char_total = data & 0x7f; break;
253 case 0x05: m_vert_total_adj = data & 0x1f; break;
254 case 0x06: m_vert_disp = data & 0x7f; break;
255 case 0x07: m_vert_sync_pos = data & 0x7f; break;
256 case 0x08: m_mode_control = data & 0xff; break;
257 case 0x09: m_max_ras_addr = data & 0x1f; break;
258 case 0x0a: m_cursor_start_ras = data & 0x7f; break;
259 case 0x0b: m_cursor_end_ras = data & 0x1f; break;
260 case 0x0c: m_disp_start_addr = ((data & 0x3f) << 8) | (m_disp_start_addr & 0x00ff); break;
261 case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
262 case 0x0e: m_cursor_addr = ((data & 0x3f) << 8) | (m_cursor_addr & 0x00ff); break;
263 case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
264 case 0x10: /* read-only */ break;
265 case 0x11: /* read-only */ break;
266 case 0x12:
267 if (m_supports_transparent)
268 {
269 m_update_addr = ((data & 0x3f) << 8) | (m_update_addr & 0x00ff);
270 if(MODE_TRANSPARENT_PHI2)
271 call_on_update_address(MODE_UPDATE_STROBE);
272 }
273 break;
274 case 0x13:
275 if (m_supports_transparent)
276 {
277 m_update_addr = ((data & 0xff) << 0) | (m_update_addr & 0xff00);
278 if(MODE_TRANSPARENT_PHI2)
279 call_on_update_address(MODE_UPDATE_STROBE);
280 }
281 break;
282 case 0x1f: transparent_update(); break;
283 default: break;
284 }
285
286 /* display message if the Mode Control register is not zero */
287 if ((m_register_address_latch == 0x08) && (m_mode_control != 0))
288 if (!m_supports_transparent)
289 logerror("M6845: Mode Control %02X is not supported!!!\n", m_mode_control);
290
291 recompute_parameters(false);
292 }
293
294
address_w(uint8_t data)295 void mos8563_device::address_w(uint8_t data)
296 {
297 m_register_address_latch = data & 0x3f;
298 }
299
300
status_r()301 uint8_t mos8563_device::status_r()
302 {
303 uint8_t ret = m_revision;
304
305 /* VBLANK bit */
306 if (!m_line_enable_ff)
307 ret = ret | 0x20;
308
309 /* light pen latched */
310 if (m_light_pen_latched)
311 ret = ret | 0x40;
312
313 /* UPDATE ready */
314 if (m_update_ready_bit)
315 ret = ret | 0x80;
316
317 return ret;
318 }
319
320
register_r()321 uint8_t mos8563_device::register_r()
322 {
323 uint8_t ret = 0xff;
324
325 switch (m_register_address_latch)
326 {
327 case 0x00: ret = m_horiz_char_total; break;
328 case 0x01: ret = m_horiz_disp; break;
329 case 0x02: ret = m_horiz_sync_pos; break;
330 case 0x03: ret = m_sync_width; break;
331 case 0x04: ret = m_vert_char_total; break;
332 case 0x05: ret = m_vert_total_adj | 0xc0; break;
333 case 0x06: ret = m_vert_disp; break;
334 case 0x07: ret = m_vert_sync_pos; break;
335 case 0x08: ret = m_mode_control | 0xfc; break;
336 case 0x09: ret = m_max_ras_addr | 0xe0; break;
337 case 0x0a: ret = m_cursor_start_ras | 0x80; break;
338 case 0x0b: ret = m_cursor_end_ras | 0xe0; break;
339 case 0x0c: ret = (m_disp_start_addr >> 8) & 0xff; break;
340 case 0x0d: ret = (m_disp_start_addr >> 0) & 0xff; break;
341 case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
342 case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
343 case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = false; break;
344 case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = false; break;
345 case 0x12: ret = (m_update_addr >> 8) & 0xff; break;
346 case 0x13: ret = (m_update_addr >> 0) & 0xff; break;
347 case 0x14: ret = (m_attribute_addr >> 8) & 0xff; break;
348 case 0x15: ret = (m_attribute_addr >> 0) & 0xff; break;
349 case 0x16: ret = m_horiz_char; break;
350 case 0x17: ret = m_vert_char_disp | 0xe0; break;
351 case 0x18: ret = m_vert_scroll; break;
352 case 0x19: ret = m_horiz_scroll; break;
353 case 0x1a: ret = m_color; break;
354 case 0x1b: ret = m_row_addr_incr; break;
355 case 0x1c: ret = m_char_base_addr | 0x1f; break;
356 case 0x1d: ret = m_underline_ras | 0xe0; break;
357 case 0x1e: ret = m_word_count; break;
358 case 0x1f: ret = read_videoram(m_update_addr++); break;
359 case 0x20: ret = (m_block_addr >> 8) & 0xff; break;
360 case 0x21: ret = (m_block_addr >> 0) & 0xff; break;
361 case 0x22: ret = (m_de_begin >> 8) & 0xff; break;
362 case 0x23: ret = (m_de_begin >> 0) & 0xff; break;
363 case 0x24: ret = m_dram_refresh | 0xf0; break;
364 case 0x25: ret = m_sync_polarity | 0x3f; break;
365 }
366
367 return ret;
368 }
369
370
register_w(uint8_t data)371 void mos8563_device::register_w(uint8_t data)
372 {
373 LOGREGS("%s:MOS8563 reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
374
375 switch (m_register_address_latch)
376 {
377 case 0x00: m_horiz_char_total = data & 0xff; break;
378 case 0x01: m_horiz_disp = data & 0xff; break;
379 case 0x02: m_horiz_sync_pos = data & 0xff; break;
380 case 0x03: m_sync_width = data & 0xff; break;
381 case 0x04: m_vert_char_total = data & 0xff; break;
382 case 0x05: m_vert_total_adj = data & 0x1f; break;
383 case 0x06: m_vert_disp = data & 0xff; break;
384 case 0x07: m_vert_sync_pos = data & 0xff; break;
385 case 0x08: m_mode_control = data & 0x03; break;
386 case 0x09: m_max_ras_addr = data & 0x1f; break;
387 case 0x0a: m_cursor_start_ras = data & 0x7f; break;
388 case 0x0b: m_cursor_end_ras = data & 0x1f; break;
389 case 0x0c: m_disp_start_addr = ((data & 0xff) << 8) | (m_disp_start_addr & 0x00ff); break;
390 case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
391 case 0x0e: m_cursor_addr = ((data & 0xff) << 8) | (m_cursor_addr & 0x00ff); break;
392 case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
393 case 0x10: /* read-only */ break;
394 case 0x11: /* read-only */ break;
395 case 0x12: m_update_addr = ((data & 0xff) << 8) | (m_update_addr & 0x00ff); break;
396 case 0x13: m_update_addr = ((data & 0xff) << 0) | (m_update_addr & 0xff00); break;
397 case 0x14: m_attribute_addr = ((data & 0xff) << 8) | (m_attribute_addr & 0x00ff); break;
398 case 0x15: m_attribute_addr = ((data & 0xff) << 0) | (m_attribute_addr & 0xff00); break;
399 case 0x16: m_horiz_char = data & 0xff; break;
400 case 0x17: m_vert_char_disp = data & 0x1f; break;
401 case 0x18: m_vert_scroll = data & 0xff; break;
402 case 0x19:
403 {
404 int dbl = HSS_DBL;
405 m_horiz_scroll = data & 0xff;
406 if (dbl && !HSS_DBL) { m_clk_scale = 4; recompute_parameters(true); }
407 if (!dbl && HSS_DBL) { m_clk_scale = 8; recompute_parameters(true); }
408 break;
409 }
410 case 0x1a: m_color = data & 0xff; break;
411 case 0x1b: m_row_addr_incr = data & 0xff; break;
412 case 0x1c: m_char_base_addr = data & 0xe0; break;
413 case 0x1d: m_underline_ras = data & 0x1f; break;
414 case 0x1e:
415 m_word_count = data & 0xff;
416 m_update_ready_bit = 0;
417 m_block_copy_timer->adjust(cclks_to_attotime(1));
418 break;
419 case 0x1f:
420 m_data = data & 0xff;
421 write_videoram(m_update_addr++, m_data);
422 break;
423 case 0x20: m_block_addr = ((data & 0xff) << 8) | (m_block_addr & 0x00ff); break;
424 case 0x21: m_block_addr = ((data & 0xff) << 0) | (m_block_addr & 0xff00); break;
425 case 0x22: m_de_begin = ((data & 0xff) << 8) | (m_de_begin & 0x00ff); break;
426 case 0x23: m_de_begin = ((data & 0xff) << 0) | (m_de_begin & 0xff00); break;
427 case 0x24: m_dram_refresh = data & 0x0f; break;
428 case 0x25: m_sync_polarity = data & 0xc0; break;
429 }
430
431 recompute_parameters(false);
432 }
433
address_w(uint8_t data)434 void hd6345_device::address_w(uint8_t data)
435 {
436 m_register_address_latch = data & 0x3f;
437 }
438
439
register_r()440 uint8_t hd6345_device::register_r()
441 {
442 uint8_t ret = 0;
443
444 switch (m_register_address_latch)
445 {
446 case 0x0c: ret = (m_disp_start_addr >> 8) & 0xff; break;
447 case 0x0d: ret = (m_disp_start_addr >> 0) & 0xff; break;
448 case 0x0e: ret = (m_cursor_addr >> 8) & 0xff; break;
449 case 0x0f: ret = (m_cursor_addr >> 0) & 0xff; break;
450 case 0x10: ret = (m_light_pen_addr >> 8) & 0xff; m_light_pen_latched = false; break;
451 case 0x11: ret = (m_light_pen_addr >> 0) & 0xff; m_light_pen_latched = false; break;
452 case 0x12: ret = m_disp2_pos; break;
453 case 0x13: ret = (m_disp2_start_addr >> 8) & 0xff; break;
454 case 0x14: ret = (m_disp2_start_addr >> 0) & 0xff; break;
455 case 0x15: ret = m_disp3_pos; break;
456 case 0x16: ret = (m_disp3_start_addr >> 8) & 0xff; break;
457 case 0x17: ret = (m_disp3_start_addr >> 0) & 0xff; break;
458 case 0x18: ret = m_disp4_pos; break;
459 case 0x19: ret = (m_disp4_start_addr >> 8) & 0xff; break;
460 case 0x1a: ret = (m_disp4_start_addr >> 0) & 0xff; break;
461 case 0x1b: ret = m_vert_sync_pos_adj; break;
462 case 0x1c: /* TODO: light pen raster */ break;
463 case 0x1d: ret = m_smooth_scroll_ras; break;
464 case 0x1f: /* TODO: status */ break;
465 case 0x21: ret = m_mem_width_offs; break;
466 case 0x24: ret = (m_cursor2_addr >> 8) & 0xff; break;
467 case 0x25: ret = (m_cursor2_addr >> 0) & 0xff; break;
468 case 0x26: ret = m_cursor_width; break;
469 case 0x27: ret = m_cursor2_width; break;
470 }
471
472 return ret;
473 }
474
register_w(uint8_t data)475 void hd6345_device::register_w(uint8_t data)
476 {
477 LOGREGS("%s:HD6345 reg 0x%02x = 0x%02x\n", machine().describe_context(), m_register_address_latch, data);
478
479 /* Omits LOGSETUP logs of cursor registers as they tend to be spammy */
480 if (m_register_address_latch < 0x28 &&
481 m_register_address_latch != 0x0a && m_register_address_latch != 0x0a &&
482 m_register_address_latch != 0x0e && m_register_address_latch != 0x0f)
483 LOGSETUP(" * %02x <= %3u [%02x] %s\n", m_register_address_latch, data, data, std::array<char const *, 40>
484 {{ "R0 - Horizontal Total", "R1 - Horizontal Displayed", "R2 - Horizontal Sync Position",
485 "R3 - Sync Width", "R4 - Vertical Total", "R5 - Vertical Total Adjust",
486 "R6 - Vertical Displayed", "R7 - Vertical Sync Position", "R8 - Interlace Mode & Skew",
487 "R9 - Maximum Raster Address", "R10 - Cursor 1 Start", "R11 - Cursor 1 End",
488 "R12 - Screen 1 Start Address (H)", "R13 - Screen 1 Start Address (L)", "R14 - Cursor 1 Address (H)",
489 "R15 - Cursor 1 Address (L)", "R16 - Light Pen (H)", "R17 - Light Pen (L)",
490 "R18 - Screen 2 Start Position", "R19 - Screen 2 Start Address (H)", "R20 - Screen 2 Start Address (L)",
491 "R21 - Screen 3 Start Position", "R22 - Screen 3 Start Address (H)", "R23 - Screen 3 Start Address (L)",
492 "R24 - Screen 4 Start Position", "R25 - Screen 4 Start Address (H)", "R26 - Screen 4 Start Address (L)",
493 "R27 - Vertical Sync Position Adj", "R28 - Light Pen Raster", "R29 - Smooth Scrolling",
494 "R30 - Control 1", "R31 - Control 2", "R32 - Control 3",
495 "R33 - Memory Width Offset", "R34 - Cursor 2 Start", "R35 - Cursor 2 End",
496 "R36 - Cursor 2 Address (H)", "R37 - Cursor 2 Address (L)", "R38 - Cursor 1 Width",
497 "R39 - Cursor 2 Width" }}[(m_register_address_latch & 0x3f)]);
498
499 switch (m_register_address_latch)
500 {
501 case 0x00: m_horiz_char_total = data & 0xff; break;
502 case 0x01: m_horiz_disp = data & 0xff; break;
503 case 0x02: m_horiz_sync_pos = data & 0xff; break;
504 case 0x03: m_sync_width = data & 0xff; break;
505 case 0x04: m_vert_char_total = data & 0xff; break;
506 case 0x05: m_vert_total_adj = data & 0x1f; break;
507 case 0x06: m_vert_disp = data & 0xff; break;
508 case 0x07: m_vert_sync_pos = data & 0xff; break;
509 case 0x08: m_mode_control = data & 0xf3; break;
510 case 0x09: m_max_ras_addr = data & 0x1f; break;
511 case 0x0a: m_cursor_start_ras = data & 0x7f; break;
512 case 0x0b: m_cursor_end_ras = data & 0x1f; break;
513 case 0x0c: m_disp_start_addr = ((data & 0x3f) << 8) | (m_disp_start_addr & 0x00ff); break;
514 case 0x0d: m_disp_start_addr = ((data & 0xff) << 0) | (m_disp_start_addr & 0xff00); break;
515 case 0x0e: m_cursor_addr = ((data & 0x3f) << 8) | (m_cursor_addr & 0x00ff); break;
516 case 0x0f: m_cursor_addr = ((data & 0xff) << 0) | (m_cursor_addr & 0xff00); break;
517 case 0x10: /* read-only */ break;
518 case 0x11: /* read-only */ break;
519 case 0x12: m_disp2_pos = data & 0xff; break;
520 case 0x13: m_disp2_start_addr = ((data & 0x3f) << 8) | (m_disp2_start_addr & 0x00ff); break;
521 case 0x14: m_disp2_start_addr = ((data & 0xff) << 0) | (m_disp2_start_addr & 0xff00); break;
522 case 0x15: m_disp3_pos = data & 0xff; break;
523 case 0x16: m_disp3_start_addr = ((data & 0x3f) << 8) | (m_disp3_start_addr & 0x00ff); break;
524 case 0x17: m_disp3_start_addr = ((data & 0xff) << 0) | (m_disp3_start_addr & 0xff00); break;
525 case 0x18: m_disp4_pos = data & 0xff; break;
526 case 0x19: m_disp4_start_addr = ((data & 0x3f) << 8) | (m_disp4_start_addr & 0x00ff); break;
527 case 0x1a: m_disp4_start_addr = ((data & 0xff) << 0) | (m_disp4_start_addr & 0xff00); break;
528 case 0x1b: m_vert_sync_pos_adj = data & 0x1f; break;
529 case 0x1c: /* read-only */ break;
530 case 0x1d: m_smooth_scroll_ras = data & 0x1f; break;
531 case 0x1e: m_control1 = data & 0xff; break;
532 case 0x1f: m_control2 = data & 0xf8; break;
533 case 0x20: m_control3 = data & 0xfe; break;
534 case 0x21: m_mem_width_offs = data & 0xff; break;
535 case 0x22: m_cursor2_start_ras = data & 0x7f; break;
536 case 0x23: m_cursor2_end_ras = data & 0x1f; break;
537 case 0x24: m_cursor2_addr = ((data & 0x3f) << 8) | (m_cursor2_addr & 0x00ff); break;
538 case 0x25: m_cursor2_addr = ((data & 0xff) << 0) | (m_cursor2_addr & 0xff00); break;
539 case 0x26: m_cursor_width = data & 0xff; break;
540 case 0x27: m_cursor2_width = data & 0xff; break;
541 }
542
543 recompute_parameters(false);
544 }
545
546
read_videoram(offs_t offset)547 inline uint8_t mos8563_device::read_videoram(offs_t offset)
548 {
549 return space(0).read_byte(offset);
550 }
551
write_videoram(offs_t offset,uint8_t data)552 inline void mos8563_device::write_videoram(offs_t offset, uint8_t data)
553 {
554 space(0).write_byte(offset, data);
555 }
556
557
READ_LINE_MEMBER(mc6845_device::de_r)558 READ_LINE_MEMBER( mc6845_device::de_r )
559 {
560 return m_de;
561 }
562
563
READ_LINE_MEMBER(mc6845_device::cursor_r)564 READ_LINE_MEMBER( mc6845_device::cursor_r )
565 {
566 return m_cur;
567 }
568
569
READ_LINE_MEMBER(mc6845_device::hsync_r)570 READ_LINE_MEMBER( mc6845_device::hsync_r )
571 {
572 return m_hsync;
573 }
574
575
READ_LINE_MEMBER(mc6845_device::vsync_r)576 READ_LINE_MEMBER( mc6845_device::vsync_r )
577 {
578 return m_vsync;
579 }
580
581
recompute_parameters(bool postload)582 void mc6845_device::recompute_parameters(bool postload)
583 {
584 uint16_t hsync_on_pos, hsync_off_pos, vsync_on_pos, vsync_off_pos;
585
586 uint16_t video_char_height = m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust); // fix garbage at the bottom of the screen (eg victor9k)
587 // Would be useful for 'interlace and video' mode support...
588 // uint16_t frame_char_height = (MODE_INTERLACE_AND_VIDEO ? m_max_ras_addr / 2 : m_max_ras_addr) + 1;
589
590 /* compute the screen sizes */
591 uint16_t horiz_pix_total = (m_horiz_char_total + 1) * m_hpixels_per_column;
592 uint16_t vert_pix_total = (m_vert_char_total + 1) * video_char_height + m_vert_total_adj;
593
594 /* determine the visible area, avoid division by 0 */
595 uint16_t max_visible_x = m_horiz_disp * m_hpixels_per_column - 1;
596 uint16_t max_visible_y = m_vert_disp * video_char_height - 1;
597
598 /* determine the syncing positions */
599 uint8_t horiz_sync_char_width = m_sync_width & 0x0f;
600 uint8_t vert_sync_pix_width = m_supports_vert_sync_width ? (m_sync_width >> 4) & 0x0f : 0x10;
601
602 if (horiz_sync_char_width == 0)
603 horiz_sync_char_width = 0x10;
604
605 if (vert_sync_pix_width == 0)
606 vert_sync_pix_width = 0x10;
607
608 /* determine the transparent update cycle time, 1 update every 4 character clocks */
609 m_upd_time = cclks_to_attotime(4 * m_hpixels_per_column);
610
611 hsync_on_pos = m_horiz_sync_pos * m_hpixels_per_column;
612 hsync_off_pos = hsync_on_pos + (horiz_sync_char_width * m_hpixels_per_column);
613 vsync_on_pos = m_vert_sync_pos * video_char_height;
614 vsync_off_pos = vsync_on_pos + vert_sync_pix_width;
615
616 // the Commodore PET computers have a non-standard 20kHz monitor which
617 // requires a wider HSYNC pulse that extends past the scanline width
618 if (hsync_off_pos > horiz_pix_total)
619 hsync_off_pos = horiz_pix_total;
620
621 if (vsync_on_pos > vert_pix_total)
622 vsync_on_pos = vert_pix_total;
623
624 if (vsync_off_pos > vert_pix_total)
625 vsync_off_pos = vert_pix_total;
626
627 /* update only if screen parameters changed, unless we are coming here after loading the saved state */
628 if (postload ||
629 (horiz_pix_total != m_horiz_pix_total) || (vert_pix_total != m_vert_pix_total) ||
630 (max_visible_x != m_max_visible_x) || (max_visible_y != m_max_visible_y) ||
631 (hsync_on_pos != m_hsync_on_pos) || (vsync_on_pos != m_vsync_on_pos) ||
632 (hsync_off_pos != m_hsync_off_pos) || (vsync_off_pos != m_vsync_off_pos))
633 {
634 /* update the screen if we have valid data */
635 if ((horiz_pix_total > 0) && (max_visible_x < horiz_pix_total) &&
636 (vert_pix_total > 0) && (max_visible_y < vert_pix_total) &&
637 (hsync_on_pos <= horiz_pix_total) && (vsync_on_pos <= vert_pix_total) &&
638 (hsync_on_pos != hsync_off_pos))
639 {
640 rectangle visarea;
641
642 attotime refresh = cclks_to_attotime((m_horiz_char_total + 1) * vert_pix_total);
643
644 // This doubles the vertical resolution, required for 'interlace and video' mode support.
645 // Tested and works for super80v, which was designed with this in mind (choose green or monochrome colour in config switches).
646 // However it breaks some other drivers (apricot,a6809,victor9k,bbc(mode7)).
647 // So, it is commented out for now.
648 // Also, the mode-register change needs to be added to the changed-parameter tests above.
649 if (MODE_INTERLACE_AND_VIDEO)
650 {
651 //max_visible_y *= 2;
652 //vert_pix_total *= 2;
653 }
654
655 if(m_show_border_area)
656 visarea.set(0, horiz_pix_total-2, 0, vert_pix_total-2);
657 else
658 visarea.set(0 + m_visarea_adjust_min_x, max_visible_x + m_visarea_adjust_max_x, 0 + m_visarea_adjust_min_y, max_visible_y + m_visarea_adjust_max_y);
659
660 LOGCONF("M6845 config screen: HTOTAL: %d VTOTAL: %d MAX_X: %d MAX_Y: %d HSYNC: %d-%d VSYNC: %d-%d Freq: %ffps\n",
661 horiz_pix_total, vert_pix_total, max_visible_x, max_visible_y, hsync_on_pos, hsync_off_pos - 1, vsync_on_pos, vsync_off_pos - 1, refresh.as_hz());
662
663 if (has_screen())
664 screen().configure(horiz_pix_total, vert_pix_total, visarea, refresh.as_attoseconds());
665
666 if(!m_reconfigure_cb.isnull())
667 m_reconfigure_cb(horiz_pix_total, vert_pix_total, visarea, refresh.as_attoseconds());
668
669 m_has_valid_parameters = true;
670 }
671 else
672 m_has_valid_parameters = false;
673
674 m_horiz_pix_total = horiz_pix_total;
675 m_vert_pix_total = vert_pix_total;
676 m_max_visible_x = max_visible_x;
677 m_max_visible_y = max_visible_y;
678 m_hsync_on_pos = hsync_on_pos;
679 m_hsync_off_pos = hsync_off_pos;
680 m_vsync_on_pos = vsync_on_pos;
681 m_vsync_off_pos = vsync_off_pos;
682 if ( (!m_reconfigure_cb.isnull()) && (!postload) )
683 m_line_counter = 0;
684 }
685 }
686
687
update_counters()688 void mc6845_device::update_counters()
689 {
690 m_character_counter = attotime_to_cclks(m_line_timer->elapsed());
691
692 if (m_hsync_off_timer->enabled())
693 {
694 m_hsync_width_counter = attotime_to_cclks(m_hsync_off_timer->elapsed());
695 }
696 }
697
698
set_de(int state)699 void mc6845_device::set_de(int state)
700 {
701 if (m_de != state)
702 {
703 m_de = state;
704
705 if (m_de)
706 {
707 /* If the upd_adr_timer was running, cancel it */
708 m_upd_adr_timer->adjust(attotime::never);
709 }
710 else
711 {
712 /* if transparent update was requested fire the update timer */
713 if(!m_update_ready_bit)
714 update_upd_adr_timer();
715 }
716
717 m_out_de_cb(m_de);
718 }
719 }
720
721
set_hsync(int state)722 void mc6845_device::set_hsync(int state)
723 {
724 if (m_hsync != state)
725 {
726 m_hsync = state;
727 m_out_hsync_cb(m_hsync);
728 }
729 }
730
731
set_vsync(int state)732 void mc6845_device::set_vsync(int state)
733 {
734 if (m_vsync != state)
735 {
736 m_vsync = state;
737 m_out_vsync_cb(m_vsync);
738 }
739 }
740
741
set_cur(int state)742 void mc6845_device::set_cur(int state)
743 {
744 if (m_cur != state)
745 {
746 m_cur = state;
747 m_out_cur_cb(m_cur);
748 }
749 }
750
751
update_upd_adr_timer()752 void mc6845_device::update_upd_adr_timer()
753 {
754 if (! m_de && m_supports_transparent)
755 m_upd_adr_timer->adjust(m_upd_time);
756 }
757
758
match_line()759 bool mc6845_device::match_line()
760 {
761 /* Check if we've reached the end of active display */
762 if ( m_line_counter == m_vert_disp )
763 {
764 m_line_enable_ff = false;
765 m_current_disp_addr = m_disp_start_addr;
766 }
767
768 /* Check if VSYNC should be enabled */
769 if ( m_line_counter == m_vert_sync_pos )
770 {
771 m_vsync_width_counter = 0;
772 m_vsync_ff = 1;
773
774 return true;
775 }
776
777 return false;
778 }
779
780
check_cursor_visible(uint16_t ra,uint16_t line_addr)781 bool mc6845_device::check_cursor_visible(uint16_t ra, uint16_t line_addr)
782 {
783 if (!m_cursor_state)
784 return false;
785
786 if ((m_cursor_addr < line_addr) ||
787 (m_cursor_addr >= (line_addr + m_horiz_disp)))
788 {
789 // Not a cursor character line.
790 return false;
791 }
792
793 uint16_t cursor_start_ras = m_cursor_start_ras & 0x1f;
794 uint16_t max_ras_addr = m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust) - 1;
795
796 if (cursor_start_ras > max_ras_addr)
797 {
798 // No cursor.
799 return false;
800 }
801
802 // TODO explore the edge cases in the 'interlace and video' mode.
803
804 if (cursor_start_ras <= m_cursor_end_ras)
805 {
806 if (m_cursor_end_ras > max_ras_addr)
807 {
808 // Wraps to produce a full cursor.
809 return true;
810 }
811 // Cursor from start to end inclusive.
812 return (ra >= cursor_start_ras) && (ra <= m_cursor_end_ras);
813 }
814
815 // Otherwise cursor_start_ras > m_cursor_end_ras giving a split cursor.
816 return (ra <= m_cursor_end_ras) || (ra >= cursor_start_ras);
817 }
818
819 // The HD6845 cursor does not wrap as it does for the MC6845.
check_cursor_visible(uint16_t ra,uint16_t line_addr)820 bool hd6845s_device::check_cursor_visible(uint16_t ra, uint16_t line_addr)
821 {
822 if (!m_cursor_state)
823 return false;
824
825 if ((m_cursor_addr < line_addr) ||
826 (m_cursor_addr >= (line_addr + m_horiz_disp)))
827 {
828 // Not a cursor character line.
829 return false;
830 }
831
832 uint16_t cursor_start_ras = m_cursor_start_ras & 0x1f;
833 uint16_t max_ras_addr = m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust) - 1;
834
835 if (cursor_start_ras > max_ras_addr || cursor_start_ras > m_cursor_end_ras)
836 {
837 // No cursor.
838 return false;
839 }
840
841 // Cursor from start to end inclusive.
842 return (ra >= cursor_start_ras) && (ra <= m_cursor_end_ras);
843 }
844
845
handle_line_timer()846 void mc6845_device::handle_line_timer()
847 {
848 bool new_vsync = m_vsync;
849
850 m_character_counter = 0;
851 m_cursor_x = -1;
852
853 /* Check if VSYNC is active */
854 if ( m_vsync_ff )
855 {
856 uint8_t vsync_width = m_supports_vert_sync_width ? (m_sync_width >> 4) & 0x0f : 0;
857
858 m_vsync_width_counter = ( m_vsync_width_counter + 1 ) & 0x0F;
859
860 /* Check if we've reached end of VSYNC */
861 if ( m_vsync_width_counter == vsync_width )
862 {
863 m_vsync_ff = 0;
864
865 new_vsync = false;
866 }
867 }
868
869 // For rudimentary 'interlace and video' support, m_raster_counter increments by 1 rather than the correct 2.
870 // The correct test would be:
871 // if ( m_raster_counter == (MODE_INTERLACE_AND_VIDEO ? m_max_ras_addr + 1 : m_max_ras_addr) )
872 if ( m_raster_counter == m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust) - 1 )
873 {
874 /* Check if we have reached the end of the vertical area */
875 if ( m_line_counter == m_vert_char_total )
876 {
877 m_adjust_counter = 0;
878 m_adjust_active = 1;
879 }
880
881 m_raster_counter = 0;
882 m_line_counter = ( m_line_counter + 1 ) & 0x7F;
883 m_line_address = ( m_line_address + m_horiz_disp ) & 0x3fff;
884
885 if (match_line())
886 new_vsync = true;
887 }
888 else
889 {
890 // For rudimentary 'interlace and video' support, m_raster_counter increments by 1 rather than the correct 2.
891 // m_raster_counter = ( m_raster_counter + (MODE_INTERLACE_AND_VIDEO ? 2 : 1) ) & 0x1F;
892 m_raster_counter = ( m_raster_counter + 1 ) & 0x1F;
893 }
894
895 if ( m_adjust_active )
896 {
897 /* Check if we have reached the end of a full cycle */
898 if ( m_adjust_counter == m_vert_total_adj )
899 {
900 m_adjust_active = 0;
901 m_raster_counter = 0;
902 m_line_counter = 0;
903 m_line_address = m_disp_start_addr;
904 m_line_enable_ff = true;
905
906 if (m_supports_vert_sync_width)
907 {
908 if (match_line())
909 new_vsync = true;
910 }
911
912 /* also update the cursor state now */
913 update_cursor_state();
914
915 if (has_screen())
916 screen().reset_origin();
917 }
918 else
919 {
920 m_adjust_counter = ( m_adjust_counter + 1 ) & 0x1F;
921 }
922 }
923
924 if ( m_line_enable_ff )
925 {
926 /* Schedule DE off signal change */
927 m_de_off_timer->adjust(cclks_to_attotime(m_horiz_disp));
928
929 /* Is cursor visible on this line? */
930 if (check_cursor_visible(m_raster_counter, m_line_address))
931 {
932 m_cursor_x = m_cursor_addr - m_line_address;
933
934 /* Schedule CURSOR ON signal */
935 m_cur_on_timer->adjust(cclks_to_attotime(m_cursor_x));
936 }
937 }
938
939 /* Schedule HSYNC on signal */
940 m_hsync_on_timer->adjust(cclks_to_attotime(m_horiz_sync_pos));
941
942 /* Schedule our next callback */
943 m_line_timer->adjust(cclks_to_attotime(m_horiz_char_total + 1));
944
945 /* Set VSYNC and DE signals */
946 set_vsync( new_vsync );
947 set_de( m_line_enable_ff ? true : false );
948 }
949
950
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)951 void mc6845_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
952 {
953 switch (id)
954 {
955 case TIMER_LINE:
956 handle_line_timer();
957 break;
958
959 case TIMER_DE_OFF:
960 set_de( false );
961 break;
962
963 case TIMER_CUR_ON:
964 set_cur( true );
965
966 /* Schedule CURSOR off signal */
967 m_cur_off_timer->adjust(cclks_to_attotime(1));
968 break;
969
970 case TIMER_CUR_OFF:
971 set_cur( false );
972 break;
973
974 case TIMER_HSYNC_ON:
975 {
976 uint8_t hsync_width = ( m_sync_width & 0x0f ) ? ( m_sync_width & 0x0f ) : 0x10;
977
978 m_hsync_width_counter = 0;
979 set_hsync( true );
980
981 /* Schedule HSYNC off signal */
982 m_hsync_off_timer->adjust(cclks_to_attotime(hsync_width));
983 }
984 break;
985
986 case TIMER_HSYNC_OFF:
987 set_hsync( false );
988 break;
989
990 case TIMER_LIGHT_PEN_LATCH:
991 m_light_pen_addr = get_ma();
992 m_light_pen_latched = true;
993 break;
994
995 case TIMER_UPD_ADR:
996 /* fire a update address strobe */
997 call_on_update_address(MODE_UPDATE_STROBE);
998 break;
999
1000 case TIMER_UPD_TRANS:
1001 {
1002 int addr = (param >> 8);
1003 int strobe = (param & 0xff);
1004
1005 /* call the callback function -- we know it exists */
1006 m_on_update_addr_changed_cb(addr, strobe);
1007
1008 if(!m_update_ready_bit && MODE_TRANSPARENT_BLANK)
1009 {
1010 m_update_addr++;
1011 m_update_addr &= 0x3fff;
1012 m_update_ready_bit = true;
1013 }
1014 }
1015 break;
1016
1017 }
1018 }
1019
1020
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)1021 void mos8563_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
1022 {
1023 switch (id)
1024 {
1025 case TIMER_BLOCK_COPY:
1026 {
1027 uint8_t data = VSS_COPY ? read_videoram(m_block_addr++) : m_data;
1028
1029 write_videoram(m_update_addr++, data);
1030
1031 if (--m_word_count)
1032 {
1033 m_block_copy_timer->adjust(cclks_to_attotime(1));
1034 }
1035 else
1036 {
1037 m_update_ready_bit = 1;
1038 }
1039 break;
1040 }
1041 default:
1042 mc6845_device::device_timer(timer, id, param, ptr);
1043 break;
1044 }
1045 }
1046
1047
get_ma()1048 uint16_t mc6845_device::get_ma()
1049 {
1050 update_counters();
1051
1052 return ( m_line_address + m_character_counter ) & 0x3fff;
1053 }
1054
1055
get_ra()1056 uint8_t mc6845_device::get_ra()
1057 {
1058 return m_raster_counter;
1059 }
1060
1061
assert_light_pen_input()1062 void mc6845_device::assert_light_pen_input()
1063 {
1064 /* compute the pixel coordinate of the NEXT character -- this is when the light pen latches */
1065 /* set the timer that will latch the display address into the light pen registers */
1066 m_light_pen_latch_timer->adjust(cclks_to_attotime(1));
1067 }
1068
1069
set_hpixels_per_column(int hpixels_per_column)1070 void mc6845_device::set_hpixels_per_column(int hpixels_per_column)
1071 {
1072 /* validate arguments */
1073 assert(hpixels_per_column > 0);
1074
1075 if (hpixels_per_column != m_hpixels_per_column)
1076 {
1077 m_hpixels_per_column = hpixels_per_column;
1078 recompute_parameters(false);
1079 }
1080 }
1081
1082
update_cursor_state()1083 void mc6845_device::update_cursor_state()
1084 {
1085 /* save and increment cursor counter */
1086 uint8_t last_cursor_blink_count = m_cursor_blink_count;
1087 m_cursor_blink_count = m_cursor_blink_count + 1;
1088
1089 /* switch on cursor blinking mode */
1090 switch (m_cursor_start_ras & 0x60)
1091 {
1092 /* always on */
1093 case 0x00: m_cursor_state = true; break;
1094
1095 /* always off */
1096 default:
1097 case 0x20: m_cursor_state = false; break;
1098
1099 /* fast blink */
1100 case 0x40:
1101 if ((last_cursor_blink_count & 0x10) != (m_cursor_blink_count & 0x10))
1102 m_cursor_state = !m_cursor_state;
1103 break;
1104
1105 /* slow blink */
1106 case 0x60:
1107 if ((last_cursor_blink_count & 0x20) != (m_cursor_blink_count & 0x20))
1108 m_cursor_state = !m_cursor_state;
1109 break;
1110 }
1111 }
1112
1113
draw_scanline(int y,bitmap_rgb32 & bitmap,const rectangle & cliprect)1114 uint8_t mc6845_device::draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1115 {
1116 /* compute the current raster line */
1117 uint8_t ra = y % (m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust));
1118
1119 // Check if the cursor is visible and is on this scanline.
1120 int cursor_visible = check_cursor_visible(ra, m_current_disp_addr);
1121
1122 // Compute the cursor X position, or -1 if not visible. This position
1123 // is in units of characters and is relative to the start of the
1124 // displayable area, not relative to the screen bitmap origin.
1125 int8_t cursor_x = cursor_visible ? (m_cursor_addr - m_current_disp_addr) : -1;
1126 int de = (y <= m_max_visible_y) ? 1 : 0;
1127 int vbp = m_vert_pix_total - m_vsync_off_pos;
1128 if (vbp < 0) vbp = 0;
1129 int hbp = m_horiz_pix_total - m_hsync_off_pos;
1130 if (hbp < 0) hbp = 0;
1131
1132 /* call the external system to draw it */
1133 if (MODE_ROW_COLUMN_ADDRESSING)
1134 {
1135 uint8_t cc = 0;
1136 uint8_t cr = y / (m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust));
1137 uint16_t ma = (cr << 8) | cc;
1138
1139 m_update_row_cb(bitmap, cliprect, ma + m_disp_start_addr, ra, y, m_horiz_disp, cursor_x, de, hbp, vbp);
1140 }
1141 else
1142 {
1143 m_update_row_cb(bitmap, cliprect, m_current_disp_addr, ra, y, m_horiz_disp, cursor_x, de, hbp, vbp);
1144 }
1145
1146 /* update MA if the last raster address */
1147 if (ra == m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust) - 1)
1148 m_current_disp_addr = (m_current_disp_addr + m_horiz_disp) & 0x3fff;
1149
1150 return ra;
1151 }
1152
1153
draw_scanline(int y,bitmap_rgb32 & bitmap,const rectangle & cliprect)1154 uint8_t hd6345_device::draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1155 {
1156 uint8_t ra = hd6845s_device::draw_scanline(y, bitmap, cliprect);
1157
1158 /* update MA for screen split */
1159 if (ra == m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust) - 1)
1160 {
1161 int y_pos = y / (m_max_ras_addr + (MODE_INTERLACE_AND_VIDEO ? m_interlace_adjust : m_noninterlace_adjust));
1162 if ((m_control1 & 0x03) > 0 && y_pos == m_disp2_pos && m_disp2_pos != m_disp3_pos && m_disp2_pos != m_disp4_pos)
1163 m_current_disp_addr = m_disp2_start_addr;
1164 if ((m_control1 & 0x03) > 1 && y_pos == m_disp3_pos && m_disp3_pos != m_disp2_pos && m_disp3_pos != m_disp4_pos)
1165 m_current_disp_addr = m_disp3_start_addr;
1166 if ((m_control1 & 0x03) > 2 && y_pos == m_disp4_pos && m_disp4_pos != m_disp2_pos && m_disp4_pos != m_disp3_pos)
1167 m_current_disp_addr = m_disp4_start_addr;
1168 }
1169
1170 return ra;
1171 }
1172
1173
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)1174 uint32_t mc6845_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1175 {
1176 assert(bitmap.valid());
1177
1178 if (m_has_valid_parameters)
1179 {
1180 assert(!m_update_row_cb.isnull());
1181
1182 if (m_display_disabled_msg_shown == true)
1183 {
1184 logerror("M6845: Valid screen parameters - display reenabled!!!\n");
1185 m_display_disabled_msg_shown = false;
1186 }
1187
1188 /* call the set up function if any */
1189 if (!m_begin_update_cb.isnull())
1190 m_begin_update_cb(bitmap, cliprect);
1191
1192 if (cliprect.min_y == 0)
1193 {
1194 /* read the start address at the beginning of the frame */
1195 m_current_disp_addr = m_disp_start_addr;
1196 }
1197
1198 /* for each row in the visible region */
1199 for (uint16_t y = cliprect.min_y; y <= cliprect.max_y; y++)
1200 {
1201 this->draw_scanline(y, bitmap, cliprect);
1202 }
1203
1204 /* call the tear down function if any */
1205 if (!m_end_update_cb.isnull())
1206 m_end_update_cb(bitmap, cliprect);
1207 }
1208 else
1209 {
1210 if (m_display_disabled_msg_shown == false)
1211 {
1212 logerror("M6845: Invalid screen parameters - display disabled!!!\n");
1213 m_display_disabled_msg_shown = true;
1214 }
1215 }
1216
1217 return 0;
1218 }
1219
1220
device_start()1221 void mc6845_device::device_start()
1222 {
1223 assert(clock() > 0);
1224 assert(m_hpixels_per_column > 0);
1225
1226 /* bind delegates */
1227 m_reconfigure_cb.resolve();
1228 m_begin_update_cb.resolve();
1229 m_update_row_cb.resolve();
1230 m_end_update_cb.resolve();
1231 m_on_update_addr_changed_cb.resolve();
1232
1233 /* resolve callbacks */
1234 m_out_de_cb.resolve_safe();
1235 m_out_cur_cb.resolve_safe();
1236 m_out_hsync_cb.resolve_safe();
1237 m_out_vsync_cb.resolve_safe();
1238
1239 /* create the timers */
1240 m_line_timer = timer_alloc(TIMER_LINE);
1241 m_de_off_timer = timer_alloc(TIMER_DE_OFF);
1242 m_cur_on_timer = timer_alloc(TIMER_CUR_ON);
1243 m_cur_off_timer = timer_alloc(TIMER_CUR_OFF);
1244 m_hsync_on_timer = timer_alloc(TIMER_HSYNC_ON);
1245 m_hsync_off_timer = timer_alloc(TIMER_HSYNC_OFF);
1246 m_light_pen_latch_timer = timer_alloc(TIMER_LIGHT_PEN_LATCH);
1247 m_upd_adr_timer = timer_alloc(TIMER_UPD_ADR);
1248 m_upd_trans_timer = timer_alloc(TIMER_UPD_TRANS);
1249
1250 /* Use some large startup values */
1251 m_horiz_char_total = 0xff;
1252 m_max_ras_addr = 0x1f;
1253 m_vert_char_total = 0x7f;
1254 m_mode_control = 0x00;
1255
1256 m_supports_disp_start_addr_r = false; // MC6845 can not read Display Start (double checked on datasheet)
1257 m_supports_vert_sync_width = false;
1258 m_supports_status_reg_d5 = false;
1259 m_supports_status_reg_d6 = false;
1260 m_supports_status_reg_d7 = false;
1261 m_supports_transparent = false;
1262 m_has_valid_parameters = false;
1263 m_display_disabled_msg_shown = false;
1264 m_line_enable_ff = false;
1265 m_vsync_ff = 0;
1266 m_raster_counter = 0;
1267 m_adjust_active = 0;
1268 m_horiz_sync_pos = 1;
1269 m_vert_sync_pos = 1;
1270 m_de = 0;
1271 m_sync_width = 1;
1272 m_horiz_pix_total = m_vert_pix_total = 0;
1273 m_max_visible_x = m_max_visible_y = 0;
1274 m_hsync_on_pos = m_vsync_on_pos = 0;
1275 m_hsync_off_pos = m_vsync_off_pos = 0;
1276 m_vsync = m_hsync = 0;
1277 m_cur = 0;
1278 m_line_counter = 0;
1279 m_horiz_disp = m_vert_disp = 0;
1280 m_vert_sync_pos = 0;
1281 m_vert_total_adj = 0;
1282 m_cursor_start_ras = m_cursor_end_ras = m_cursor_addr = 0;
1283 m_cursor_blink_count = 0;
1284 m_cursor_state = 0;
1285 m_update_ready_bit = 0;
1286 m_line_address = 0;
1287 m_current_disp_addr = 0;
1288 m_disp_start_addr = 0;
1289 m_noninterlace_adjust = 1;
1290 m_interlace_adjust = 1;
1291
1292 save_item(NAME(m_show_border_area));
1293 save_item(NAME(m_visarea_adjust_min_x));
1294 save_item(NAME(m_visarea_adjust_max_x));
1295 save_item(NAME(m_visarea_adjust_min_y));
1296 save_item(NAME(m_visarea_adjust_max_y));
1297 save_item(NAME(m_hpixels_per_column));
1298 save_item(NAME(m_register_address_latch));
1299 save_item(NAME(m_horiz_char_total));
1300 save_item(NAME(m_horiz_disp));
1301 save_item(NAME(m_horiz_sync_pos));
1302 save_item(NAME(m_sync_width));
1303 save_item(NAME(m_vert_char_total));
1304 save_item(NAME(m_vert_total_adj));
1305 save_item(NAME(m_vert_disp));
1306 save_item(NAME(m_vert_sync_pos));
1307 save_item(NAME(m_mode_control));
1308 save_item(NAME(m_max_ras_addr));
1309 save_item(NAME(m_cursor_start_ras));
1310 save_item(NAME(m_cursor_end_ras));
1311 save_item(NAME(m_disp_start_addr));
1312 save_item(NAME(m_cursor_addr));
1313 save_item(NAME(m_light_pen_addr));
1314 save_item(NAME(m_light_pen_latched));
1315 save_item(NAME(m_cursor_state));
1316 save_item(NAME(m_cursor_blink_count));
1317 save_item(NAME(m_update_addr));
1318 save_item(NAME(m_update_ready_bit));
1319 save_item(NAME(m_cur));
1320 save_item(NAME(m_hsync));
1321 save_item(NAME(m_vsync));
1322 save_item(NAME(m_de));
1323 save_item(NAME(m_character_counter));
1324 save_item(NAME(m_hsync_width_counter));
1325 save_item(NAME(m_line_counter));
1326 save_item(NAME(m_raster_counter));
1327 save_item(NAME(m_adjust_counter));
1328 save_item(NAME(m_vsync_width_counter));
1329 save_item(NAME(m_line_enable_ff));
1330 save_item(NAME(m_vsync_ff));
1331 save_item(NAME(m_adjust_active));
1332 save_item(NAME(m_line_address));
1333 save_item(NAME(m_cursor_x));
1334 save_item(NAME(m_has_valid_parameters));
1335 }
1336
1337
device_start()1338 void mc6845_1_device::device_start()
1339 {
1340 mc6845_device::device_start();
1341
1342 m_supports_disp_start_addr_r = true;
1343 m_supports_vert_sync_width = true;
1344 m_supports_status_reg_d5 = false;
1345 m_supports_status_reg_d6 = false;
1346 m_supports_status_reg_d7 = false;
1347 m_supports_transparent = false;
1348 }
1349
1350
device_start()1351 void c6545_1_device::device_start()
1352 {
1353 mc6845_device::device_start();
1354
1355 m_supports_disp_start_addr_r = false;
1356 m_supports_vert_sync_width = true;
1357 m_supports_status_reg_d5 = true;
1358 m_supports_status_reg_d6 = true;
1359 m_supports_status_reg_d7 = false;
1360 m_supports_transparent = false;
1361 }
1362
1363
device_start()1364 void r6545_1_device::device_start()
1365 {
1366 mc6845_device::device_start();
1367
1368 m_supports_disp_start_addr_r = false;
1369 m_supports_vert_sync_width = true;
1370 m_supports_status_reg_d5 = true;
1371 m_supports_status_reg_d6 = true;
1372 m_supports_status_reg_d7 = true;
1373 m_supports_transparent = true;
1374 }
1375
1376
device_start()1377 void hd6845s_device::device_start()
1378 {
1379 mc6845_device::device_start();
1380
1381 m_supports_disp_start_addr_r = true; // HD6845S can definitely read Display Start (double checked on datasheet)
1382 m_supports_vert_sync_width = true;
1383 m_supports_status_reg_d5 = false;
1384 m_supports_status_reg_d6 = false;
1385 m_supports_status_reg_d7 = false;
1386 m_supports_transparent = false;
1387
1388 // Non-interlace Mode, Interlace Sync Mode - When total number of rasters is RN, RN-1 shall be programmed.
1389 m_noninterlace_adjust = 1;
1390 // Interlace Sync & Video Mode - When total number of rasters is RN, RN-2 shall be programmed.
1391 m_interlace_adjust = 2;
1392 }
1393
1394
device_start()1395 void sy6545_1_device::device_start()
1396 {
1397 mc6845_device::device_start();
1398
1399 m_supports_disp_start_addr_r = false;
1400 m_supports_vert_sync_width = true;
1401 m_supports_status_reg_d5 = true;
1402 m_supports_status_reg_d6 = true;
1403 m_supports_status_reg_d7 = true;
1404 m_supports_transparent = true;
1405 }
1406
1407
device_start()1408 void sy6845e_device::device_start()
1409 {
1410 mc6845_device::device_start();
1411
1412 m_supports_disp_start_addr_r = false;
1413 m_supports_vert_sync_width = true;
1414 m_supports_status_reg_d5 = true;
1415 m_supports_status_reg_d6 = true;
1416 m_supports_status_reg_d7 = true;
1417 m_supports_transparent = true;
1418 }
1419
1420
device_start()1421 void hd6345_device::device_start()
1422 {
1423 hd6845s_device::device_start();
1424
1425 m_disp2_pos = 0;
1426 m_disp3_pos = 0;
1427 m_disp4_pos = 0;
1428 m_disp2_start_addr = 0;
1429 m_disp3_start_addr = 0;
1430 m_disp4_start_addr = 0;
1431 m_vert_sync_pos_adj = 0;
1432 m_smooth_scroll_ras = 0;
1433 m_mem_width_offs = 0;
1434 m_cursor2_start_ras = 0;
1435 m_cursor2_end_ras = 0;
1436 m_cursor2_addr = 0;
1437 m_cursor_width = 0;
1438 m_cursor2_width = 0;
1439
1440 save_item(NAME(m_disp2_pos));
1441 save_item(NAME(m_disp2_start_addr));
1442 save_item(NAME(m_disp3_pos));
1443 save_item(NAME(m_disp3_start_addr));
1444 save_item(NAME(m_disp4_pos));
1445 save_item(NAME(m_disp4_start_addr));
1446 save_item(NAME(m_vert_sync_pos_adj));
1447 save_item(NAME(m_smooth_scroll_ras));
1448 save_item(NAME(m_control1));
1449 save_item(NAME(m_control2));
1450 save_item(NAME(m_control3));
1451 save_item(NAME(m_mem_width_offs));
1452 save_item(NAME(m_cursor2_start_ras));
1453 save_item(NAME(m_cursor2_end_ras));
1454 save_item(NAME(m_cursor2_addr));
1455 save_item(NAME(m_cursor_width));
1456 save_item(NAME(m_cursor2_width));
1457 }
1458
1459
device_start()1460 void ams40489_device::device_start()
1461 {
1462 mc6845_device::device_start();
1463
1464 m_supports_disp_start_addr_r = true;
1465 m_supports_vert_sync_width = false;
1466 m_supports_status_reg_d5 = false;
1467 m_supports_status_reg_d6 = false;
1468 m_supports_status_reg_d7 = false;
1469 m_supports_transparent = false;
1470 }
1471
1472
device_start()1473 void mos8563_device::device_start()
1474 {
1475 mc6845_device::device_start();
1476
1477 /* create the timers */
1478 m_block_copy_timer = timer_alloc(TIMER_BLOCK_COPY);
1479
1480 m_supports_status_reg_d5 = true;
1481 m_supports_status_reg_d6 = true;
1482 m_supports_status_reg_d7 = true;
1483 m_update_ready_bit = 1;
1484
1485 // default update_row delegate
1486 m_update_row_cb.set(*this, FUNC(mos8563_device::vdc_update_row));
1487
1488 m_char_blink_state = false;
1489 m_char_blink_count = 0;
1490 m_attribute_addr = 0;
1491 m_horiz_char = 0;
1492 m_vert_char_disp = 0;
1493 m_vert_scroll = 0;
1494 m_horiz_scroll = 0;
1495 m_color = 0;
1496 m_row_addr_incr = 0;
1497 m_char_base_addr = 0;
1498 m_underline_ras = 0;
1499 m_word_count = 0;
1500 m_data = 0;
1501 m_block_addr = 0;
1502 m_de_begin = 0;
1503 m_dram_refresh = 0;
1504 m_sync_polarity = 0;
1505
1506 m_revision = 1;
1507
1508 // initialize video RAM
1509 uint8_t data = 0xff;
1510
1511 for (offs_t offset = 0; offset < 0x10000; offset++)
1512 {
1513 write_videoram(offset, data);
1514 data ^= 0xff;
1515 }
1516
1517 // VICE palette
1518 set_pen_color(0, rgb_t::black());
1519 set_pen_color(1, rgb_t(0x55, 0x55, 0x55));
1520 set_pen_color(2, rgb_t(0x00, 0x00, 0xaa));
1521 set_pen_color(3, rgb_t(0x55, 0x55, 0xff));
1522 set_pen_color(4, rgb_t(0x00, 0xaa, 0x00));
1523 set_pen_color(5, rgb_t(0x55, 0xff, 0x55));
1524 set_pen_color(6, rgb_t(0x00, 0xaa, 0xaa));
1525 set_pen_color(7, rgb_t(0x55, 0xff, 0xff));
1526 set_pen_color(8, rgb_t(0xaa, 0x00, 0x00));
1527 set_pen_color(9, rgb_t(0xff, 0x55, 0x55));
1528 set_pen_color(10, rgb_t(0xaa, 0x00, 0xaa));
1529 set_pen_color(11, rgb_t(0xff, 0x55, 0xff));
1530 set_pen_color(12, rgb_t(0xaa, 0x55, 0x00));
1531 set_pen_color(13, rgb_t(0xff, 0xff, 0x55));
1532 set_pen_color(14, rgb_t(0xaa, 0xaa, 0xaa));
1533 set_pen_color(15, rgb_t::white());
1534
1535 save_item(NAME(m_char_buffer));
1536 save_item(NAME(m_attr_buffer));
1537 save_item(NAME(m_attribute_addr));
1538 save_item(NAME(m_horiz_char));
1539 save_item(NAME(m_vert_char_disp));
1540 save_item(NAME(m_vert_scroll));
1541 save_item(NAME(m_horiz_scroll));
1542 save_item(NAME(m_color));
1543 save_item(NAME(m_row_addr_incr));
1544 save_item(NAME(m_char_base_addr));
1545 save_item(NAME(m_underline_ras));
1546 save_item(NAME(m_word_count));
1547 save_item(NAME(m_data));
1548 save_item(NAME(m_block_addr));
1549 save_item(NAME(m_de_begin));
1550 save_item(NAME(m_dram_refresh));
1551 save_item(NAME(m_sync_polarity));
1552 save_item(NAME(m_revision));
1553 save_item(NAME(m_clk_scale));
1554 }
1555
1556
device_start()1557 void mos8568_device::device_start()
1558 {
1559 mos8563_device::device_start();
1560 }
1561
1562
device_reset()1563 void mc6845_device::device_reset()
1564 {
1565 /* internal registers other than status remain unchanged, all outputs go low */
1566 m_out_de_cb(false);
1567
1568 m_out_hsync_cb(false);
1569
1570 m_out_vsync_cb(false);
1571
1572 if (!m_line_timer->enabled())
1573 m_line_timer->adjust(cclks_to_attotime(m_horiz_char_total + 1));
1574
1575 m_light_pen_latched = false;
1576
1577 m_cursor_addr = 0;
1578 m_line_address = 0;
1579 m_horiz_disp = 0;
1580 m_cursor_x = 0;
1581 m_mode_control = 0;
1582 m_register_address_latch = 0;
1583 m_update_addr = 0;
1584 m_light_pen_addr = 0;
1585 }
1586
1587
device_reset()1588 void r6545_1_device::device_reset() { mc6845_device::device_reset(); }
device_reset()1589 void mc6845_1_device::device_reset() { mc6845_device::device_reset(); }
device_reset()1590 void hd6845s_device::device_reset() { mc6845_device::device_reset(); }
device_reset()1591 void c6545_1_device::device_reset() { mc6845_device::device_reset(); }
device_reset()1592 void sy6545_1_device::device_reset() { mc6845_device::device_reset(); }
device_reset()1593 void sy6845e_device::device_reset() { mc6845_device::device_reset(); }
1594
device_reset()1595 void hd6345_device::device_reset()
1596 {
1597 hd6845s_device::device_reset();
1598
1599 m_control1 = 0;
1600 m_control2 = 0;
1601 m_control3 = 0;
1602 }
1603
device_reset()1604 void ams40489_device::device_reset() { mc6845_device::device_reset(); }
1605
device_reset()1606 void mos8563_device::device_reset()
1607 {
1608 mc6845_device::device_reset();
1609
1610 m_sync_polarity = 0xc0;
1611 }
1612
device_reset()1613 void mos8568_device::device_reset() { mos8563_device::device_reset(); }
1614
1615
1616 //-------------------------------------------------
1617 // memory_space_config - return a description of
1618 // any address spaces owned by this device
1619 //-------------------------------------------------
1620
memory_space_config() const1621 device_memory_interface::space_config_vector mos8563_device::memory_space_config() const
1622 {
1623 return space_config_vector {
1624 std::make_pair(0, &m_videoram_space_config),
1625 };
1626 }
1627
1628 // default address maps
mos8563_videoram_map(address_map & map)1629 void mos8563_device::mos8563_videoram_map(address_map &map)
1630 {
1631 if (!has_configured_map(0))
1632 map(0x0000, 0xffff).ram();
1633 }
1634
1635
r6545_1_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1636 r6545_1_device::r6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1637 : mc6845_device(mconfig, R6545_1, tag, owner, clock)
1638 {
1639 }
1640
1641
mc6845_1_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1642 mc6845_1_device::mc6845_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1643 : mc6845_device(mconfig, MC6845_1, tag, owner, clock)
1644 {
1645 }
1646
1647
hd6845s_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)1648 hd6845s_device::hd6845s_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
1649 : mc6845_device(mconfig, type, tag, owner, clock)
1650 {
1651 }
1652
1653
hd6845s_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1654 hd6845s_device::hd6845s_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1655 : mc6845_device(mconfig, HD6845S, tag, owner, clock)
1656 {
1657 }
1658
1659
c6545_1_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1660 c6545_1_device::c6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1661 : mc6845_device(mconfig, C6545_1, tag, owner, clock)
1662 {
1663 }
1664
1665
sy6545_1_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1666 sy6545_1_device::sy6545_1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1667 : mc6845_device(mconfig, SY6545_1, tag, owner, clock)
1668 {
1669 }
1670
1671
sy6845e_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1672 sy6845e_device::sy6845e_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1673 : mc6845_device(mconfig, SY6845E, tag, owner, clock)
1674 {
1675 }
1676
1677
hd6345_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1678 hd6345_device::hd6345_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1679 : hd6845s_device(mconfig, HD6345, tag, owner, clock)
1680 {
1681 }
1682
1683
ams40489_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1684 ams40489_device::ams40489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1685 : mc6845_device(mconfig, AMS40489, tag, owner, clock)
1686 {
1687 }
1688
1689
mos8563_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)1690 mos8563_device::mos8563_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
1691 : mc6845_device(mconfig, type, tag, owner, clock)
1692 , device_memory_interface(mconfig, *this)
1693 , device_palette_interface(mconfig, *this)
1694 , m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(mos8563_device::mos8563_videoram_map), this))
1695 {
1696 m_clk_scale = 8;
1697 }
1698
1699
mos8563_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1700 mos8563_device::mos8563_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1701 : mos8563_device(mconfig, MOS8563, tag, owner, clock)
1702 {
1703 }
1704
1705
mos8568_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1706 mos8568_device::mos8568_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1707 : mos8563_device(mconfig, MOS8568, tag, owner, clock)
1708 {
1709 }
1710
1711
update_cursor_state()1712 void mos8563_device::update_cursor_state()
1713 {
1714 mc6845_device::update_cursor_state();
1715
1716 /* save and increment character blink counter */
1717 uint8_t last_char_blink_count = m_char_blink_count;
1718 m_char_blink_count++;
1719
1720 /* switch on character blinking mode */
1721 if (VSS_CBRATE)
1722 {
1723 if ((last_char_blink_count & 0x20) != (m_char_blink_count & 0x20))
1724 m_char_blink_state = !m_char_blink_state;
1725 }
1726 else
1727 {
1728 if ((last_char_blink_count & 0x10) != (m_char_blink_count & 0x10))
1729 m_char_blink_state = !m_char_blink_state;
1730 }
1731 }
1732
1733
draw_scanline(int y,bitmap_rgb32 & bitmap,const rectangle & cliprect)1734 uint8_t mos8563_device::draw_scanline(int y, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1735 {
1736 uint8_t ra = mc6845_device::draw_scanline(y, bitmap, cliprect);
1737
1738 if (ra == m_max_ras_addr)
1739 m_current_disp_addr = (m_current_disp_addr + m_row_addr_incr) & 0x3fff;
1740
1741 return ra;
1742 }
1743
1744
MC6845_UPDATE_ROW(mos8563_device::vdc_update_row)1745 MC6845_UPDATE_ROW( mos8563_device::vdc_update_row )
1746 {
1747 ra += (m_vert_scroll & 0x0f);
1748 ra &= 0x0f;
1749
1750 uint8_t cth = (m_horiz_char >> 4) + (HSS_DBL ? 0 : 1);
1751 uint8_t cdh = (m_horiz_char & 0x0f) + (HSS_DBL ? 0 : 1);
1752 uint8_t cdv = m_vert_char_disp;
1753
1754 for (int column = 0; column < x_count; column++)
1755 {
1756 uint8_t code = read_videoram(ma + column);
1757 uint8_t attr = 0;
1758
1759 int fg = m_color >> 4;
1760 int bg = m_color & 0x0f;
1761
1762 if (HSS_ATTR)
1763 {
1764 offs_t attr_addr = m_attribute_addr + ma + column;
1765 attr = read_videoram(attr_addr);
1766 }
1767
1768 if (HSS_TEXT)
1769 {
1770 if (HSS_ATTR)
1771 {
1772 fg = ATTR_FOREGROUND;
1773 bg = ATTR_BACKGROUND;
1774 }
1775
1776 if (VSS_RVS) code ^= 0xff;
1777
1778 for (int bit = 0; bit < cdh; bit++)
1779 {
1780 int x = (m_horiz_scroll & 0x0f) - cth + (column * cth) + bit;
1781 if (x < 0) x = 0;
1782 int color = BIT(code, 7) ? fg : bg;
1783
1784 bitmap.pix(vbp + y, hbp + x) = pen(de ? color : 0);
1785 }
1786 }
1787 else
1788 {
1789 if (HSS_ATTR)
1790 {
1791 fg = ATTR_COLOR;
1792 }
1793
1794 offs_t font_addr;
1795
1796 if (m_max_ras_addr < 16)
1797 {
1798 font_addr = ((m_char_base_addr & 0xe0) << 8) | (ATTR_ALTERNATE_CHARSET << 12) | (code << 4) | (ra & 0x0f);
1799 }
1800 else
1801 {
1802 font_addr = ((m_char_base_addr & 0xc0) << 8) | (ATTR_ALTERNATE_CHARSET << 13) | (code << 5) | (ra & 0x1f);
1803 }
1804
1805 uint8_t data = read_videoram(font_addr);
1806
1807 if (ra >= cdv) data = 0;
1808 if (ATTR_UNDERLINE && (ra == m_underline_ras)) data = 0xff;
1809 if (ATTR_BLINK && !m_char_blink_state) data = 0;
1810 if (ATTR_REVERSE) data ^= 0xff;
1811 if (column == cursor_x) data ^= 0xff;
1812 if (VSS_RVS) data ^= 0xff;
1813
1814 for (int bit = 0; bit < cdh; bit++)
1815 {
1816 int x = (m_horiz_scroll & 0x0f) - cth + (column * cth) + bit;
1817 if (x < 0) x = 0;
1818 int color = BIT(data, 7) ? fg : bg;
1819
1820 bitmap.pix(vbp + y, hbp + x) = pen(de ? color : 0);
1821
1822 if ((bit < 8) || !HSS_SEMI) data <<= 1;
1823 }
1824 }
1825 }
1826 }
1827