1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     Atari Crystal Castles hardware
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/ccastles.h"
11 #include "video/resnet.h"
12 
13 
14 /*************************************
15  *
16  *  Video startup
17  *
18  *************************************/
19 
video_start()20 void ccastles_state::video_start()
21 {
22 	static const int resistances[3] = { 22000, 10000, 4700 };
23 
24 	/* get pointers to our PROMs */
25 	m_syncprom = memregion("proms")->base() + 0x000;
26 	m_wpprom = memregion("proms")->base() + 0x200;
27 	m_priprom = memregion("proms")->base() + 0x300;
28 
29 	/* compute the color output resistor weights at startup */
30 	compute_resistor_weights(0, 255, -1.0,
31 			3,  resistances, m_rweights, 1000, 0,
32 			3,  resistances, m_gweights, 1000, 0,
33 			3,  resistances, m_bweights, 1000, 0);
34 
35 	/* allocate a bitmap for drawing sprites */
36 	m_screen->register_screen_bitmap(m_spritebitmap);
37 
38 	/* register for savestates */
39 	save_item(NAME(m_bitmode_addr));
40 	save_item(NAME(m_hscroll));
41 	save_item(NAME(m_vscroll));
42 }
43 
44 
45 
46 /*************************************
47  *
48  *  Video control registers
49  *
50  *************************************/
51 
ccastles_hscroll_w(uint8_t data)52 void ccastles_state::ccastles_hscroll_w(uint8_t data)
53 {
54 	m_screen->update_partial(m_screen->vpos());
55 	m_hscroll = data;
56 }
57 
58 
ccastles_vscroll_w(uint8_t data)59 void ccastles_state::ccastles_vscroll_w(uint8_t data)
60 {
61 	m_vscroll = data;
62 }
63 
64 
ccastles_video_control_w(offs_t offset,uint8_t data)65 void ccastles_state::ccastles_video_control_w(offs_t offset, uint8_t data)
66 {
67 	/* only D3 matters */
68 	m_outlatch[1]->write_bit(offset, BIT(data, 3));
69 }
70 
71 
72 
73 /*************************************
74  *
75  *  Palette RAM accesses
76  *
77  *************************************/
78 
ccastles_paletteram_w(offs_t offset,uint8_t data)79 void ccastles_state::ccastles_paletteram_w(offs_t offset, uint8_t data)
80 {
81 	int bit0, bit1, bit2;
82 	int r, g, b;
83 
84 	/* extract the raw RGB bits */
85 	r = ((data & 0xc0) >> 6) | ((offset & 0x20) >> 3);
86 	b = (data & 0x38) >> 3;
87 	g = (data & 0x07);
88 
89 	/* red component (inverted) */
90 	bit0 = (~r >> 0) & 0x01;
91 	bit1 = (~r >> 1) & 0x01;
92 	bit2 = (~r >> 2) & 0x01;
93 	r = combine_weights(m_rweights, bit0, bit1, bit2);
94 
95 	/* green component (inverted) */
96 	bit0 = (~g >> 0) & 0x01;
97 	bit1 = (~g >> 1) & 0x01;
98 	bit2 = (~g >> 2) & 0x01;
99 	g = combine_weights(m_gweights, bit0, bit1, bit2);
100 
101 	/* blue component (inverted) */
102 	bit0 = (~b >> 0) & 0x01;
103 	bit1 = (~b >> 1) & 0x01;
104 	bit2 = (~b >> 2) & 0x01;
105 	b = combine_weights(m_bweights, bit0, bit1, bit2);
106 
107 	m_palette->set_pen_color(offset & 0x1f, rgb_t(r, g, b));
108 }
109 
110 
111 
112 /*************************************
113  *
114  *  Video RAM access via the write
115  *  protect PROM
116  *
117  *************************************/
118 
ccastles_write_vram(uint16_t addr,uint8_t data,uint8_t bitmd,uint8_t pixba)119 inline void ccastles_state::ccastles_write_vram( uint16_t addr, uint8_t data, uint8_t bitmd, uint8_t pixba )
120 {
121 	uint8_t *dest = &m_videoram[addr & 0x7ffe];
122 	uint8_t promaddr = 0;
123 	uint8_t wpbits;
124 
125 	/*
126 	    Inputs to the write-protect PROM:
127 
128 	    Bit 7 = BA1520 = 0 if (BA15-BA12 != 0), or 1 otherwise
129 	    Bit 6 = DRBA11
130 	    Bit 5 = DRBA10
131 	    Bit 4 = /BITMD
132 	    Bit 3 = GND
133 	    Bit 2 = BA0
134 	    Bit 1 = PIXB
135 	    Bit 0 = PIXA
136 	*/
137 	promaddr |= ((addr & 0xf000) == 0) << 7;
138 	promaddr |= (addr & 0x0c00) >> 5;
139 	promaddr |= (!bitmd) << 4;
140 	promaddr |= (addr & 0x0001) << 2;
141 	promaddr |= (pixba << 0);
142 
143 	/* look up the PROM result */
144 	wpbits = m_wpprom[promaddr];
145 
146 	/* write to the appropriate parts of VRAM depending on the result */
147 	if (!(wpbits & 1))
148 		dest[0] = (dest[0] & 0xf0) | (data & 0x0f);
149 	if (!(wpbits & 2))
150 		dest[0] = (dest[0] & 0x0f) | (data & 0xf0);
151 	if (!(wpbits & 4))
152 		dest[1] = (dest[1] & 0xf0) | (data & 0x0f);
153 	if (!(wpbits & 8))
154 		dest[1] = (dest[1] & 0x0f) | (data & 0xf0);
155 }
156 
157 
158 
159 /*************************************
160  *
161  *  Autoincrement control for bit mode
162  *
163  *************************************/
164 
bitmode_autoinc()165 inline void ccastles_state::bitmode_autoinc(  )
166 {
167 	/* auto increment in the x-direction if it's enabled */
168 	if (!m_outlatch[1]->q0_r()) /* /AX */
169 	{
170 		if (!m_outlatch[1]->q2_r()) /* /XINC */
171 			m_bitmode_addr[0]++;
172 		else
173 			m_bitmode_addr[0]--;
174 	}
175 
176 	/* auto increment in the y-direction if it's enabled */
177 	if (!m_outlatch[1]->q1_r()) /* /AY */
178 	{
179 		if (!m_outlatch[1]->q3_r()) /* /YINC */
180 			m_bitmode_addr[1]++;
181 		else
182 			m_bitmode_addr[1]--;
183 	}
184 }
185 
186 
187 
188 /*************************************
189  *
190  *  Standard video RAM access
191  *
192  *************************************/
193 
ccastles_videoram_w(offs_t offset,uint8_t data)194 void ccastles_state::ccastles_videoram_w(offs_t offset, uint8_t data)
195 {
196 	/* direct writes to VRAM go through the write protect PROM as well */
197 	ccastles_write_vram(offset, data, 0, 0);
198 }
199 
200 
201 
202 /*************************************
203  *
204  *  Bit mode video RAM access
205  *
206  *************************************/
207 
ccastles_bitmode_r()208 uint8_t ccastles_state::ccastles_bitmode_r()
209 {
210 	/* in bitmode, the address comes from the autoincrement latches */
211 	uint16_t addr = (m_bitmode_addr[1] << 7) | (m_bitmode_addr[0] >> 1);
212 
213 	/* the appropriate pixel is selected into the upper 4 bits */
214 	uint8_t result = m_videoram[addr] << ((~m_bitmode_addr[0] & 1) * 4);
215 
216 	/* autoincrement because /BITMD was selected */
217 	bitmode_autoinc();
218 
219 	/* the low 4 bits of the data lines are not driven so make them all 1's */
220 	return result | 0x0f;
221 }
222 
223 
ccastles_bitmode_w(uint8_t data)224 void ccastles_state::ccastles_bitmode_w(uint8_t data)
225 {
226 	/* in bitmode, the address comes from the autoincrement latches */
227 	uint16_t addr = (m_bitmode_addr[1] << 7) | (m_bitmode_addr[0] >> 1);
228 
229 	/* the upper 4 bits of data are replicated to the lower 4 bits */
230 	data = (data & 0xf0) | (data >> 4);
231 
232 	/* write through the generic VRAM routine, passing the low 2 X bits as PIXB/PIXA */
233 	ccastles_write_vram(addr, data, 1, m_bitmode_addr[0] & 3);
234 
235 	/* autoincrement because /BITMD was selected */
236 	bitmode_autoinc();
237 }
238 
239 
ccastles_bitmode_addr_w(offs_t offset,uint8_t data)240 void ccastles_state::ccastles_bitmode_addr_w(offs_t offset, uint8_t data)
241 {
242 	/* write through to video RAM and also to the addressing latches */
243 	ccastles_write_vram(offset, data, 0, 0);
244 	m_bitmode_addr[offset] = data;
245 }
246 
247 
248 
249 /*************************************
250  *
251  *  Video updating
252  *
253  *************************************/
254 
screen_update_ccastles(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)255 uint32_t ccastles_state::screen_update_ccastles(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
256 {
257 	uint8_t const *const spriteaddr = &m_spriteram[m_outlatch[1]->q7_r() * 0x100];   /* BUF1/BUF2 */
258 	int const flip = m_outlatch[1]->q4_r() ? 0xff : 0x00;    /* PLAYER2 */
259 	pen_t const black = m_palette->black_pen();
260 
261 	/* draw the sprites */
262 	m_spritebitmap.fill(0x0f, cliprect);
263 	for (int offs = 0; offs < 320/2; offs += 4)
264 	{
265 		int const x = spriteaddr[offs + 3];
266 		int const y = 256 - 16 - spriteaddr[offs + 1];
267 		int const which = spriteaddr[offs];
268 		int const color = spriteaddr[offs + 2] >> 7;
269 
270 		m_gfxdecode->gfx(0)->transpen(m_spritebitmap,cliprect, which, color, flip, flip, x, y, 7);
271 	}
272 
273 	/* draw the bitmap to the screen, looping over Y */
274 	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
275 	{
276 		uint16_t *const dst = &bitmap.pix(y);
277 
278 		/* if we're in the VBLANK region, just fill with black */
279 		if (m_syncprom[y] & 1)
280 		{
281 			for (int x = cliprect.left(); x <= cliprect.right(); x++)
282 				dst[x] = black;
283 		}
284 
285 		/* non-VBLANK region: merge the sprites and the bitmap */
286 		else
287 		{
288 			uint16_t const *const mosrc = &m_spritebitmap.pix(y);
289 
290 			/* the "POTATO" chip does some magic here; this is just a guess */
291 			int effy = (((y - m_vblank_end) + (flip ? 0 : m_vscroll)) ^ flip) & 0xff;
292 			if (effy < 24)
293 				effy = 24;
294 			uint8_t const *const src = &m_videoram[effy * 128];
295 
296 			/* loop over X */
297 			for (int x = cliprect.left(); x <= cliprect.right(); x++)
298 			{
299 				/* if we're in the HBLANK region, just store black */
300 				if (x >= 256)
301 					dst[x] = black;
302 
303 				/* otherwise, process normally */
304 				else
305 				{
306 					int const effx = (m_hscroll + (x ^ flip)) & 255;
307 
308 					/* low 4 bits = left pixel, high 4 bits = right pixel */
309 					uint8_t pix = (src[effx / 2] >> ((effx & 1) * 4)) & 0x0f;
310 					uint8_t const mopix = mosrc[x];
311 
312 					/* Inputs to the priority PROM:
313 
314 					    Bit 7 = GND
315 					    Bit 6 = /CRAM
316 					    Bit 5 = BA4
317 					    Bit 4 = MV2
318 					    Bit 3 = MV1
319 					    Bit 2 = MV0
320 					    Bit 1 = MPI
321 					    Bit 0 = BIT3
322 					*/
323 					uint8_t prindex;
324 					prindex = 0x40;
325 					prindex |= (mopix & 7) << 2;
326 					prindex |= (mopix & 8) >> 2;
327 					prindex |= (pix & 8) >> 3;
328 					uint8_t const prvalue = m_priprom[prindex];
329 
330 					/* Bit 1 of prvalue selects the low 4 bits of the final pixel */
331 					if (prvalue & 2)
332 						pix = mopix;
333 
334 					/* Bit 0 of prvalue selects bit 4 of the final color */
335 					pix |= (prvalue & 1) << 4;
336 
337 					/* store the pixel value and also a priority value based on the topmost bit */
338 					dst[x] = pix;
339 				}
340 			}
341 		}
342 	}
343 	return 0;
344 }
345