1 // license:BSD-3-Clause
2 // copyright-holders:R. Belmont
3 /***************************************************************************
4 
5   Apple Hi-Resolution Video Card emulation
6 
7   RAMDAC: control at Fs0940e0, data at Fs0940e4
8   Fs090010 bit 16 is vbl status, bit 17 must be "1" for proper operation
9   Fs08000x are the control registers
10 
11 ***************************************************************************/
12 
13 #include "emu.h"
14 #include "nubus_m2hires.h"
15 #include "screen.h"
16 
17 #define M2HIRES_SCREEN_NAME "m2hires_screen"
18 #define M2HIRES_ROM_REGION  "m2hires_rom"
19 
20 #define VRAM_SIZE   (0x80000)   // 512k max
21 
22 
23 ROM_START( m2hires )
24 	ROM_REGION(0x2000, M2HIRES_ROM_REGION, 0)
CRC(ea6f7913)25 	ROM_LOAD( "341-0660.bin", 0x0000, 0x2000, CRC(ea6f7913) SHA1(37c59f38ae34021d0cb86c2e76a598b7e6077c0d) )
26 ROM_END
27 
28 //**************************************************************************
29 //  GLOBAL VARIABLES
30 //**************************************************************************
31 
32 DEFINE_DEVICE_TYPE(NUBUS_M2HIRES, nubus_m2hires_device, "nb_m2hr", "Macintosh II Hi-Resolution video card")
33 
34 
35 //-------------------------------------------------
36 //  device_add_mconfig - add device configuration
37 //-------------------------------------------------
38 
39 void nubus_m2hires_device::device_add_mconfig(machine_config &config)
40 {
41 	screen_device &screen(SCREEN(config, M2HIRES_SCREEN_NAME, SCREEN_TYPE_RASTER));
42 	screen.set_screen_update(FUNC(nubus_m2hires_device::screen_update));
43 	screen.set_raw(25175000, 800, 0, 640, 525, 0, 480);
44 	screen.set_size(1024, 768);
45 	screen.set_visarea(0, 640-1, 0, 480-1);
46 }
47 
48 //-------------------------------------------------
49 //  rom_region - device-specific ROM region
50 //-------------------------------------------------
51 
device_rom_region() const52 const tiny_rom_entry *nubus_m2hires_device::device_rom_region() const
53 {
54 	return ROM_NAME( m2hires );
55 }
56 
57 //**************************************************************************
58 //  LIVE DEVICE
59 //**************************************************************************
60 
61 //-------------------------------------------------
62 //  nubus_m2hires_device - constructor
63 //-------------------------------------------------
64 
nubus_m2hires_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)65 nubus_m2hires_device::nubus_m2hires_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
66 	nubus_m2hires_device(mconfig, NUBUS_M2HIRES, tag, owner, clock)
67 {
68 }
69 
nubus_m2hires_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)70 nubus_m2hires_device::nubus_m2hires_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
71 	device_t(mconfig, type, tag, owner, clock),
72 	device_video_interface(mconfig, *this),
73 	device_nubus_card_interface(mconfig, *this),
74 	m_vram32(nullptr), m_mode(0), m_vbl_disable(0), m_toggle(0), m_count(0), m_clutoffs(0), m_timer(nullptr)
75 {
76 	set_screen(*this, M2HIRES_SCREEN_NAME);
77 }
78 
79 //-------------------------------------------------
80 //  device_start - device-specific startup
81 //-------------------------------------------------
82 
device_start()83 void nubus_m2hires_device::device_start()
84 {
85 	uint32_t slotspace;
86 
87 	install_declaration_rom(this, M2HIRES_ROM_REGION, true);
88 
89 	slotspace = get_slotspace();
90 
91 //  logerror("[m2hires %p] slotspace = %x\n", this, slotspace);
92 
93 	m_vram.resize(VRAM_SIZE);
94 	m_vram32 = (uint32_t *)&m_vram[0];
95 
96 	nubus().install_device(slotspace, slotspace+VRAM_SIZE-1, read32s_delegate(*this, FUNC(nubus_m2hires_device::vram_r)), write32s_delegate(*this, FUNC(nubus_m2hires_device::vram_w)));
97 	nubus().install_device(slotspace+0x900000, slotspace+VRAM_SIZE-1+0x900000, read32s_delegate(*this, FUNC(nubus_m2hires_device::vram_r)), write32s_delegate(*this, FUNC(nubus_m2hires_device::vram_w)));
98 	nubus().install_device(slotspace+0x80000, slotspace+0xeffff, read32s_delegate(*this, FUNC(nubus_m2hires_device::m2hires_r)), write32s_delegate(*this, FUNC(nubus_m2hires_device::m2hires_w)));
99 
100 	m_timer = timer_alloc(0, nullptr);
101 	m_timer->adjust(screen().time_until_pos(479, 0), 0);
102 }
103 
104 //-------------------------------------------------
105 //  device_reset - device-specific reset
106 //-------------------------------------------------
107 
device_reset()108 void nubus_m2hires_device::device_reset()
109 {
110 	m_count = 0;
111 	m_clutoffs = 0;
112 	m_vbl_disable = 1;
113 	m_mode = 0;
114 	memset(&m_vram[0], 0, VRAM_SIZE);
115 	memset(m_palette, 0, sizeof(m_palette));
116 
117 	m_palette[0] = rgb_t(255, 255, 255);
118 	m_palette[0x80] = rgb_t(0, 0, 0);
119 }
120 
121 
device_timer(emu_timer & timer,device_timer_id tid,int param,void * ptr)122 void nubus_m2hires_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
123 {
124 	if (!m_vbl_disable)
125 	{
126 		raise_slot_irq();
127 	}
128 
129 	m_timer->adjust(screen().time_until_pos(479, 0), 0);
130 }
131 
132 /***************************************************************************
133 
134   Spectrum 24 PDQ section
135 
136 ***************************************************************************/
137 
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)138 uint32_t nubus_m2hires_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
139 {
140 	uint8_t const *const vram = &m_vram[0x20];
141 
142 	switch (m_mode)
143 	{
144 		case 0: // 1 bpp?
145 			for (int y = 0; y < 480; y++)
146 			{
147 				uint32_t *scanline = &bitmap.pix(y);
148 				for (int x = 0; x < 640/8; x++)
149 				{
150 					uint8_t const pixels = vram[(y * 128) + (BYTE4_XOR_BE(x))];
151 
152 					*scanline++ = m_palette[BIT(pixels, 7)];
153 					*scanline++ = m_palette[BIT(pixels, 6)];
154 					*scanline++ = m_palette[BIT(pixels, 5)];
155 					*scanline++ = m_palette[BIT(pixels, 4)];
156 					*scanline++ = m_palette[BIT(pixels, 3)];
157 					*scanline++ = m_palette[BIT(pixels, 2)];
158 					*scanline++ = m_palette[BIT(pixels, 1)];
159 					*scanline++ = m_palette[BIT(pixels, 0)];
160 				}
161 			}
162 			break;
163 
164 		case 1: // 2 bpp
165 			for (int y = 0; y < 480; y++)
166 			{
167 				uint32_t *scanline = &bitmap.pix(y);
168 				for (int x = 0; x < 640/4; x++)
169 				{
170 					uint8_t const pixels = vram[(y * 256) + (BYTE4_XOR_BE(x))];
171 
172 					*scanline++ = m_palette[((pixels>>6)&3)];
173 					*scanline++ = m_palette[((pixels>>4)&3)];
174 					*scanline++ = m_palette[((pixels>>2)&3)];
175 					*scanline++ = m_palette[(pixels&3)];
176 				}
177 			}
178 			break;
179 
180 		case 2: // 4 bpp
181 			for (int y = 0; y < 480; y++)
182 			{
183 				uint32_t *scanline = &bitmap.pix(y);
184 
185 				for (int x = 0; x < 640/2; x++)
186 				{
187 					uint8_t const pixels = vram[(y * 512) + (BYTE4_XOR_BE(x))];
188 
189 					*scanline++ = m_palette[((pixels&0xf0)>>4)];
190 					*scanline++ = m_palette[(pixels&0xf)];
191 				}
192 			}
193 			break;
194 
195 		case 3: // 8 bpp
196 			for (int y = 0; y < 480; y++)
197 			{
198 				uint32_t *scanline = &bitmap.pix(y);
199 
200 				for (int x = 0; x < 640; x++)
201 				{
202 					uint8_t const pixels = vram[(y * 1024) + (BYTE4_XOR_BE(x))];
203 					*scanline++ = m_palette[pixels];
204 				}
205 			}
206 			break;
207 
208 		default:
209 			fatalerror("m2hires: unknown video mode %d\n", m_mode);
210 	}
211 	return 0;
212 }
213 
m2hires_w(offs_t offset,uint32_t data,uint32_t mem_mask)214 void nubus_m2hires_device::m2hires_w(offs_t offset, uint32_t data, uint32_t mem_mask)
215 {
216 	data ^= 0xffffffff;
217 
218 	switch (offset)
219 	{
220 		case 1:         // mode
221 			switch (data)
222 			{
223 				case 0x20000000:
224 					m_mode = 0;
225 					break;
226 
227 				case 0x40000000:
228 					m_mode = 1;
229 					break;
230 
231 				case 0x80000000:
232 					m_mode = 2;
233 					break;
234 
235 				case 0x00010000:
236 					m_mode = 3;
237 					break;
238 			}
239 			break;
240 
241 		case 0x5038:    // DAC control
242 //          logerror("%08x to DAC control %s\n", data, machine().describe_context());
243 			m_clutoffs = (data>>24)&0xff;
244 			break;
245 
246 		case 0x5039: // DAC data
247 			m_colors[m_count++] = (data>>24) & 0xff;
248 
249 			if (m_count == 3)
250 			{
251 //              logerror("RAMDAC: color %d = %02x %02x %02x %s\n", m_clutoffs, m_colors[0], m_colors[1], m_colors[2], machine().describe_context() );
252 				m_palette[m_clutoffs] = rgb_t(m_colors[0], m_colors[1], m_colors[2]);
253 				m_clutoffs++;
254 				if (m_clutoffs > 255)
255 				{
256 					m_clutoffs = 0;
257 				}
258 				m_count = 0;
259 			}
260 			break;
261 
262 		case 0x8000:    // enable and ack VBL
263 			m_vbl_disable = 0;
264 			lower_slot_irq();
265 			break;
266 
267 		case 0x8001:    // disable VBL
268 			m_vbl_disable = 1;
269 			break;
270 
271 		default:
272 //          logerror("m2hires_w: %08x @ %x, mask %08x %s\n", data, offset, mem_mask, machine().describe_context());
273 			break;
274 	}
275 }
276 
m2hires_r(offs_t offset,uint32_t mem_mask)277 uint32_t nubus_m2hires_device::m2hires_r(offs_t offset, uint32_t mem_mask)
278 {
279 	if (offset == 0x10010/4)
280 	{
281 		m_toggle ^= (1<<16);
282 		return m_toggle | (1<<17);  // bit 17 indicates a 4/8bpp capable ASIC apparently; the firmware won't enter those modes otherwise (although they show in the list)
283 	}
284 /*  else
285     {
286         logerror("m2hires_r: @ %x, mask %08x %s\n", offset, mem_mask, machine().describe_context());
287     }*/
288 
289 	return 0;
290 }
291 
vram_w(offs_t offset,uint32_t data,uint32_t mem_mask)292 void nubus_m2hires_device::vram_w(offs_t offset, uint32_t data, uint32_t mem_mask)
293 {
294 	data ^= 0xffffffff;
295 	COMBINE_DATA(&m_vram32[offset]);
296 }
297 
vram_r(offs_t offset,uint32_t mem_mask)298 uint32_t nubus_m2hires_device::vram_r(offs_t offset, uint32_t mem_mask)
299 {
300 	return m_vram32[offset] ^ 0xffffffff;
301 }
302