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