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