1 // license:BSD-3-Clause
2 // copyright-holders:Dirk Best
3 /***************************************************************************
4 
5     Intel 82730
6 
7     Text Coprocessor
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "i82730.h"
13 
14 #include "screen.h"
15 
16 
17 //**************************************************************************
18 //  CONSTANTS
19 //**************************************************************************
20 
21 #define VERBOSE 1
22 #define VERBOSE_COMMANDS    1
23 #define VERBOSE_DATASTREAM  0
24 
25 
26 //**************************************************************************
27 //  DEVICE DEFINITIONS
28 //**************************************************************************
29 
30 DEFINE_DEVICE_TYPE(I82730, i82730_device, "i82730", "Intel 82730")
31 
32 const char *const i82730_device::s_command_names[] =
33 {
34 	/* 00 */ "NOP",
35 	/* 01 */ "START DISPLAY",
36 	/* 02 */ "START VIRTUAL DISPLAY",
37 	/* 03 */ "STOP DISPLAY",
38 	/* 04 */ "MODE SET",
39 	/* 05 */ "LOAD CBP",
40 	/* 06 */ "LOAD INTMASK",
41 	/* 07 */ "LPEN ENABLE",
42 	/* 08 */ "READ STATUS",
43 	/* 09 */ "LD CUR POS",
44 	/* 0a */ "SELF TEST",
45 	/* 0b */ "TEST ROW BUFFER"
46 };
47 
48 
49 //**************************************************************************
50 //  LIVE DEVICE
51 //**************************************************************************
52 
53 //-------------------------------------------------
54 //  i82730_device - constructor
55 //-------------------------------------------------
56 
i82730_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)57 i82730_device::i82730_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
58 	device_t(mconfig, I82730, tag, owner, clock),
59 	device_video_interface(mconfig, *this),
60 	m_sint_handler(*this),
61 	m_update_row_cb(*this),
62 	m_cpu(*this, finder_base::DUMMY_TAG),
63 	m_program(nullptr),
64 	m_row_timer(nullptr),
65 	m_initialized(false),
66 	m_mode_set(false),
67 	m_ca(0),
68 	m_sysbus(0x00),
69 	m_ibp(0x0000),
70 	m_cbp(0x0000),
71 	m_intmask(0xffff),
72 	m_status(0x0000),
73 	m_list_switch(0),
74 	m_auto_line_feed(0),
75 	m_max_dma_count(0),
76 	m_lptr(0),
77 	m_sptr(0),
78 	m_dma_burst_space(0),
79 	m_dma_burst_length(0),
80 	m_hfldstrt(0),
81 	m_margin(0),
82 	m_lpr(0),
83 	m_field_attribute_mask(0),
84 	m_vsyncstp(0),
85 	m_vfldstrt(0),
86 	m_vfldstp(0),
87 	m_frame_int_count(0),
88 	m_row_index(0)
89 {
90 }
91 
92 //-------------------------------------------------
93 //  device_start - device-specific startup
94 //-------------------------------------------------
95 
device_start()96 void i82730_device::device_start()
97 {
98 	// register bitmap
99 	screen().register_screen_bitmap(m_bitmap);
100 
101 	// resolve callbacks
102 	m_sint_handler.resolve_safe();
103 
104 	// bind delegates
105 	m_update_row_cb.resolve();
106 
107 	// allocate row timer
108 	m_row_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(i82730_device::row_update), this));
109 }
110 
111 //-------------------------------------------------
112 //  device_reset - device-specific reset
113 //-------------------------------------------------
114 
device_reset()115 void i82730_device::device_reset()
116 {
117 	m_program = &m_cpu->space(AS_PROGRAM);
118 
119 	m_initialized = false;
120 	m_mode_set = false;
121 
122 	m_ca = 0;
123 	m_status = 0x0000;
124 }
125 
126 
127 //**************************************************************************
128 //  MEMORY ACCESS
129 //**************************************************************************
130 
read_byte(offs_t address)131 uint8_t i82730_device::read_byte(offs_t address)
132 {
133 	return m_program->read_byte(address);
134 }
135 
read_word(offs_t address)136 uint16_t i82730_device::read_word(offs_t address)
137 {
138 	uint16_t data;
139 
140 	if (sysbus_16bit() && WORD_ALIGNED(address))
141 	{
142 		data = m_program->read_word(address);
143 	}
144 	else
145 	{
146 		data  = m_program->read_byte(address);
147 		data |= m_program->read_byte(address + 1) << 8;
148 	}
149 
150 	return data;
151 }
152 
write_byte(offs_t address,uint8_t data)153 void i82730_device::write_byte(offs_t address, uint8_t data)
154 {
155 	m_program->write_byte(address, data);
156 }
157 
write_word(offs_t address,uint16_t data)158 void i82730_device::write_word(offs_t address, uint16_t data)
159 {
160 	if (sysbus_16bit() && WORD_ALIGNED(address))
161 	{
162 		m_program->write_word(address, data);
163 	}
164 	else
165 	{
166 		m_program->write_byte(address, data & 0xff);
167 		m_program->write_byte(address + 1, (data >> 8) & 0xff);
168 	}
169 }
170 
171 
172 //**************************************************************************
173 //  IMPLEMENTATION
174 //**************************************************************************
175 
update_interrupts()176 void i82730_device::update_interrupts()
177 {
178 	uint16_t code = m_status & ~m_intmask & ~(VDIP | DIP);
179 	write_word(m_cbp + 20, code);
180 
181 	if (code)
182 		m_sint_handler(1);
183 }
184 
mode_set()185 void i82730_device::mode_set()
186 {
187 	uint32_t mptr = (read_word(m_cbp + 32) << 16) | read_word(m_cbp + 30);
188 	uint16_t tmp;
189 
190 	tmp = read_word(mptr);
191 	m_dma_burst_space = tmp & 0x7f;
192 	m_dma_burst_length = (tmp >> 8) & 0x7f;
193 
194 	tmp = read_word(mptr + 2);
195 	uint8_t hsyncstp = tmp & 0xff;
196 	uint8_t line_length = (tmp >> 8) & 0xff;
197 
198 	tmp = read_word(mptr + 4);
199 	uint8_t hfldstp = tmp & 0xff;
200 	m_hfldstrt = (tmp >> 8) & 0xff;
201 
202 	tmp = read_word(mptr + 6);
203 	uint8_t hbrdstp = tmp & 0xff;
204 	uint8_t hbrdstrt = (tmp >> 8) & 0xff;
205 
206 	tmp = read_word(mptr + 8);
207 	m_margin = tmp & 0x1f;
208 
209 	tmp = read_word(mptr + 10);
210 	m_lpr = tmp & 0x1f;
211 
212 	tmp = read_word(mptr + 24);
213 	m_field_attribute_mask = tmp & 0x7fff;
214 
215 	tmp = read_word(mptr + 26);
216 	uint16_t frame_length = tmp & 0x7ff;
217 
218 	tmp = read_word(mptr + 28);
219 	m_vsyncstp = tmp & 0x7ff;
220 
221 	tmp = read_word(mptr + 30);
222 	m_vfldstrt = tmp & 0x7ff;
223 
224 	tmp = read_word(mptr + 32);
225 	m_vfldstp = tmp & 0x7ff;
226 
227 	tmp = read_word(mptr + 38);
228 	m_frame_int_count = tmp & 0x0f;
229 
230 	// setup screen mode
231 	rectangle visarea(hbrdstrt * 16, hbrdstp * 16 - 1, m_vsyncstp, m_vfldstp + m_margin + 1 + m_lpr - 1);
232 	attoseconds_t period = HZ_TO_ATTOSECONDS(clock() * 16) * line_length * 16 * frame_length;
233 	screen().configure(line_length * 16, frame_length, visarea, period);
234 
235 	// start display is now valid
236 	m_mode_set = true;
237 
238 	// adjust timer for the new mode
239 	m_row_timer->adjust(screen().time_until_pos(0));
240 
241 	// output some debug info
242 	if (VERBOSE)
243 	{
244 		logerror("%s('%s'): ---- setting mode ----\n", shortname(), basetag());
245 		logerror("%s('%s'): dma burst length %02x, space %02x\n", shortname(), basetag(), m_dma_burst_length, m_dma_burst_space);
246 		logerror("%s('%s'): margin %02x, lpr %02x\n", shortname(), basetag(), m_margin, m_lpr);
247 		logerror("%s('%s'): hsyncstp: %02x, line_length: %02x, hfldstrt: %02x, hbrdstart: %02x, hfldstop: %02x, hbrdstop: %02x\n",
248 			shortname(), basetag(), hsyncstp, line_length, m_hfldstrt, hbrdstrt, hfldstp, hbrdstp);
249 		logerror("%s('%s'): frame_length %04x, vsyncstp: %04x, vfldstrt: %04x, vfldstp: %04x\n",
250 			shortname(), basetag(), frame_length, m_vsyncstp, m_vfldstrt, m_vfldstp);
251 	}
252 }
253 
execute_command()254 void i82730_device::execute_command()
255 {
256 	uint8_t command = read_byte(m_cbp + 1);
257 	uint16_t tmp;
258 
259 	if (VERBOSE_COMMANDS && command < ARRAY_LENGTH(s_command_names))
260 		logerror("%s('%s'): executing command: %s [cbp = %08x]\n", shortname(), basetag(), s_command_names[command], m_cbp);
261 
262 	tmp = read_word(m_cbp + 2);
263 	m_list_switch = BIT(tmp, 6);
264 	m_auto_line_feed = BIT(tmp, 7);
265 
266 	tmp = read_word(m_cbp + 4);
267 	m_max_dma_count = tmp & 0xff;
268 
269 	switch (command)
270 	{
271 	// NOP
272 	case 0x00:
273 		break;
274 
275 	// START DISPLAY
276 	case 0x01:
277 		if (m_mode_set)
278 			m_status = (m_status & ~VDIP) | DIP;
279 		break;
280 
281 	// START VIRTUAL DISPLAY
282 	case 0x02:
283 		if (m_mode_set)
284 			m_status = VDIP | (m_status & ~DIP);
285 		break;
286 
287 	// STOP DISPLAY
288 	case 0x03:
289 		m_status &= ~(VDIP | DIP);
290 		break;
291 
292 	// MODE SET
293 	case 0x04:
294 		mode_set();
295 		break;
296 
297 	// LOAD CBP
298 	case 0x05:
299 		m_cbp = (read_word(m_cbp + 16) << 16) | read_word(m_cbp + 14);
300 		execute_command();
301 		break;
302 
303 	// LOAD INTMASK
304 	case 0x06:
305 		m_intmask = read_word(m_cbp + 22);
306 		if (VERBOSE_COMMANDS)
307 			logerror("%s('%s'): intmask now %04x\n", shortname(), basetag(), m_intmask);
308 		break;
309 
310 	// LPEN ENABLE
311 	case 0x07:
312 		fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), s_command_names[command]);
313 		break;
314 
315 	// READ STATUS
316 	case 0x08:
317 		write_word(m_cbp + 18, m_status);
318 		m_status &= (VDIP | DIP);
319 		break;
320 
321 	// LD CUR POS
322 	case 0x09:
323 		fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), s_command_names[command]);
324 		break;
325 
326 	// SELF TEST
327 	case 0x0a:
328 		fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), s_command_names[command]);
329 		break;
330 
331 	// TEST ROW BUFFER
332 	case 0x0b:
333 		fatalerror("%s('%s'): Unimplemented command %s\n", shortname(), basetag(), s_command_names[command]);
334 		break;
335 
336 	default:
337 		if (VERBOSE_COMMANDS)
338 			logerror("%s('%s'): executing command: (reserved) [cbp = %08x]\n", shortname(), basetag(), m_cbp);
339 		m_status |= RCC;
340 		update_interrupts();
341 		break;
342 	}
343 
344 	// clear busy
345 	write_word(m_cbp, read_word(m_cbp) & 0xff00);
346 }
347 
load_row()348 void i82730_device::load_row()
349 {
350 	bool finished = false;
351 
352 	m_row[m_row_index].count = 0;
353 
354 	while (!finished)
355 	{
356 		uint16_t data = read_word(m_sptr);
357 		m_sptr += 2;
358 
359 		if (BIT(data, 15))
360 		{
361 			switch (data >> 8)
362 			{
363 			case 0x8e:
364 				m_field_attribute_mask = read_word(m_sptr) & 0x7fff;
365 				m_sptr += 2;
366 
367 				if (VERBOSE_DATASTREAM)
368 					logerror("%s('%s'): SET FIELD ATTRIB to %04x\n", shortname(), basetag(), m_field_attribute_mask);
369 
370 				break;
371 
372 			default:
373 				fatalerror("%s('%s'): Unimplemented datastream command %02x\n", shortname(), basetag(), data >> 8);
374 			}
375 		}
376 		else
377 		{
378 			// maximum row size is 200
379 			if (m_row[m_row_index].count < m_max_dma_count && m_row[m_row_index].count < 200)
380 			{
381 				m_row[m_row_index].data[m_row[m_row_index].count++] = data;
382 			}
383 			else
384 			{
385 #if 0
386 				// move to next string?
387 				if (m_auto_line_feed == 0)
388 				{
389 					m_sptr = (read_word(m_lptr + 2) << 16) | read_word(m_lptr);
390 					m_lptr += 4;
391 				}
392 #endif
393 				finished = true;
394 			}
395 		}
396 	}
397 
398 	m_sptr -= 2;
399 }
400 
TIMER_CALLBACK_MEMBER(i82730_device::row_update)401 TIMER_CALLBACK_MEMBER( i82730_device::row_update )
402 {
403 	int y = screen().vpos();
404 
405 	if (y == 0)
406 	{
407 		// clear interrupt status flags
408 		m_status &= (VDIP | DIP);
409 
410 		// clear field attribute mask
411 		m_field_attribute_mask = 0;
412 
413 		// get listbase
414 		if (m_list_switch)
415 			m_lptr = (read_word(m_cbp + 8) << 16) | read_word(m_cbp + 6);
416 		else
417 			m_lptr = (read_word(m_cbp + 12) << 16) | read_word(m_cbp + 10);
418 
419 		m_sptr = (read_word(m_lptr + 2) << 16) | read_word(m_lptr);
420 		m_lptr += 4;
421 
422 		// fetch initial row
423 		m_row_index = 0;
424 		load_row();
425 	}
426 	else if (y >= m_vsyncstp && y < m_vfldstrt)
427 	{
428 		// blank (top border)
429 	}
430 	else if (y >= m_vfldstrt && y < m_vfldstp)
431 	{
432 		uint8_t lc = (y - m_vfldstrt) % (m_lpr + 1);
433 
434 		// call driver
435 		m_update_row_cb(m_bitmap, m_row[m_row_index].data, lc, y - m_vsyncstp, m_row[m_row_index].count);
436 
437 		// swap buffers at end of row
438 		if (lc == m_lpr)
439 		{
440 			m_row_index ^= 1;
441 			load_row();
442 		}
443 	}
444 	else if (y >= m_vfldstp && y < m_vfldstp + m_margin + 1)
445 	{
446 		// margin
447 	}
448 	else if (y >= m_vfldstp + m_margin + 1 && y < m_vfldstp + m_margin + 1 + m_lpr + 1)
449 	{
450 		uint8_t lc = (y - (m_vfldstp + m_margin + 1)) % (m_lpr + 1);
451 
452 		m_sptr = (read_word(m_cbp + 36) << 16) | read_word(m_cbp + 34);
453 		load_row();
454 
455 		// call driver
456 		m_update_row_cb(m_bitmap, m_row[m_row_index].data, lc, y - m_vsyncstp, m_row[m_row_index].count);
457 	}
458 	else if (y == m_vfldstp + m_margin + 1 + m_lpr + 1)
459 	{
460 		// todo: check ca
461 
462 		// frame interrupt?
463 		if ((screen().frame_number() % m_frame_int_count) == 0)
464 			m_status |= EONF;
465 
466 		// check interrupts
467 		update_interrupts();
468 	}
469 	else
470 	{
471 		// vblank
472 	}
473 
474 	m_row_timer->adjust(screen().time_until_pos((y + 1) % screen().height()));
475 }
476 
WRITE_LINE_MEMBER(i82730_device::ca_w)477 WRITE_LINE_MEMBER( i82730_device::ca_w )
478 {
479 	if (VERBOSE)
480 		logerror("%s('%s'): ca_w %d\n", shortname(), basetag(), state);
481 
482 	// falling edge
483 	if (m_ca == 1 && state == 0)
484 	{
485 		if (!m_initialized)
486 		{
487 			// get system bus width
488 			m_sysbus = m_program->read_byte(0xfffffff6);
489 
490 			// get intermediate block pointer
491 			m_ibp = (read_word(0xfffffffe) << 16) | read_word(0xfffffffc);
492 
493 			// get system configuration byte
494 			uint8_t scb = read_byte(m_ibp + 6);
495 
496 			// clear busy
497 			write_word(m_ibp, read_word(m_ibp) & 0xff00);
498 
499 			// done
500 			m_initialized = true;
501 
502 			// output some debug info
503 			if (VERBOSE)
504 			{
505 				logerror("%s('%s'): ---- initializing ----\n", shortname(), basetag());
506 				logerror("%s('%s'): %s system bus\n", shortname(), basetag(), sysbus_16bit() ? "16-bit" : "8-bit");
507 				logerror("%s('%s'): intermediate block pointer: %08x\n", shortname(), basetag(), m_ibp);
508 				logerror("%s('%s'): addrbus: %s, clno: %d, clpos: %d, mode: %s, dtw16: %s, srdy: %s\n", shortname(), basetag(),
509 					BIT(scb, 0) ? "32-bit" : "16-bit", (scb >> 1) & 0x03, (scb >> 3) & 0x03,
510 					BIT(scb, 5) ? "master" : "slave", BIT(scb, 6) ? "16-bit" : "8-bit", BIT(scb, 7) ? "synchronous" : "asynchronous");
511 			}
512 		}
513 
514 		// fetch command block pointer
515 		m_cbp = (read_word(m_ibp + 4) << 16) | read_word(m_ibp + 2);
516 
517 		// and execute command
518 		execute_command();
519 	}
520 
521 	m_ca = state;
522 }
523 
WRITE_LINE_MEMBER(i82730_device::irst_w)524 WRITE_LINE_MEMBER( i82730_device::irst_w )
525 {
526 	if (VERBOSE)
527 		logerror("%s('%s'): irst_w %d\n", shortname(), basetag(), state);
528 
529 	m_sint_handler(0);
530 }
531 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)532 uint32_t i82730_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
533 {
534 	copybitmap(bitmap, m_bitmap, 0, 0, m_hfldstrt * 16, 0, cliprect);
535 	return 0;
536 }
537