1 // license:BSD-3-Clause
2 // copyright-holders:Sean Young, Nathan Woods, Aaron Giles, Wilbert Pol, hap
3 /*
4 ** File: tms9928a.c -- software implementation of the Texas Instruments
5 ** TMS9918(A), TMS9928(A) and TMS9929(A), used by the Coleco, MSX and
6 ** TI99/4(A).
7 **
8 ** All undocumented features as described in the following file
9 ** should be emulated.
10 **
11 ** http://bifi.msxnet.org/msxnet/tech/tms9918a.txt
12 **
13 ** By Sean Young 1999 (sean@msxnet.org).
14 ** Based on code by Mike Balfour.
15 ** Improved over the years by MESS and MAME teams.
16 **
17 ** Todo:
18 ** - External VDP input and sync (pin 34/35 on 9918A)
19 ** - Updates during mid-scanline, probably only used in some MSX1 demos
20 ** - Colours are incorrect. [fixed by R Nabet ?]
21 ** - Sprites 8-31 are ghosted/cloned in mode 3 when using less than
22 ** three pattern tables. Exact behaviour is not known.
23 ** - Address scrambling when setting TMS99xxA to 4K (not on TMS91xx)
24 */
25
26 #include "emu.h"
27 #include "tms9928a.h"
28
29
30 DEFINE_DEVICE_TYPE(TMS9928A, tms9928a_device, "tms9928a", "TMS9928A VDP")
31 DEFINE_DEVICE_TYPE(TMS9918, tms9918_device, "tms9918", "TMS9918 VDP")
32 DEFINE_DEVICE_TYPE(TMS9918A, tms9918a_device, "tms9918a", "TMS9918A VDP")
33 DEFINE_DEVICE_TYPE(TMS9118, tms9118_device, "tms9118", "TMS9118 VDP")
34 DEFINE_DEVICE_TYPE(TMS9128, tms9128_device, "tms9128", "TMS9128 VDP")
35 DEFINE_DEVICE_TYPE(TMS9929, tms9929_device, "tms9929", "TMS9929 VDP")
36 DEFINE_DEVICE_TYPE(TMS9929A, tms9929a_device, "tms9929a", "TMS9929A VDP")
37 DEFINE_DEVICE_TYPE(TMS9129, tms9129_device, "tms9129", "TMS9129 VDP")
38 DEFINE_DEVICE_TYPE(EFO90501, efo90501_device, "efo90501", "EFO90501 VDP")
39
40 // ======= Debugging =========
41
42 // Log register accesses
43 #define TRACE_REG 0
44
45 // Log mode settings
46 #define TRACE_MODE 0
47
48 // ===========================
49
50 /*
51 The TMS9928 has an own address space.
52 */
memmap(address_map & map)53 void tms9928a_device::memmap(address_map &map)
54 {
55 if (!has_configured_map(0))
56 map(0x0000, 0x3fff).ram();
57 }
58
tms9928a_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,uint16_t horz_total,bool is_50hz,bool is_reva,bool is_99)59 tms9928a_device::tms9928a_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, uint16_t horz_total, bool is_50hz, bool is_reva, bool is_99)
60 : device_t(mconfig, type, tag, owner, clock)
61 , device_memory_interface(mconfig, *this)
62 , device_palette_interface(mconfig, *this)
63 , device_video_interface(mconfig, *this)
64 , m_vram_size(0)
65 , m_out_int_line_cb(*this)
66 , m_out_gromclk_cb(*this)
67 , m_total_horz(horz_total)
68 , m_50hz(is_50hz)
69 , m_reva(is_reva)
70 , m_99(is_99)
71 , m_space_config("vram", ENDIANNESS_BIG, 8, 14, 0, address_map_constructor(FUNC(tms9928a_device::memmap), this))
72 {
73 }
74
device_config_complete()75 void tms9928a_device::device_config_complete()
76 {
77 if (!has_screen())
78 return;
79
80 if (!screen().has_screen_update())
81 screen().set_screen_update(*this, FUNC(tms9928a_device::screen_update));
82
83 if (!screen().refresh_attoseconds())
84 {
85 if (m_50hz)
86 screen().set_raw(clock() / 2, m_total_horz, HORZ_DISPLAY_START - 12, HORZ_DISPLAY_START + 256 + 12,
87 TOTAL_VERT_PAL, VERT_DISPLAY_START_PAL - 12, VERT_DISPLAY_START_PAL + 192 + 12);
88 else
89 screen().set_raw(clock() / 2, m_total_horz, HORZ_DISPLAY_START - 12, HORZ_DISPLAY_START + 256 + 12,
90 TOTAL_VERT_NTSC, VERT_DISPLAY_START_NTSC - 12, VERT_DISPLAY_START_NTSC + 192 + 12);
91 }
92 }
93
94
tms9928a_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)95 tms9928a_device::tms9928a_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
96 : tms9928a_device(mconfig, TMS9928A, tag, owner, clock, 342, false, true, true)
97 {
98 }
99
tms9129_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)100 tms9129_device::tms9129_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
101 : tms9928a_device(mconfig, TMS9129, tag, owner, clock, 342, true, true, false)
102 {
103 }
104
tms9918_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)105 tms9918_device::tms9918_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
106 : tms9928a_device(mconfig, TMS9918, tag, owner, clock, 342, false, false, true)
107 {
108 }
109
tms9918a_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)110 tms9918a_device::tms9918a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
111 : tms9928a_device(mconfig, TMS9918A, tag, owner, clock, 342, false, true, true)
112 {
113 }
114
tms9118_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)115 tms9118_device::tms9118_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
116 : tms9928a_device(mconfig, TMS9118, tag, owner, clock, 342, false, true, false)
117 {
118 }
119
tms9128_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)120 tms9128_device::tms9128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
121 : tms9928a_device(mconfig, TMS9128, tag, owner, clock, 342, false, true, false)
122 {
123 }
124
tms9929_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)125 tms9929_device::tms9929_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
126 : tms9928a_device(mconfig, TMS9929, tag, owner, clock, 342, true, false, true)
127 {
128 }
129
tms9929a_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)130 tms9929a_device::tms9929a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
131 : tms9928a_device(mconfig, TMS9929A, tag, owner, clock, 342, true, true, true)
132 {
133 }
134
efo90501_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)135 efo90501_device::efo90501_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
136 : tms9928a_device(mconfig, EFO90501, tag, owner, clock, 346, true, true, true)
137 {
138 }
139
memory_space_config() const140 device_memory_interface::space_config_vector tms9928a_device::memory_space_config() const
141 {
142 return space_config_vector {
143 std::make_pair(AS_DATA, &m_space_config)
144 };
145 }
146
read(offs_t offset)147 uint8_t tms9928a_device::read(offs_t offset)
148 {
149 uint8_t value = 0;
150
151 if ((offset & 1) == 0)
152 value = vram_read();
153 else
154 value = register_read();
155
156 return value;
157 }
158
write(offs_t offset,uint8_t data)159 void tms9928a_device::write(offs_t offset, uint8_t data)
160 {
161 if ((offset & 1) == 0)
162 vram_write(data);
163 else
164 register_write(data);
165 }
166
vram_read()167 u8 tms9928a_device::vram_read()
168 {
169 uint8_t data = m_ReadAhead;
170
171 // prevent debugger from changing the address base
172 if (machine().side_effects_disabled()) return data;
173
174 m_ReadAhead = m_vram_space->read_byte(m_Addr);
175 m_Addr = (m_Addr + 1) & (m_vram_size - 1);
176 m_latch = 0;
177
178 return data;
179 }
180
vram_write(u8 data)181 void tms9928a_device::vram_write(u8 data)
182 {
183 m_vram_space->write_byte(m_Addr, data);
184
185 // prevent debugger from changing the address base
186 if (!machine().side_effects_disabled())
187 {
188 m_Addr = (m_Addr + 1) & (m_vram_size - 1);
189 m_ReadAhead = data;
190 m_latch = 0;
191 }
192 }
193
register_read()194 u8 tms9928a_device::register_read()
195 {
196 uint8_t data = m_StatusReg;
197
198 // prevent debugger from changing the internal state
199 if (machine().side_effects_disabled()) return data;
200
201 m_StatusReg = m_FifthSprite;
202 check_interrupt();
203 m_latch = 0;
204
205 return data;
206 }
207
check_interrupt()208 void tms9928a_device::check_interrupt()
209 {
210 // trigger if vblank and interrupt-enable bits are set
211 uint8_t b = (m_StatusReg & 0x80 && m_Regs[1] & 0x20) ? 1 : 0;
212
213 if (b != m_INT)
214 {
215 m_INT = b;
216 if ( !m_out_int_line_cb.isnull() )
217 m_out_int_line_cb( m_INT );
218 }
219 }
220
221
update_backdrop()222 void tms9928a_device::update_backdrop()
223 {
224 // update backdrop colour to transparent if EXTVID bit is set
225 if ((m_Regs[7] & 15) == 0)
226 set_pen_color(0, rgb_t(m_Regs[0] & 1 ? 0 : 255,0,0,0));
227 }
228
229
update_table_masks()230 void tms9928a_device::update_table_masks()
231 {
232 m_colourmask = ( (m_Regs[3] & 0x7f) << 3 ) | 7;
233
234 // on 91xx family, the colour table mask doesn't affect the pattern table mask
235 m_patternmask = ( (m_Regs[4] & 3) << 8 ) | ( m_99 ? (m_colourmask & 0xff) : 0xff );
236 }
237
238
change_register(uint8_t reg,uint8_t val)239 void tms9928a_device::change_register(uint8_t reg, uint8_t val)
240 {
241 static const uint8_t Mask[8] =
242 { 0x03, 0xfb, 0x0f, 0xff, 0x07, 0x7f, 0x07, 0xff };
243 static const char *const modes[] =
244 {
245 "Mode 0 (GRAPHIC 1)", "Mode 1 (TEXT 1)", "Mode 2 (GRAPHIC 2)",
246 "Mode 1+2 (TEXT 1 variation)", "Mode 3 (MULTICOLOR)",
247 "Mode 1+3 (BOGUS)", "Mode 2+3 (MULTICOLOR variation)",
248 "Mode 1+2+3 (BOGUS)"
249 };
250
251 uint8_t prev = m_Regs[reg];
252 val &= Mask[reg];
253 m_Regs[reg] = val;
254
255 if (TRACE_REG) logerror("TMS9928A('%s'): Reg %d = %02xh\n", tag(), reg, (int)val);
256
257 switch (reg)
258 {
259 case 0:
260 /* re-calculate masks and pattern generator & colour */
261 if (val & 2)
262 {
263 m_colour = ((m_Regs[3] & 0x80) * 64) & (m_vram_size - 1);
264 m_pattern = ((m_Regs[4] & 4) * 2048) & (m_vram_size - 1);
265 update_table_masks();
266 }
267 else
268 {
269 m_colour = (m_Regs[3] * 64) & (m_vram_size - 1);
270 m_pattern = (m_Regs[4] * 2048) & (m_vram_size - 1);
271 }
272 m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1));
273 if ((val ^ prev) & 1)
274 update_backdrop();
275 if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]);
276 break;
277 case 1:
278 check_interrupt();
279 m_mode = ( (m_reva ? (m_Regs[0] & 2) : 0) | ((m_Regs[1] & 0x10)>>4) | ((m_Regs[1] & 8)>>1));
280 if (TRACE_MODE) logerror("TMS9928A('%s'): %s\n", tag(), modes[m_mode]);
281 break;
282 case 2:
283 m_nametbl = (val * 1024) & (m_vram_size - 1);
284 break;
285 case 3:
286 if (m_Regs[0] & 2)
287 {
288 m_colour = ((val & 0x80) * 64) & (m_vram_size - 1);
289 update_table_masks();
290 }
291 else
292 {
293 m_colour = (val * 64) & (m_vram_size - 1);
294 }
295 break;
296 case 4:
297 if (m_Regs[0] & 2)
298 {
299 m_pattern = ((val & 4) * 2048) & (m_vram_size - 1);
300 update_table_masks();
301 }
302 else
303 {
304 m_pattern = (val * 2048) & (m_vram_size - 1);
305 }
306 break;
307 case 5:
308 m_spriteattribute = (val * 128) & (m_vram_size - 1);
309 break;
310 case 6:
311 m_spritepattern = (val * 2048) & (m_vram_size - 1);
312 break;
313 case 7:
314 if ((val ^ prev) & 15)
315 update_backdrop();
316 break;
317 }
318 }
319
320
register_write(u8 data)321 void tms9928a_device::register_write(u8 data)
322 {
323 // prevent debugger from changing the internal state
324 if (machine().side_effects_disabled()) return;
325
326 if (m_latch)
327 {
328 /* set high part of read/write address */
329 m_Addr = ((data << 8) | (m_Addr & 0xff)) & (m_vram_size - 1);
330
331 if (data & 0x80)
332 {
333 /* register write */
334 change_register (data & 7, m_Addr & 0xff);
335 }
336 else
337 {
338 if ( !(data & 0x40) )
339 {
340 /* read ahead */
341 vram_read();
342 }
343 }
344 m_latch = 0;
345 }
346 else
347 {
348 /* set low part of read/write address */
349 m_Addr = ((m_Addr & 0xff00) | data) & (m_vram_size - 1);
350 m_latch = 1;
351 }
352 }
353
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)354 void tms9928a_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
355 {
356 // Handle GROM clock if present
357 if (id==GROMCLK)
358 {
359 // Pulse it
360 m_out_gromclk_cb(ASSERT_LINE);
361 m_out_gromclk_cb(CLEAR_LINE);
362 return;
363 }
364
365 int raw_vpos = screen().vpos();
366 int vpos = raw_vpos * m_vertical_size / screen().height();
367 uint16_t BackColour = m_Regs[7] & 15;
368 uint32_t *p = &m_tmpbmp.pix(vpos);
369
370 int y = vpos - m_top_border;
371
372 if ( y < 0 || y >= 192 || ! (m_Regs[1] & 0x40) )
373 {
374 /* Draw backdrop colour */
375 for ( int i = 0; i < m_total_horz; i++ )
376 p[i] = pen(BackColour);
377
378 /* vblank is set at the last cycle of the first inactive line */
379 if ( y == 193 )
380 {
381 m_StatusReg |= 0x80;
382 check_interrupt();
383 }
384 }
385 else
386 {
387 /* Draw regular line */
388
389 /* Left border */
390 for ( int i = 0; i < HORZ_DISPLAY_START; i++ )
391 p[i] = pen(BackColour);
392
393 /* Active display */
394
395 switch( m_mode )
396 {
397 case 0: /* MODE 0 */
398 // if (vpos==100 ) popmessage("TMS9928A MODE 0");
399 {
400 uint16_t addr = m_nametbl + ( ( y & 0xF8 ) << 2 );
401
402 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ )
403 {
404 uint8_t charcode = m_vram_space->read_byte( addr );
405 uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
406 uint8_t colour = m_vram_space->read_byte( m_colour + ( charcode >> 3 ) );
407 rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour);
408 rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour);
409
410 for ( int i = 0; i < 8; pattern <<= 1, i++ )
411 p[x+i] = ( pattern & 0x80 ) ? fg : bg;
412 }
413 }
414 break;
415
416 case 1: /* MODE 1 */
417 //if (vpos==100 ) popmessage("TMS9928A MODE 1");
418 {
419 uint16_t addr = m_nametbl + ( ( y >> 3 ) * 40 );
420 rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour);
421 rgb_t bg = pen(BackColour);
422
423 /* Extra 6 pixels left border */
424 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ )
425 p[x] = bg;
426
427 for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6, addr++ )
428 {
429 uint16_t charcode = m_vram_space->read_byte( addr );
430 uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
431
432 for ( int i = 0; i < 6; pattern <<= 1, i++ )
433 p[x+i] = ( pattern & 0x80 ) ? fg : bg;
434 }
435
436 /* Extra 10 pixels right border */
437 for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ )
438 p[x] = bg;
439 }
440 break;
441
442 case 2: /* MODE 2 */
443 //if (vpos==100 ) popmessage("TMS9928A MODE 2");
444 {
445 uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 );
446
447 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ )
448 {
449 uint16_t charcode = m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 );
450 uint8_t pattern = m_vram_space->read_byte( m_pattern + ( ( charcode & m_patternmask ) << 3 ) + ( y & 7 ) );
451 uint8_t colour = m_vram_space->read_byte( m_colour + ( ( charcode & m_colourmask ) << 3 ) + ( y & 7 ) );
452 rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour);
453 rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour);
454
455 for ( int i = 0; i < 8; pattern <<= 1, i++ )
456 p[x+i] = ( pattern & 0x80 ) ? fg : bg;
457 }
458 }
459 break;
460
461 case 3: /* MODE 1+2 */
462 //if (vpos==100) popmessage("TMS9928A MODE1+2");
463 {
464 uint16_t addr = m_nametbl + ( ( y >> 3 ) * 40 );
465 rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour);
466 rgb_t bg = pen(BackColour);
467
468 /* Extra 6 pixels left border */
469 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ )
470 p[x] = bg;
471
472 for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6, addr++ )
473 {
474 uint16_t charcode = ( m_vram_space->read_byte( addr ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask;
475 uint8_t pattern = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( y & 7 ) );
476
477 for ( int i = 0; i < 6; pattern <<= 1, i++ )
478 p[x+i] = ( pattern & 0x80 ) ? fg : bg;
479 }
480
481 /* Extra 10 pixels right border */
482 for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ )
483 p[x] = bg;
484 }
485 break;
486
487 case 4: /* MODE 3 */
488 //if (vpos==100 ) popmessage("TMS9928A MODE 3");
489 {
490 uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 );
491
492 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ )
493 {
494 uint8_t charcode = m_vram_space->read_byte( addr );
495 uint8_t colour = m_vram_space->read_byte( m_pattern + ( charcode << 3 ) + ( ( y >> 2 ) & 7 ) );
496 rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour);
497 rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour);
498
499 p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
500 p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg;
501 }
502 }
503 break;
504
505 case 5: case 7: /* MODE bogus */
506 //if (vpos==100 ) popmessage("TMS9928A MODE bogus");
507 {
508 rgb_t fg = pen((m_Regs[7] >> 4) ? (m_Regs[7] >> 4) : BackColour);
509 rgb_t bg = pen(BackColour);
510
511 /* Extra 6 pixels left border */
512 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 6; x++ )
513 p[x] = bg;
514
515 for ( int x = HORZ_DISPLAY_START + 6; x < HORZ_DISPLAY_START + 246; x+= 6 )
516 {
517 p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
518 p[x+4] = p[x+5] = bg;
519 }
520
521 /* Extra 10 pixels right border */
522 for ( int x = HORZ_DISPLAY_START + 246; x < HORZ_DISPLAY_START + 256; x++ )
523 p[x] = bg;
524 }
525 break;
526
527 case 6: /* MODE 2+3 */
528 //if (vpos==100 ) popmessage("TMS9928A MODE 2+3");
529 {
530 uint16_t addr = m_nametbl + ( ( y >> 3 ) * 32 );
531
532 for ( int x = HORZ_DISPLAY_START; x < HORZ_DISPLAY_START + 256; x+= 8, addr++ )
533 {
534 uint8_t charcode = m_vram_space->read_byte( addr );
535 uint8_t colour = m_vram_space->read_byte( m_pattern + ( ( ( charcode + ( ( y >> 2 ) & 7 ) + ( ( y >> 6 ) << 8 ) ) & m_patternmask ) << 3 ) );
536 rgb_t fg = pen((colour >> 4) ? (colour >> 4) : BackColour);
537 rgb_t bg = pen((colour & 15) ? (colour & 15) : BackColour);
538
539 p[x+0] = p[x+1] = p[x+2] = p[x+3] = fg;
540 p[x+4] = p[x+5] = p[x+6] = p[x+7] = bg;
541 }
542 }
543 break;
544 }
545
546 /* Draw sprites */
547 if ( ( m_Regs[1] & 0x50 ) != 0x40 )
548 {
549 /* sprites are disabled */
550 m_FifthSprite = 31;
551 }
552 else
553 {
554 uint8_t sprite_size = ( m_Regs[1] & 0x02 ) ? 16 : 8;
555 uint8_t sprite_mag = m_Regs[1] & 0x01;
556 uint8_t sprite_height = sprite_size * ( sprite_mag + 1 );
557 uint8_t spr_drawn[32+256+32] = { 0 };
558 uint8_t num_sprites = 0;
559 bool fifth_encountered = false;
560
561 for ( uint16_t sprattr = 0; sprattr < 128; sprattr += 4 )
562 {
563 int spr_y = m_vram_space->read_byte( m_spriteattribute + sprattr + 0 );
564
565 m_FifthSprite = sprattr / 4;
566
567 /* Stop processing sprites */
568 if ( spr_y == 208 )
569 break;
570
571 if ( spr_y > 0xE0 )
572 spr_y -= 256;
573
574 /* vert pos 255 is displayed on the first line of the screen */
575 spr_y++;
576
577 /* is sprite enabled on this line? */
578 if ( spr_y <= y && y < spr_y + sprite_height )
579 {
580 int spr_x = m_vram_space->read_byte( m_spriteattribute + sprattr + 1 );
581 uint8_t sprcode = m_vram_space->read_byte( m_spriteattribute + sprattr + 2 );
582 uint8_t sprcol = m_vram_space->read_byte( m_spriteattribute + sprattr + 3 );
583 uint16_t pataddr = m_spritepattern + ( ( sprite_size == 16 ) ? sprcode & ~0x03 : sprcode ) * 8;
584
585 num_sprites++;
586
587 /* Fifth sprite encountered? */
588 if ( num_sprites == 5 )
589 {
590 fifth_encountered = true;
591 break;
592 }
593
594 if ( sprite_mag )
595 pataddr += ( ( ( y - spr_y ) & 0x1F ) >> 1 );
596 else
597 pataddr += ( ( y - spr_y ) & 0x0F );
598
599 uint8_t pattern = m_vram_space->read_byte( pataddr );
600
601 if ( sprcol & 0x80 )
602 spr_x -= 32;
603
604 sprcol &= 0x0f;
605
606 for ( int s = 0; s < sprite_size; s += 8 )
607 {
608 for ( int i = 0; i < 8; pattern <<= 1, i++ )
609 {
610 int colission_index = spr_x + ( sprite_mag ? i * 2 : i ) + 32;
611
612 for ( int z = 0; z <= sprite_mag; colission_index++, z++ )
613 {
614 /* Check if pixel should be drawn */
615 if ( pattern & 0x80 )
616 {
617 if ( colission_index >= 32 && colission_index < 32 + 256 )
618 {
619 /* Check for colission */
620 if ( spr_drawn[ colission_index ] )
621 m_StatusReg |= 0x20;
622 spr_drawn[ colission_index ] |= 0x01;
623
624 if ( sprcol )
625 {
626 /* Has another sprite already drawn here? */
627 if ( ! ( spr_drawn[ colission_index ] & 0x02 ) )
628 {
629 spr_drawn[ colission_index ] |= 0x02;
630 p[ HORZ_DISPLAY_START + colission_index - 32 ] = pen(sprcol);
631 }
632 }
633 }
634 }
635 }
636 }
637
638 pattern = m_vram_space->read_byte( pataddr + 16 );
639 spr_x += sprite_mag ? 16 : 8;
640 }
641 }
642 }
643
644 /* Update sprite overflow bits */
645 if (~m_StatusReg & 0x40)
646 {
647 m_StatusReg = (m_StatusReg & 0xe0) | m_FifthSprite;
648 if (fifth_encountered && ~m_StatusReg & 0x80)
649 m_StatusReg |= 0x40;
650 }
651 }
652
653 /* Right border */
654 for ( int i = HORZ_DISPLAY_START + 256; i < m_total_horz; i++ )
655 p[i] = pen(BackColour);
656 }
657
658 /* Schedule next callback */
659 m_line_timer->adjust( screen().time_until_pos( ( raw_vpos + 1 ) % screen().height() , HORZ_DISPLAY_START ) );
660 }
661
662
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)663 uint32_t tms9928a_device::screen_update( screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect )
664 {
665 copybitmap( bitmap, m_tmpbmp, 0, 0, 0, 0, cliprect );
666 return 0;
667 }
668
set_palette()669 void tms9928a_device::set_palette()
670 {
671 /*
672 New palette (R. Nabet).
673
674 First 3 columns from TI datasheet (in volts).
675 Next 3 columns based on formula :
676 Y = .299*R + .587*G + .114*B (NTSC)
677 (the coefficients are likely to be slightly different with PAL, but who cares ?)
678 I assumed the "zero" for R-Y and B-Y was 0.47V.
679 Last 3 coeffs are the 8-bit values.
680
681 Color Y R-Y B-Y R G B R G B
682 0 Transparent
683 1 Black 0.00 0.47 0.47 0.00 0.00 0.00 0 0 0
684 2 Medium green 0.53 0.07 0.20 0.13 0.79 0.26 33 200 66
685 3 Light green 0.67 0.17 0.27 0.37 0.86 0.47 94 220 120
686 4 Dark blue 0.40 0.40 1.00 0.33 0.33 0.93 84 85 237
687 5 Light blue 0.53 0.43 0.93 0.49 0.46 0.99 125 118 252
688 6 Dark red 0.47 0.83 0.30 0.83 0.32 0.30 212 82 77
689 7 Cyan 0.73 0.00 0.70 0.26 0.92 0.96 66 235 245
690 8 Medium red 0.53 0.93 0.27 0.99 0.33 0.33 252 85 84
691 9 Light red 0.67 0.93 0.27 1.13(!) 0.47 0.47 255 121 120
692 A Dark yellow 0.73 0.57 0.07 0.83 0.76 0.33 212 193 84
693 B Light yellow 0.80 0.57 0.17 0.90 0.81 0.50 230 206 128
694 C Dark green 0.47 0.13 0.23 0.13 0.69 0.23 33 176 59
695 D Magenta 0.53 0.73 0.67 0.79 0.36 0.73 201 91 186
696 E Gray 0.80 0.47 0.47 0.80 0.80 0.80 204 204 204
697 F White 1.00 0.47 0.47 1.00 1.00 1.00 255 255 255
698 */
699 static const rgb_t tms9928a_palette[PALETTE_SIZE] =
700 {
701 rgb_t::black(),
702 rgb_t::black(),
703 rgb_t(33, 200, 66),
704 rgb_t(94, 220, 120),
705 rgb_t(84, 85, 237),
706 rgb_t(125, 118, 252),
707 rgb_t(212, 82, 77),
708 rgb_t(66, 235, 245),
709 rgb_t(252, 85, 84),
710 rgb_t(255, 121, 120),
711 rgb_t(212, 193, 84),
712 rgb_t(230, 206, 128),
713 rgb_t(33, 176, 59),
714 rgb_t(201, 91, 186),
715 rgb_t(204, 204, 204),
716 rgb_t::white()
717 };
718
719 /* copy default palette into working palette */
720 for (int i = 0; i < PALETTE_SIZE; i++)
721 {
722 set_pen_color(i, tms9928a_palette[i]);
723 }
724 }
725
device_start()726 void tms9928a_device::device_start()
727 {
728 m_top_border = m_50hz ? VERT_DISPLAY_START_PAL : VERT_DISPLAY_START_NTSC;
729 m_vertical_size = m_50hz ? TOTAL_VERT_PAL : TOTAL_VERT_NTSC;
730
731 m_out_int_line_cb.resolve();
732 m_out_gromclk_cb.resolve();
733
734 // Video RAM is allocated as an own address space
735 m_vram_space = &space(AS_DATA);
736
737 /* back bitmap */
738 m_tmpbmp.allocate(m_total_horz, TOTAL_VERT_PAL);
739
740 m_line_timer = timer_alloc(TIMER_LINE);
741 m_gromclk_timer = timer_alloc(GROMCLK);
742
743 set_palette();
744
745 save_item(NAME(m_Regs[0]));
746 save_item(NAME(m_Regs[1]));
747 save_item(NAME(m_Regs[2]));
748 save_item(NAME(m_Regs[3]));
749 save_item(NAME(m_Regs[4]));
750 save_item(NAME(m_Regs[5]));
751 save_item(NAME(m_Regs[6]));
752 save_item(NAME(m_Regs[7]));
753 save_item(NAME(m_StatusReg));
754 save_item(NAME(m_FifthSprite));
755 save_item(NAME(m_ReadAhead));
756 save_item(NAME(m_latch));
757 save_item(NAME(m_Addr));
758 save_item(NAME(m_INT));
759 // save_pointer(NAME(m_vMem), m_vram_size);
760 save_item(NAME(m_colour));
761 save_item(NAME(m_colourmask));
762 save_item(NAME(m_pattern));
763 save_item(NAME(m_patternmask));
764 save_item(NAME(m_nametbl));
765 save_item(NAME(m_spriteattribute));
766 save_item(NAME(m_spritepattern));
767 save_item(NAME(m_mode));
768 }
769
770
device_reset()771 void tms9928a_device::device_reset()
772 {
773 for (auto & elem : m_Regs)
774 elem = 0;
775
776 m_StatusReg = 0;
777 m_FifthSprite = 31;
778 m_nametbl = 0;
779 m_pattern = 0;
780 m_colour = 0;
781 m_spritepattern = 0;
782 m_spriteattribute = 0;
783 m_colourmask = 0x3fff;
784 m_patternmask = 0x3fff;
785 m_Addr = 0;
786 m_ReadAhead = 0;
787 m_INT = 0;
788 m_latch = 0;
789 m_mode = 0;
790
791 m_line_timer->adjust( screen().time_until_pos( 0, HORZ_DISPLAY_START ) );
792
793 // TODO: Check clock freq settings in all drivers
794 if (!m_out_gromclk_cb.isnull() && m_99)
795 m_gromclk_timer->adjust(attotime::zero, 0, clocks_to_attotime(24));
796 }
797