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