1 // license:BSD-3-Clause
2 // copyright-holders:R. Belmont
3 /***************************************************************************
4 
5   SuperMac Spectrum PDQ video card
6 
7   Accelerated only in 256 color mode.  Accleration is not yet emulated
8   properly (pattern fill works but has glitches).  Use in B&W or 16 colors
9   for full functionality right now.
10 
11   blitter info:
12 
13   ctrl 1 = ?
14   ctrl 2 = low 3 bits of Y position in bits 3-5, low 3 bits of X position in bits 0-2
15   ctrl 3 = width
16   ctrl 4 = height
17   ctrl 5 = ?
18   ctrl 6 = VRAM offset * 4
19   ctrl 7 = command/execute (00000002 for pattern fill, 00000100 for copy)
20 
21   Busy flag at Fs800000 (bit 8)
22 
23   There is 256 bytes of pattern RAM arranged as 32 pixels horizontally by 8
24   vertically.
25 
26 ***************************************************************************/
27 
28 #include "emu.h"
29 #include "nubus_specpdq.h"
30 #include "screen.h"
31 
32 //#define VERBOSE 1
33 #include "logmacro.h"
34 
35 
36 #define SPECPDQ_SCREEN_NAME "specpdq_screen"
37 #define SPECPDQ_ROM_REGION  "specpdq_rom"
38 
39 #define VRAM_SIZE   (0x400000)
40 
41 ROM_START( specpdq )
42 	ROM_REGION(0x10000, SPECPDQ_ROM_REGION, 0)
43 	ROM_LOAD( "specpdq.bin",  0x000000, 0x010000, CRC(82a35f78) SHA1(9511c2df47140f4279196d3b8836b53429879dd9) )
44 ROM_END
45 
46 //**************************************************************************
47 //  GLOBAL VARIABLES
48 //**************************************************************************
49 
50 DEFINE_DEVICE_TYPE(NUBUS_SPECPDQ, nubus_specpdq_device, "nb_spdq", "SuperMac Spectrum PDQ video card")
51 
52 
53 //-------------------------------------------------
54 //  device_add_mconfig - add device configuration
55 //-------------------------------------------------
56 
device_add_mconfig(machine_config & config)57 void nubus_specpdq_device::device_add_mconfig(machine_config &config)
58 {
59 	screen_device &screen(SCREEN(config, SPECPDQ_SCREEN_NAME, SCREEN_TYPE_RASTER));
60 	screen.set_screen_update(FUNC(nubus_specpdq_device::screen_update));
61 	screen.set_raw(25175000, 800, 0, 640, 525, 0, 480);
62 	screen.set_size(1280, 1024);
63 	screen.set_visarea(0, 1152-1, 0, 844-1);
64 
65 	PALETTE(config, m_palette).set_entries(256);
66 }
67 
68 //-------------------------------------------------
69 //  rom_region - device-specific ROM region
70 //-------------------------------------------------
71 
device_rom_region() const72 const tiny_rom_entry *nubus_specpdq_device::device_rom_region() const
73 {
74 	return ROM_NAME( specpdq );
75 }
76 
77 //**************************************************************************
78 //  LIVE DEVICE
79 //**************************************************************************
80 
81 //-------------------------------------------------
82 //  nubus_specpdq_device - constructor
83 //-------------------------------------------------
84 
nubus_specpdq_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)85 nubus_specpdq_device::nubus_specpdq_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
86 	nubus_specpdq_device(mconfig, NUBUS_SPECPDQ, tag, owner, clock)
87 {
88 }
89 
nubus_specpdq_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)90 nubus_specpdq_device::nubus_specpdq_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
91 	device_t(mconfig, type, tag, owner, clock),
92 	device_video_interface(mconfig, *this),
93 	device_nubus_card_interface(mconfig, *this),
94 	m_vram32(nullptr), m_mode(0), m_vbl_disable(0), m_count(0), m_clutoffs(0), m_timer(nullptr),
95 	m_width(0), m_height(0), m_patofsx(0), m_patofsy(0), m_vram_addr(0), m_vram_src(0),
96 	m_palette(*this, "palette")
97 {
98 	set_screen(*this, SPECPDQ_SCREEN_NAME);
99 }
100 
101 //-------------------------------------------------
102 //  device_start - device-specific startup
103 //-------------------------------------------------
104 
device_start()105 void nubus_specpdq_device::device_start()
106 {
107 	uint32_t slotspace;
108 
109 	install_declaration_rom(this, SPECPDQ_ROM_REGION);
110 
111 	slotspace = get_slotspace();
112 
113 //  logerror("[specpdq %p] slotspace = %x\n", this, slotspace);
114 
115 	m_vram.resize(VRAM_SIZE);
116 	m_vram32 = (uint32_t *)&m_vram[0];
117 	nubus().install_device(slotspace, slotspace+VRAM_SIZE-1, read32s_delegate(*this, FUNC(nubus_specpdq_device::vram_r)), write32s_delegate(*this, FUNC(nubus_specpdq_device::vram_w)));
118 	nubus().install_device(slotspace+0x400000, slotspace+0xfbffff, read32s_delegate(*this, FUNC(nubus_specpdq_device::specpdq_r)), write32s_delegate(*this, FUNC(nubus_specpdq_device::specpdq_w)));
119 
120 	m_timer = timer_alloc(0, nullptr);
121 	m_timer->adjust(screen().time_until_pos(843, 0), 0);
122 }
123 
124 //-------------------------------------------------
125 //  device_reset - device-specific reset
126 //-------------------------------------------------
127 
device_reset()128 void nubus_specpdq_device::device_reset()
129 {
130 	m_count = 0;
131 	m_clutoffs = 0;
132 	m_vbl_disable = 1;
133 	m_mode = 0;
134 	memset(&m_vram[0], 0, VRAM_SIZE);
135 	memset(m_palette_val, 0, sizeof(m_palette_val));
136 
137 	m_palette_val[0] = rgb_t(255, 255, 255);
138 	m_palette_val[0x80] = rgb_t(0, 0, 0);
139 }
140 
141 
device_timer(emu_timer & timer,device_timer_id tid,int param,void * ptr)142 void nubus_specpdq_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
143 {
144 	if (!m_vbl_disable)
145 	{
146 		raise_slot_irq();
147 	}
148 
149 	m_timer->adjust(screen().time_until_pos(843, 0), 0);
150 }
151 
152 /***************************************************************************
153 
154   Spectrum 24 PDQ section
155 
156 ***************************************************************************/
157 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)158 uint32_t nubus_specpdq_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
159 {
160 	// first time?  kick off the VBL timer
161 	uint8_t const *const vram = &m_vram[0x9000];
162 
163 	switch (m_mode)
164 	{
165 		case 0: // 1 bpp
166 			for (int y = 0; y < 844; y++)
167 			{
168 				uint32_t *scanline = &bitmap.pix(y);
169 				for (int x = 0; x < 1152/8; x++)
170 				{
171 					uint8_t const pixels = vram[(y * 512) + (BYTE4_XOR_BE(x))];
172 
173 					*scanline++ = m_palette_val[(pixels&0x80)];
174 					*scanline++ = m_palette_val[((pixels<<1)&0x80)];
175 					*scanline++ = m_palette_val[((pixels<<2)&0x80)];
176 					*scanline++ = m_palette_val[((pixels<<3)&0x80)];
177 					*scanline++ = m_palette_val[((pixels<<4)&0x80)];
178 					*scanline++ = m_palette_val[((pixels<<5)&0x80)];
179 					*scanline++ = m_palette_val[((pixels<<6)&0x80)];
180 					*scanline++ = m_palette_val[((pixels<<7)&0x80)];
181 				}
182 			}
183 			break;
184 
185 		case 1: // 2 bpp
186 			for (int y = 0; y < 844; y++)
187 			{
188 				uint32_t *scanline = &bitmap.pix(y);
189 				for (int x = 0; x < 1152/4; x++)
190 				{
191 					uint8_t const pixels = vram[(y * 512) + (BYTE4_XOR_BE(x))];
192 
193 					*scanline++ = m_palette_val[(pixels&0xc0)];
194 					*scanline++ = m_palette_val[((pixels<<2)&0xc0)];
195 					*scanline++ = m_palette_val[((pixels<<4)&0xc0)];
196 					*scanline++ = m_palette_val[((pixels<<6)&0xc0)];
197 				}
198 			}
199 			break;
200 
201 		case 2: // 4 bpp
202 			for (int y = 0; y < 844; y++)
203 			{
204 				uint32_t *scanline = &bitmap.pix(y);
205 				for (int x = 0; x < 1152/2; x++)
206 				{
207 					uint8_t const pixels = vram[(y * 1024) + (BYTE4_XOR_BE(x))];
208 
209 					*scanline++ = m_palette_val[(pixels&0xf0)];
210 					*scanline++ = m_palette_val[((pixels<<4)&0xf0)];
211 				}
212 			}
213 			break;
214 
215 		case 3: // 8 bpp
216 			for (int y = 0; y < 844; y++)
217 			{
218 				uint32_t *scanline = &bitmap.pix(y);
219 				for (int x = 0; x < 1152; x++)
220 				{
221 					uint8_t const pixels = vram[(y * 1152) + (BYTE4_XOR_BE(x))];
222 					*scanline++ = m_palette_val[pixels];
223 				}
224 			}
225 			break;
226 
227 		default:
228 			fatalerror("specpdq: unknown video mode %d\n", m_mode);
229 	}
230 	return 0;
231 }
232 
specpdq_w(offs_t offset,uint32_t data,uint32_t mem_mask)233 void nubus_specpdq_device::specpdq_w(offs_t offset, uint32_t data, uint32_t mem_mask)
234 {
235 	if (offset >= 0xc0000 && offset < 0x100000)
236 	{
237 		COMBINE_DATA(&m_7xxxxx_regs[offset-0xc0000]);
238 	}
239 
240 	switch (offset)
241 	{
242 		case 0xc0054:   // mode 1
243 			LOG("%x to mode1\n", data);
244 			break;
245 
246 		case 0xc005c:   // interrupt control
247 			if (!(data & 0x8000))
248 			{
249 				m_vbl_disable = 1;
250 			}
251 			else
252 			{
253 				m_vbl_disable = 0;
254 				lower_slot_irq();
255 			}
256 			break;
257 
258 		case 0xc005e:   // not sure, interrupt related?
259 			break;
260 
261 		case 0xc007a:
262 			LOG("%x to mode2\n", data);
263 
264 			switch (data)
265 			{
266 				case 0xff7fffff:
267 					m_mode = 0;
268 					break;
269 
270 				case 0xfeffffff:
271 					m_mode = 2;
272 					break;
273 
274 				case 0xfedfffff:
275 					m_mode = 3;
276 					break;
277 			}
278 
279 			LOG("m_mode = %d\n", m_mode);
280 			break;
281 
282 		case 0x120000:  // DAC address
283 			LOG("%08x to DAC control %s\n", data,machine().describe_context());
284 			m_clutoffs = ((data>>8)&0xff)^0xff;
285 			break;
286 
287 		case 0x120001:  // DAC data
288 			m_colors[m_count++] = ((data>>8)&0xff)^0xff;
289 
290 			if (m_count == 3)
291 			{
292 				LOG("RAMDAC: color %d = %02x %02x %02x %s\n", m_clutoffs, m_colors[0], m_colors[1], m_colors[2], machine().describe_context());
293 				m_palette->set_pen_color(m_clutoffs, rgb_t(m_colors[0], m_colors[1], m_colors[2]));
294 				m_palette_val[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
295 				m_clutoffs++;
296 				if (m_clutoffs > 255)
297 				{
298 					m_clutoffs = 0;
299 				}
300 				m_count = 0;
301 			}
302 			break;
303 
304 		// blitter texture? pattern?  256 pixels worth at 8bpp
305 		case 0x181000:
306 		case 0x181001:
307 		case 0x181002:
308 		case 0x181003:
309 		case 0x181004:
310 		case 0x181005:
311 		case 0x181006:
312 		case 0x181007:
313 		case 0x181008:
314 		case 0x181009:
315 		case 0x18100a:
316 		case 0x18100b:
317 		case 0x18100c:
318 		case 0x18100d:
319 		case 0x18100e:
320 		case 0x18100f:
321 		case 0x181010:
322 		case 0x181011:
323 		case 0x181012:
324 		case 0x181013:
325 		case 0x181014:
326 		case 0x181015:
327 		case 0x181016:
328 		case 0x181017:
329 		case 0x181018:
330 		case 0x181019:
331 		case 0x18101a:
332 		case 0x18101b:
333 		case 0x18101c:
334 		case 0x18101d:
335 		case 0x18101e:
336 		case 0x18101f:
337 		case 0x181020:
338 		case 0x181021:
339 		case 0x181022:
340 		case 0x181023:
341 		case 0x181024:
342 		case 0x181025:
343 		case 0x181026:
344 		case 0x181027:
345 		case 0x181028:
346 		case 0x181029:
347 		case 0x18102a:
348 		case 0x18102b:
349 		case 0x18102c:
350 		case 0x18102d:
351 		case 0x18102e:
352 		case 0x18102f:
353 		case 0x181030:
354 		case 0x181031:
355 		case 0x181032:
356 		case 0x181033:
357 		case 0x181034:
358 		case 0x181035:
359 		case 0x181036:
360 		case 0x181037:
361 		case 0x181038:
362 		case 0x181039:
363 		case 0x18103a:
364 		case 0x18103b:
365 		case 0x18103c:
366 		case 0x18103d:
367 		case 0x18103e:
368 		case 0x18103f:
369 			if(offset == 0x181000) {
370 				machine().debug_break();
371 				LOG("Pattern %08x @ %x\n", data ^ 0xffffffff, offset);
372 			}
373 			m_fillbytes[((offset&0x3f)*4)] = ((data>>24) & 0xff) ^ 0xff;
374 			m_fillbytes[((offset&0x3f)*4)+1] = ((data>>16) & 0xff) ^ 0xff;
375 			m_fillbytes[((offset&0x3f)*4)+2] = ((data>>8) & 0xff) ^ 0xff;
376 			m_fillbytes[((offset&0x3f)*4)+3] = (data& 0xff) ^ 0xff;
377 			break;
378 
379 		// blitter control
380 		case 0x182006:
381 			LOG("%08x (%d) to blitter ctrl 1 %s rectangle\n", data^0xffffffff, data^0xffffffff, machine().describe_context());
382 			break;
383 
384 		case 0x182008:
385 			LOG("%08x (%d) to blitter ctrl 2 %s rectangle\n", data^0xffffffff, data^0xffffffff, machine().describe_context());
386 			m_patofsx = (data ^ 0xffffffff) & 7;
387 			m_patofsy = ((data ^ 0xffffffff)>>3) & 7;
388 			break;
389 
390 		case 0x18200e:
391 			LOG("%08x (%d) to blitter ctrl 3 %s\n", data^0xffffffff, data^0xffffffff, machine().describe_context());
392 			m_width = data ^ 0xffffffff;
393 			break;
394 
395 		case 0x18200b:
396 			LOG("%08x (%d) to blitter ctrl 4 %s\n", data^0xffffffff, data^0xffffffff, machine().describe_context());
397 			m_height = (data ^ 0xffffffff) & 0xffff;
398 			break;
399 
400 		case 0x18200a:
401 			data ^= 0xffffffff;
402 			LOG("%08x to blitter ctrl 5 %s\n", data, machine().describe_context());
403 			m_vram_src = data>>2;
404 			break;
405 
406 		case 0x182009:
407 			data ^= 0xffffffff;
408 			LOG("%08x to blitter ctrl 6 %s\n", data, machine().describe_context());
409 			m_vram_addr = data>>2;
410 			break;
411 
412 		case 0x182007:
413 			data ^= 0xffffffff;
414 			LOG("%08x to blitter ctrl 7 %s\n", data, machine().describe_context());
415 
416 			// fill rectangle
417 			if (data == 2)
418 			{
419 				int x, y;
420 				uint8_t *vram = &m_vram[m_vram_addr & ~3];
421 
422 				int ddx = m_vram_addr & 3;
423 
424 				LOG("Fill rectangle with %02x %02x %02x %02x, adr %x (%d, %d) width %d height %d delta %d %d\n", m_fillbytes[0], m_fillbytes[1], m_fillbytes[2], m_fillbytes[3], m_vram_addr, m_vram_addr % 1152, m_vram_addr / 1152, m_width, m_height, m_patofsx, m_patofsy);
425 
426 				for (y = 0; y <= m_height; y++)
427 				{
428 					for (x = 0; x <= m_width; x++)
429 					{
430 						vram[(y * 1152)+BYTE4_XOR_BE(x + ddx)] = m_fillbytes[((m_patofsx + x) & 0x1f)+(((m_patofsy + y) & 0x7) << 5)];
431 					}
432 				}
433 			}
434 			else if (data == 0x100)
435 			{
436 				int x, y;
437 				uint8_t *vram = &m_vram[m_vram_addr & ~3];
438 				uint8_t *vramsrc = &m_vram[m_vram_src & ~3];
439 
440 				int sdx = m_vram_src & 3;
441 				int ddx = m_vram_addr & 3;
442 
443 				LOG("Copy rectangle forwards, width %d height %d dst %x (%d, %d) src %x (%d, %d)\n", m_width, m_height, m_vram_addr, m_vram_addr % 1152, m_vram_addr / 1152, m_vram_src, m_vram_src % 1152, m_vram_src / 1152);
444 
445 				for (y = 0; y <= m_height; y++)
446 				{
447 					for (x = 0; x <= m_width; x++)
448 					{
449 						vram[(y * 1152)+BYTE4_XOR_BE(x + ddx)] = vramsrc[(y * 1152)+BYTE4_XOR_BE(x + sdx)];
450 					}
451 				}
452 				(void)vramsrc; (void)sdx;
453 			}
454 			else if (data == 0x101)
455 			{
456 				int x, y;
457 				uint8_t *vram = &m_vram[m_vram_addr & ~3];
458 				uint8_t *vramsrc = &m_vram[m_vram_src & ~3];
459 
460 				int sdx = m_vram_src & 3;
461 				int ddx = m_vram_addr & 3;
462 
463 				LOG("Copy rectangle backwards, width %d height %d dst %x (%d, %d) src %x (%d, %d)\n", m_width, m_height, m_vram_addr, m_vram_addr % 1152, m_vram_addr / 1152, m_vram_src, m_vram_src % 1152, m_vram_src / 1152);
464 
465 				for (y = 0; y < m_height; y++)
466 				{
467 					for (x = 0; x < m_width; x++)
468 					{
469 						vram[(-y * 1152)+BYTE4_XOR_BE(-x + ddx)] = vramsrc[(-y * 1152)+BYTE4_XOR_BE(-x + sdx)];
470 					}
471 				}
472 				(void)vramsrc; (void)sdx;
473 			}
474 			else
475 			{
476 				LOG("Unknown blitter command %08x\n", data);
477 			}
478 			break;
479 
480 		default:
481 			LOG("specpdq_w: %08x @ %x (mask %08x  %s)\n", data^0xffffffff, offset, mem_mask, machine().describe_context());
482 			break;
483 	}
484 }
485 
specpdq_r(offs_t offset,uint32_t mem_mask)486 uint32_t nubus_specpdq_device::specpdq_r(offs_t offset, uint32_t mem_mask)
487 {
488 //  if (offset != 0xc005c && offset != 0xc005e) logerror("specpdq_r: @ %x (mask %08x  %s)\n", offset, mem_mask, machine().describe_context());
489 
490 	if (offset >= 0xc0000 && offset < 0x100000)
491 	{
492 		return m_7xxxxx_regs[offset-0xc0000];
493 	}
494 
495 	return 0;
496 }
497 
vram_w(offs_t offset,uint32_t data,uint32_t mem_mask)498 void nubus_specpdq_device::vram_w(offs_t offset, uint32_t data, uint32_t mem_mask)
499 {
500 	data ^= 0xffffffff;
501 	COMBINE_DATA(&m_vram32[offset]);
502 }
503 
vram_r(offs_t offset,uint32_t mem_mask)504 uint32_t nubus_specpdq_device::vram_r(offs_t offset, uint32_t mem_mask)
505 {
506 	return m_vram32[offset] ^ 0xffffffff;
507 }
508