1 // license:BSD-3-Clause
2 // copyright-holders:Ernesto Corvi, Mariusz Wojcieszek, Aaron Giles
3 /***************************************************************************
4 
5     Amiga hardware
6 
7     Driver by: Ernesto Corvi, Mariusz Wojcieszek, Aaron Giles
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "includes/amiga.h"
13 
14 
15 
16 /*************************************
17  *
18  *  Debugging
19  *
20  *************************************/
21 
22 #define LOG_COPPER          0
23 #define GUESS_COPPER_OFFSET 0
24 #define LOG_SPRITE_DMA      0
25 
26 
27 
28 /*************************************
29  *
30  *  Macros
31  *
32  *************************************/
33 
34 #define COPPER_CYCLES_TO_PIXELS(x)      (4 * (x))
35 
36 
37 
38 /*************************************
39  *
40  *  Tables
41  *
42  *************************************/
43 
44 /* expand an 8-bit bit pattern into 16 bits, every other bit */
45 const uint16_t amiga_state::s_expand_byte[256] =
46 {
47 	0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
48 	0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
49 	0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
50 	0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
51 	0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
52 	0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
53 	0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
54 	0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
55 	0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
56 	0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
57 	0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
58 	0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
59 	0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
60 	0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
61 	0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
62 	0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
63 
64 	0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
65 	0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
66 	0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
67 	0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
68 	0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
69 	0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
70 	0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
71 	0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
72 	0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
73 	0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
74 	0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
75 	0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
76 	0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
77 	0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
78 	0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
79 	0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
80 };
81 
82 const uint16_t delay[256] =
83 {
84 	1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,    /* 0x000 - 0x03e */
85 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                        /* 0x040 - 0x05e */
86 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                        /* 0x060 - 0x07e */
87 	0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,                        /* 0x080 - 0x09e */
88 	1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,    /* 0x0a0 - 0x0de */
89 	/* BPLxPTH/BPLxPTL */
90 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                        /* 0x0e0 - 0x0fe */
91 	/* BPLCON0-3,BPLMOD1-2 */
92 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,                        /* 0x100 - 0x11e */
93 	/* SPRxPTH/SPRxPTL */
94 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,                        /* 0x120 - 0x13e */
95 	/* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */
96 	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,    /* 0x140 - 0x17e */
97 	/* COLORxx */
98 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,    /* 0x180 - 0x1be */
99 	/* RESERVED */
100 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0x1c0 - 0x1fe */
101 };
102 
103 
104 
105 /*************************************
106  *
107  *  4-4-4 palette init
108  *
109  *************************************/
110 
amiga_palette(palette_device & palette) const111 void amiga_state::amiga_palette(palette_device &palette) const
112 {
113 	for (int i = 0; i < 0x1000; i++)
114 		palette.set_pen_color(i, pal4bit(i >> 8), pal4bit(i >> 4), pal4bit(i));
115 }
116 
117 
118 
119 /*************************************
120  *
121  *  Video startup
122  *
123  *************************************/
124 
VIDEO_START_MEMBER(amiga_state,amiga)125 VIDEO_START_MEMBER( amiga_state, amiga )
126 {
127 	int j;
128 
129 	/* generate tables that produce the correct playfield color for dual playfield mode */
130 	for (j = 0; j < 64; j++)
131 	{
132 		int pf1pix = ((j >> 0) & 1) | ((j >> 1) & 2) | ((j >> 2) & 4);
133 		int pf2pix = ((j >> 1) & 1) | ((j >> 2) & 2) | ((j >> 3) & 4);
134 
135 		m_separate_bitplanes[0][j] = (pf1pix || !pf2pix) ? pf1pix : (pf2pix + 8);
136 		m_separate_bitplanes[1][j] = pf2pix ? (pf2pix + 8) : pf1pix;
137 	}
138 
139 #if GUESS_COPPER_OFFSET
140 	m_wait_offset = 3;
141 #else
142 	(void)m_wait_offset;
143 #endif
144 
145 	/* reset the genlock color */
146 	m_genlock_color = 0xffff;
147 
148 	m_sprite_ctl_written = 0;
149 
150 	m_screen->register_screen_bitmap(m_flickerfixer);
151 }
152 
153 
154 
155 /*************************************
156  *
157  *  Beam position
158  *
159  *************************************/
160 
amiga_gethvpos()161 uint32_t amiga_state::amiga_gethvpos()
162 {
163 	uint32_t hvpos = (m_last_scanline << 8) | (m_screen->hpos() >> 2);
164 	uint32_t latchedpos = m_hvpos.read_safe(0);
165 
166 	/* if there's no latched position, or if we are in the active display area */
167 	/* but before the latching point, return the live HV position */
168 	if ((CUSTOM_REG(REG_BPLCON0) & 0x0008) == 0 || latchedpos == 0 || (m_last_scanline >= 20 && hvpos < latchedpos))
169 		return hvpos;
170 
171 	/* otherwise, return the latched position */
172 	return latchedpos;
173 }
174 
175 
176 
177 /*************************************
178  *
179  *  Genlock interaction
180  *
181  *************************************/
182 
set_genlock_color(uint16_t color)183 void amiga_state::set_genlock_color(uint16_t color)
184 {
185 	m_genlock_color = color;
186 }
187 
188 
189 
190 /*************************************
191  *
192  *  Copper emulation
193  *
194  *************************************/
195 
copper_setpc(uint32_t pc)196 void amiga_state::copper_setpc(uint32_t pc)
197 {
198 	if (LOG_COPPER)
199 		logerror("copper_setpc(%06x)\n", pc);
200 
201 	m_copper_pc = pc;
202 	m_copper_waiting = false;
203 }
204 
205 
copper_execute_next(int xpos)206 int amiga_state::copper_execute_next(int xpos)
207 {
208 	uint8_t ypos = m_last_scanline & 0xff;
209 	int word0, word1;
210 
211 	/* bail if not enabled */
212 	if ((CUSTOM_REG(REG_DMACON) & (DMACON_COPEN | DMACON_DMAEN)) != (DMACON_COPEN | DMACON_DMAEN))
213 		return 511;
214 
215 	/* flush any pending writes */
216 	if (m_copper_pending_offset)
217 	{
218 		if (LOG_COPPER)
219 			logerror("%02X.%02X: Write to %s = %04x\n", m_last_scanline, xpos / 2, s_custom_reg_names[m_copper_pending_offset & 0xff], m_copper_pending_data);
220 		custom_chip_w(m_copper_pending_offset, m_copper_pending_data);
221 		m_copper_pending_offset = 0;
222 	}
223 
224 	/* if we're waiting, check for a breakthrough */
225 	if (m_copper_waiting)
226 	{
227 		int curpos = (ypos << 8) | (xpos >> 1);
228 
229 		/* if we're past the wait time, stop it and hold up 2 cycles */
230 		if ((curpos & m_copper_waitmask) >= (m_copper_waitval & m_copper_waitmask) &&
231 			(!m_copper_waitblit || !(CUSTOM_REG(REG_DMACON) & DMACON_BBUSY)))
232 		{
233 			m_copper_waiting = false;
234 #if GUESS_COPPER_OFFSET
235 			return xpos + COPPER_CYCLES_TO_PIXELS(1 + m_wait_offset);
236 #else
237 			return xpos + COPPER_CYCLES_TO_PIXELS(1 + 3);
238 #endif
239 		}
240 
241 		/* otherwise, see if this line is even a possibility; if not, punt */
242 		if (((curpos | 0xff) & m_copper_waitmask) < (m_copper_waitval & m_copper_waitmask))
243 			return 511;
244 
245 		/* else just advance another pixel */
246 		xpos += COPPER_CYCLES_TO_PIXELS(1);
247 		return xpos;
248 	}
249 
250 	/* fetch the first data word */
251 	word0 = read_chip_ram(m_copper_pc);
252 	m_copper_pc += 2;
253 	xpos += COPPER_CYCLES_TO_PIXELS(1);
254 
255 	/* fetch the second data word */
256 	word1 = read_chip_ram(m_copper_pc);
257 	m_copper_pc += 2;
258 	xpos += COPPER_CYCLES_TO_PIXELS(1);
259 
260 	if (LOG_COPPER)
261 		logerror("%02X.%02X: Copper inst @ %06x = %04x %04x\n", m_last_scanline, xpos / 2, m_copper_pc, word0, word1);
262 
263 	/* handle a move */
264 	if ((word0 & 1) == 0)
265 	{
266 		int min = (CUSTOM_REG(REG_COPCON) & 2) ? 0x20 : 0x40;
267 
268 		/* do the write if we're allowed */
269 		word0 = (word0 >> 1) & 0xff;
270 		if (word0 >= min)
271 		{
272 			if (delay[word0] == 0)
273 			{
274 				if (LOG_COPPER)
275 					logerror("%02X.%02X: Write to %s = %04x\n", m_last_scanline, xpos / 2, s_custom_reg_names[word0 & 0xff], word1);
276 				custom_chip_w(word0, word1);
277 			}
278 			else    // additional 2 cycles needed for non-Agnus registers
279 			{
280 				m_copper_pending_offset = word0;
281 				m_copper_pending_data = word1;
282 			}
283 		}
284 
285 		/* illegal writes suspend until next frame */
286 		else
287 		{
288 			if (LOG_COPPER)
289 				logerror("%02X.%02X: Aborting copper on illegal write\n", m_last_scanline, xpos / 2);
290 
291 			m_copper_waitval = 0xffff;
292 			m_copper_waitmask = 0xffff;
293 			m_copper_waitblit = false;
294 			m_copper_waiting = true;
295 
296 			return 511;
297 		}
298 	}
299 	else
300 	{
301 		/* extract common wait/skip values */
302 		m_copper_waitval = word0 & 0xfffe;
303 
304 #if 0
305 		if (m_copper_waitval != 0xfffe)
306 			m_copper_waitval = (word0 & 0x00fe) | ((((word0 >> 8) & 0xff) + 1) << 8);
307 #endif
308 
309 		m_copper_waitmask = word1 | 0x8001;
310 		m_copper_waitblit = (~word1 >> 15) & 1;
311 
312 		/* handle a wait */
313 		if ((word1 & 1) == 0)
314 		{
315 			if (LOG_COPPER)
316 				logerror("  Waiting for %04x & %04x (currently %04x)\n", m_copper_waitval, m_copper_waitmask, (m_last_scanline << 8) | (xpos >> 1));
317 
318 			m_copper_waiting = true;
319 		}
320 
321 		/* handle a skip */
322 		else
323 		{
324 			int curpos = (ypos << 8) | (xpos >> 1);
325 
326 			if (LOG_COPPER)
327 				logerror("  Skipping if %04x & %04x (currently %04x)\n", m_copper_waitval, m_copper_waitmask, (m_last_scanline << 8) | (xpos >> 1));
328 
329 			/* if we're past the wait time, stop it and hold up 2 cycles */
330 			if ((curpos & m_copper_waitmask) >= (m_copper_waitval & m_copper_waitmask) &&
331 				(!m_copper_waitblit || !(CUSTOM_REG(REG_DMACON) & DMACON_BBUSY)))
332 			{
333 				if (LOG_COPPER)
334 					logerror("  Skipped\n");
335 
336 				/* count the cycles it out have taken to fetch the next instruction */
337 				m_copper_pc += 4;
338 				xpos += COPPER_CYCLES_TO_PIXELS(2);
339 			}
340 		}
341 	}
342 
343 	/* advance and consume 8 cycles */
344 	return xpos;
345 }
346 
347 
348 
349 /*************************************
350  *
351  *  External sprite controls
352  *
353  *************************************/
354 
sprite_dma_reset(int which)355 void amiga_state::sprite_dma_reset(int which)
356 {
357 	if (LOG_SPRITE_DMA) logerror("sprite %d dma reset\n", which );
358 	m_sprite_dma_reload_mask |= 1 << which;
359 	m_sprite_dma_live_mask |= 1 << which;
360 }
361 
362 
sprite_enable_comparitor(int which,int enable)363 void amiga_state::sprite_enable_comparitor(int which, int enable)
364 {
365 	if (LOG_SPRITE_DMA) logerror("sprite %d comparitor %sable\n", which, enable ? "en" : "dis" );
366 	if (enable)
367 	{
368 		m_sprite_comparitor_enable_mask |= 1 << which;
369 		m_sprite_dma_live_mask &= ~(1 << which);
370 	}
371 	else
372 	{
373 		m_sprite_comparitor_enable_mask &= ~(1 << which);
374 		m_sprite_ctl_written |= (1 << which);
375 	}
376 }
377 
378 
379 
380 /*************************************
381  *
382  *  Per-scanline sprite fetcher
383  *
384  *************************************/
385 
fetch_sprite_data(int scanline,int sprite)386 void amiga_state::fetch_sprite_data(int scanline, int sprite)
387 {
388 	CUSTOM_REG(REG_SPR0DATA + 4 * sprite) = read_chip_ram(CUSTOM_REG_LONG(REG_SPR0PTH + 2 * sprite) + 0);
389 	CUSTOM_REG(REG_SPR0DATB + 4 * sprite) = read_chip_ram(CUSTOM_REG_LONG(REG_SPR0PTH + 2 * sprite) + 2);
390 	CUSTOM_REG_LONG(REG_SPR0PTH + 2 * sprite) += 4;
391 	if (LOG_SPRITE_DMA) logerror("%3d:sprite %d fetch: data=%04X-%04X\n", scanline, sprite, CUSTOM_REG(REG_SPR0DATA + 4 * sprite), CUSTOM_REG(REG_SPR0DATB + 4 * sprite));
392 }
393 
update_sprite_dma(int scanline)394 void amiga_state::update_sprite_dma(int scanline)
395 {
396 	int dmaenable = (CUSTOM_REG(REG_DMACON) & (DMACON_SPREN | DMACON_DMAEN)) == (DMACON_SPREN | DMACON_DMAEN);
397 	int num, maxdma;
398 
399 	/* channels are limited by DDFSTART */
400 	maxdma = (CUSTOM_REG(REG_DDFSTRT) - 0x14) / 4;
401 	if (maxdma > 8)
402 		maxdma = 8;
403 
404 	/* loop over sprite channels */
405 	for (num = 0; num < maxdma; num++)
406 	{
407 		int bitmask = 1 << num;
408 		int vstart, vstop;
409 
410 		/* if we are == VSTOP, fetch new control words */
411 		if (dmaenable && (m_sprite_dma_live_mask & bitmask) && (m_sprite_dma_reload_mask & bitmask))
412 		{
413 			/* disable the sprite */
414 			m_sprite_comparitor_enable_mask &= ~bitmask;
415 			m_sprite_dma_reload_mask &= ~bitmask;
416 
417 			/* fetch data into the control words */
418 			CUSTOM_REG(REG_SPR0POS + 4 * num) = read_chip_ram(CUSTOM_REG_LONG(REG_SPR0PTH + 2 * num) + 0);
419 			CUSTOM_REG(REG_SPR0CTL + 4 * num) = read_chip_ram(CUSTOM_REG_LONG(REG_SPR0PTH + 2 * num) + 2);
420 			CUSTOM_REG_LONG(REG_SPR0PTH + 2 * num) += 4;
421 			if (LOG_SPRITE_DMA) logerror("%3d:sprite %d fetch: pos=%04X ctl=%04X\n", scanline, num, CUSTOM_REG(REG_SPR0POS + 4 * num), CUSTOM_REG(REG_SPR0CTL + 4 * num));
422 		}
423 
424 		/* compute vstart/vstop */
425 		vstart = (CUSTOM_REG(REG_SPR0POS + 4 * num) >> 8) | ((CUSTOM_REG(REG_SPR0CTL + 4 * num) << 6) & 0x100);
426 		vstop = (CUSTOM_REG(REG_SPR0CTL + 4 * num) >> 8) | ((CUSTOM_REG(REG_SPR0CTL + 4 * num) << 7) & 0x100);
427 
428 		/* if we hit vstart, enable the comparitor */
429 		if (scanline == vstart)
430 		{
431 			m_sprite_comparitor_enable_mask |= 1 << num;
432 			if (LOG_SPRITE_DMA) logerror("%3d:sprite %d comparitor enable\n", scanline, num);
433 		}
434 
435 		/* if we hit vstop, disable the comparitor and trigger a reload for the next scanline */
436 		if (scanline == vstop)
437 		{
438 			m_sprite_ctl_written &= ~bitmask;
439 			m_sprite_comparitor_enable_mask &= ~bitmask;
440 			m_sprite_dma_reload_mask |= 1 << num;
441 			CUSTOM_REG(REG_SPR0DATA + 4 * num) = 0;     /* just a guess */
442 			CUSTOM_REG(REG_SPR0DATB + 4 * num) = 0;
443 			if (LOG_SPRITE_DMA) logerror("%3d:sprite %d comparitor disable, prepare for reload\n", scanline, num);
444 		}
445 
446 		/* fetch data if this sprite is enabled */
447 		if (dmaenable && (m_sprite_dma_live_mask & bitmask) && (m_sprite_comparitor_enable_mask & bitmask))
448 		{
449 			fetch_sprite_data(scanline, num);
450 		}
451 	}
452 }
453 
454 
455 
456 /*************************************
457  *
458  *  Per-pixel sprite computations
459  *
460  *************************************/
461 
interleave_sprite_data(uint16_t lobits,uint16_t hibits)462 uint32_t amiga_state::interleave_sprite_data(uint16_t lobits, uint16_t hibits)
463 {
464 	return (s_expand_byte[lobits & 0xff] << 0) | (s_expand_byte[lobits >> 8] << 16) |
465 			(s_expand_byte[hibits & 0xff] << 1) | (s_expand_byte[hibits >> 8] << 17);
466 }
467 
468 
get_sprite_pixel(int x)469 int amiga_state::get_sprite_pixel(int x)
470 {
471 	int pixels = 0;
472 	int num, pair;
473 
474 	/* loop over sprite channels */
475 	for (num = 0; num < 8; num++)
476 		if (m_sprite_comparitor_enable_mask & (1 << num))
477 		{
478 			/* if we're not currently clocking, check against hstart */
479 			if (m_sprite_remain[num] == 0)
480 			{
481 				int hstart = ((CUSTOM_REG(REG_SPR0POS + 4 * num) & 0xff) << 1) | (CUSTOM_REG(REG_SPR0CTL + 4 * num) & 1);
482 				if (hstart == x)
483 				{
484 					m_sprite_remain[num] = 16;
485 					m_sprite_shiftreg[num] = interleave_sprite_data(CUSTOM_REG(REG_SPR0DATA + 4 * num), CUSTOM_REG(REG_SPR0DATB + 4 * num));
486 				}
487 			}
488 
489 			/* clock the next pixel if we're doing it */
490 			if (m_sprite_remain[num] != 0)
491 			{
492 				m_sprite_remain[num]--;
493 				pixels |= (m_sprite_shiftreg[num] & 0xc0000000) >> (16 + 2 * (7 - num));
494 				m_sprite_shiftreg[num] <<= 2;
495 			}
496 		}
497 
498 	/* if we have pixels, determine the actual color and get out */
499 	if (pixels)
500 	{
501 		static const uint16_t ormask[16] =
502 		{
503 			0x0000, 0x000c, 0x00c0, 0x00cc, 0x0c00, 0x0c0c, 0x0cc0, 0x0ccc,
504 			0xc000, 0xc00c, 0xc0c0, 0xc0cc, 0xcc00, 0xcc0c, 0xccc0, 0xcccc
505 		};
506 		static const uint16_t spritecollide[16] =
507 		{
508 			0x0000, 0x0000, 0x0000, 0x0200, 0x0000, 0x0400, 0x1000, 0x1600,
509 			0x0000, 0x0800, 0x2000, 0x2a00, 0x4000, 0x4c00, 0x7000, 0x7e00
510 		};
511 		int collide;
512 		const int esprm = 0x10, osprm = 0x10;
513 
514 		/* OR the two sprite bits together so we only have 1 bit per sprite */
515 		collide = pixels | (pixels >> 1);
516 
517 		/* based on the CLXCON, merge even/odd sprite results */
518 		collide |= (collide & ormask[CUSTOM_REG(REG_CLXCON) >> 12]) >> 2;
519 
520 		/* collapse down to a 4-bit final "sprite present" mask */
521 		collide = (collide & 1) | ((collide >> 3) & 2) | ((collide >> 6) & 4) | ((collide >> 9) & 8);
522 
523 		/* compute sprite-sprite collisions */
524 		CUSTOM_REG(REG_CLXDAT) |= spritecollide[collide];
525 
526 		/* now determine the actual color */
527 		for (pair = 0; pixels; pair++, pixels >>= 4)
528 			if (pixels & 0x0f)
529 			{
530 				/* final result is:
531 				    topmost sprite color in bits 0-5
532 				    sprite present bitmask in bits 6-9
533 				    topmost sprite pair index in bits 10-11
534 				*/
535 				uint32_t result = (collide << 6) | (pair << 10);
536 
537 				/* attached case */
538 				if (CUSTOM_REG(REG_SPR1CTL + 8 * pair) & 0x0080)
539 					return (pixels & 0xf) | osprm | result;
540 
541 				/* lower-numbered sprite of pair */
542 				else if (pixels & 3)
543 					return (pixels & 3) | esprm | (pair << 2) | result;
544 
545 				/* higher-numbered sprite of pair */
546 				else
547 					return ((pixels >> 2) & 3) | osprm | (pair << 2) | result;
548 			}
549 	}
550 
551 	return 0;
552 }
553 
554 
555 
556 /*************************************
557  *
558  *  Bitplane assembly
559  *
560  *************************************/
561 
assemble_odd_bitplanes(int planes,int obitoffs)562 uint8_t amiga_state::assemble_odd_bitplanes(int planes, int obitoffs)
563 {
564 	uint8_t pix = (CUSTOM_REG(REG_BPL1DAT) >> obitoffs) & 1;
565 	if (planes >= 3)
566 	{
567 		pix |= ((CUSTOM_REG(REG_BPL3DAT) >> obitoffs) & 1) << 2;
568 		if (planes >= 5)
569 			pix |= ((CUSTOM_REG(REG_BPL5DAT) >> obitoffs) & 1) << 4;
570 	}
571 	return pix;
572 }
573 
574 
assemble_even_bitplanes(int planes,int ebitoffs)575 uint8_t amiga_state::assemble_even_bitplanes(int planes, int ebitoffs)
576 {
577 	uint8_t pix = 0;
578 	if (planes >= 2)
579 	{
580 		pix |= ((CUSTOM_REG(REG_BPL2DAT) >> ebitoffs) & 1) << 1;
581 		if (planes >= 4)
582 		{
583 			pix |= ((CUSTOM_REG(REG_BPL4DAT) >> ebitoffs) & 1) << 3;
584 			if (planes >= 6)
585 				pix |= ((CUSTOM_REG(REG_BPL6DAT) >> ebitoffs) & 1) << 5;
586 		}
587 	}
588 	return pix;
589 }
590 
fetch_bitplane_data(int plane)591 void amiga_state::fetch_bitplane_data(int plane)
592 {
593 	CUSTOM_REG(REG_BPL1DAT + plane) = read_chip_ram(CUSTOM_REG_LONG(REG_BPL1PTH + plane * 2));
594 	CUSTOM_REG_LONG(REG_BPL1PTH + plane * 2) += 2;
595 }
596 
597 
598 /*************************************
599  *
600  *  Hold and modify pixel computations
601  *
602  *************************************/
603 
update_ham(int newpix)604 int amiga_state::update_ham(int newpix)
605 {
606 	switch (newpix >> 4)
607 	{
608 		case 0:
609 			m_ham_color = CUSTOM_REG(REG_COLOR00 + (newpix & 0xf));
610 			break;
611 
612 		case 1:
613 			m_ham_color = (m_ham_color & 0xff0) | ((newpix & 0xf) << 0);
614 			break;
615 
616 		case 2:
617 			m_ham_color = (m_ham_color & 0x0ff) | ((newpix & 0xf) << 8);
618 			break;
619 
620 		case 3:
621 			m_ham_color = (m_ham_color & 0xf0f) | ((newpix & 0xf) << 4);
622 			break;
623 	}
624 	return m_ham_color;
625 }
626 
627 
628 //**************************************************************************
629 //  DISPLAY WINDOW
630 //**************************************************************************
631 
update_display_window()632 void amiga_state::update_display_window()
633 {
634 	int vstart = CUSTOM_REG(REG_DIWSTRT) >> 8;
635 	int vstop = CUSTOM_REG(REG_DIWSTOP) >> 8;
636 	int hstart = CUSTOM_REG(REG_DIWSTRT) & 0xff;
637 	int hstop = CUSTOM_REG(REG_DIWSTOP) & 0xff;
638 
639 	if (m_diwhigh_valid)
640 	{
641 		vstart |= (CUSTOM_REG(REG_DIWHIGH) & 7) << 8;
642 		vstop  |= ((CUSTOM_REG(REG_DIWHIGH) >> 8) & 7) << 8;
643 		hstart |= ((CUSTOM_REG(REG_DIWHIGH) >> 5) & 1) << 8;
644 		hstop  |= ((CUSTOM_REG(REG_DIWHIGH) >> 13) & 1) << 8;
645 	}
646 	else
647 	{
648 		vstop |= ((~CUSTOM_REG(REG_DIWSTOP) >> 7) & 0x100);
649 		hstop |= 0x100;
650 	}
651 
652 	if (hstop < hstart)
653 	{
654 		hstart = 0x00;
655 		hstop = 0x1ff;
656 	}
657 
658 	m_diw.set(hstart, hstop, vstart, vstop);
659 }
660 
661 
662 /*************************************
663  *
664  *  Single scanline rasterizer
665  *
666  *************************************/
667 
render_scanline(bitmap_rgb32 & bitmap,int scanline)668 void amiga_state::render_scanline(bitmap_rgb32 &bitmap, int scanline)
669 {
670 	uint16_t save_color0 = CUSTOM_REG(REG_COLOR00);
671 	int ddf_start_pixel = 0, ddf_stop_pixel = 0;
672 	int hires = 0, dualpf = 0, ham = 0;
673 	int pf1pri = 0, pf2pri = 0;
674 	int planes = 0;
675 
676 	uint32_t *dst = nullptr;
677 	int ebitoffs = 0, obitoffs = 0;
678 	int ecolmask = 0, ocolmask = 0;
679 	int edelay = 0, odelay = 0;
680 	int next_copper_x;
681 	int pl;
682 	const int defbitoffs = 15;
683 
684 	int save_scanline = scanline;
685 
686 	// we need to do a bit more work on the first scanline
687 	if (scanline == 0)
688 	{
689 		m_previous_lof = CUSTOM_REG(REG_VPOSR) & VPOSR_LOF;
690 
691 		// toggle lof if enabled
692 		if (CUSTOM_REG(REG_BPLCON0) & BPLCON0_LACE)
693 			CUSTOM_REG(REG_VPOSR) ^= VPOSR_LOF;
694 
695 		// reset copper and ham color
696 		copper_setpc(CUSTOM_REG_LONG(REG_COP1LCH));
697 		m_ham_color = CUSTOM_REG(REG_COLOR00);
698 	}
699 
700 	// in visible area?
701 	if (bitmap.valid())
702 	{
703 		bool lof = CUSTOM_REG(REG_VPOSR) & VPOSR_LOF;
704 
705 		if ((scanline & 1) ^ lof)
706 		{
707 			// lof matches? then render this scanline
708 			dst = &bitmap.pix(scanline);
709 		}
710 		else
711 		{
712 			// lof doesn't match, we don't render this scanline
713 			// if we didn't switch lof we have a full non-interlace screen,
714 			// so we fill the black gaps with the contents of the previous scanline
715 			// otherwise just render the contents of the previous frame's scanline
716 			int shift = (m_previous_lof == lof) ? 1 : 0;
717 
718 			std::copy_n(&m_flickerfixer.pix(scanline - shift), amiga_state::SCREEN_WIDTH, &bitmap.pix(scanline));
719 			return;
720 		}
721 	}
722 
723 	scanline /= 2;
724 
725 	m_last_scanline = scanline;
726 
727 	/* update sprite data fetching */
728 	update_sprite_dma(scanline);
729 
730 	/* all sprites off at the start of the line */
731 	memset(m_sprite_remain, 0, sizeof(m_sprite_remain));
732 
733 	/* temporary set color 0 to the genlock color */
734 	if (m_genlock_color != 0xffff)
735 		CUSTOM_REG(REG_COLOR00) = m_genlock_color;
736 
737 	/* loop over the line */
738 	next_copper_x = 0;
739 	for (int x = 0; x < amiga_state::SCREEN_WIDTH / 2; x++)
740 	{
741 		int sprpix;
742 
743 		/* time to execute the copper? */
744 		if (x == next_copper_x)
745 		{
746 			/* execute the next batch, restoring and re-saving color 0 around it */
747 			CUSTOM_REG(REG_COLOR00) = save_color0;
748 			next_copper_x = copper_execute_next(x);
749 			save_color0 = CUSTOM_REG(REG_COLOR00);
750 			if (m_genlock_color != 0xffff)
751 				CUSTOM_REG(REG_COLOR00) = m_genlock_color;
752 
753 			/* compute update-related register values */
754 			planes = (CUSTOM_REG(REG_BPLCON0) & (BPLCON0_BPU0 | BPLCON0_BPU1 | BPLCON0_BPU2)) >> 12;
755 			hires = CUSTOM_REG(REG_BPLCON0) & BPLCON0_HIRES;
756 			ham = CUSTOM_REG(REG_BPLCON0) & BPLCON0_HOMOD;
757 			dualpf = CUSTOM_REG(REG_BPLCON0) & BPLCON0_DBLPF;
758 
759 			/* compute the pixel fetch parameters */
760 			ddf_start_pixel = (CUSTOM_REG(REG_DDFSTRT) & (hires ? 0xfc : 0xf8)) * 2;
761 			ddf_start_pixel += hires ? 9 : 17;
762 			ddf_stop_pixel = (CUSTOM_REG(REG_DDFSTOP) & (hires ? 0xfc : 0xf8)) * 2;
763 			ddf_stop_pixel += hires ? (9 + defbitoffs) : (17 + defbitoffs);
764 
765 			if ( ( CUSTOM_REG(REG_DDFSTRT) ^ CUSTOM_REG(REG_DDFSTOP) ) & 0x04 )
766 				ddf_stop_pixel += 8;
767 
768 			// display window
769 			update_display_window();
770 
771 			/* extract playfield priorities */
772 			pf1pri = CUSTOM_REG(REG_BPLCON2) & 7;
773 			pf2pri = (CUSTOM_REG(REG_BPLCON2) >> 3) & 7;
774 
775 			/* extract collision masks */
776 			ocolmask = (CUSTOM_REG(REG_CLXCON) >> 6) & 0x15;
777 			ecolmask = (CUSTOM_REG(REG_CLXCON) >> 6) & 0x2a;
778 		}
779 
780 		/* clear the target pixels to the background color as a starting point */
781 		if (dst != nullptr)
782 			dst[x*2+0] =
783 			dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00));
784 
785 		/* if we hit the first fetch pixel, reset the counters and latch the delays */
786 		if (x == ddf_start_pixel)
787 		{
788 			odelay = CUSTOM_REG(REG_BPLCON1) & 0xf;
789 			edelay = ( CUSTOM_REG(REG_BPLCON1) >> 4 ) & 0x0f;
790 
791 			if ( hires )
792 			{
793 				obitoffs = defbitoffs + ( odelay << 1 );
794 				ebitoffs = defbitoffs + ( edelay << 1 );
795 			}
796 			else
797 			{
798 				if ( CUSTOM_REG(REG_DDFSTRT) & 0x04 )
799 				{
800 					odelay = ( odelay + 8 ) & 0x0f;
801 					edelay = ( edelay + 8 ) & 0x0f;
802 				}
803 
804 				obitoffs = defbitoffs + odelay;
805 				ebitoffs = defbitoffs + edelay;
806 			}
807 
808 			for (pl = 0; pl < 6; pl++)
809 				CUSTOM_REG(REG_BPL1DAT + pl) = 0;
810 		}
811 
812 		/* need to run the sprite engine every pixel to ensure display */
813 		sprpix = get_sprite_pixel(x);
814 
815 		/* to render, we must have bitplane DMA enabled, at least 1 plane, and be within the */
816 		/* vertical display window */
817 		if ((CUSTOM_REG(REG_DMACON) & (DMACON_BPLEN | DMACON_DMAEN)) == (DMACON_BPLEN | DMACON_DMAEN) &&
818 			planes > 0 && scanline >= m_diw.top() && scanline < m_diw.bottom())
819 		{
820 			int pfpix0 = 0, pfpix1 = 0, collide;
821 
822 			/* fetch the odd bits if we are within the fetching region */
823 			if (x >= ddf_start_pixel && x <= ddf_stop_pixel + odelay)
824 			{
825 				/* if we need to fetch more data, do it now */
826 				if (obitoffs == defbitoffs)
827 				{
828 					for (pl = 0; pl < planes; pl += 2)
829 					{
830 						fetch_bitplane_data(pl);
831 					}
832 				}
833 
834 				/* now assemble the bits */
835 				pfpix0 |= assemble_odd_bitplanes(planes, obitoffs);
836 				obitoffs--;
837 
838 				/* for high res, assemble a second set of bits */
839 				if (hires)
840 				{
841 					/* reset bit offsets and fetch more data if needed */
842 					if (obitoffs < 0)
843 					{
844 						obitoffs = defbitoffs;
845 
846 						for (pl = 0; pl < planes; pl += 2)
847 						{
848 							fetch_bitplane_data(pl);
849 						}
850 					}
851 
852 					pfpix1 |= assemble_odd_bitplanes(planes, obitoffs);
853 					obitoffs--;
854 				}
855 				else
856 					pfpix1 |= pfpix0 & 0x15;
857 
858 				/* reset bit offsets if needed */
859 				if (obitoffs < 0)
860 					obitoffs = defbitoffs;
861 			}
862 
863 			/* fetch the even bits if we are within the fetching region */
864 			if (x >= ddf_start_pixel && x <= ddf_stop_pixel + edelay)
865 			{
866 				/* if we need to fetch more data, do it now */
867 				if (ebitoffs == defbitoffs)
868 				{
869 					for (pl = 1; pl < planes; pl += 2)
870 					{
871 						fetch_bitplane_data(pl);
872 					}
873 				}
874 
875 				/* now assemble the bits */
876 				pfpix0 |= assemble_even_bitplanes(planes, ebitoffs);
877 				ebitoffs--;
878 
879 				/* for high res, assemble a second set of bits */
880 				if (hires)
881 				{
882 					/* reset bit offsets and fetch more data if needed */
883 					if (ebitoffs < 0)
884 					{
885 						ebitoffs = defbitoffs;
886 
887 						for (pl = 1; pl < planes; pl += 2)
888 						{
889 							fetch_bitplane_data(pl);
890 						}
891 					}
892 
893 					pfpix1 |= assemble_even_bitplanes(planes, ebitoffs);
894 					ebitoffs--;
895 				}
896 				else
897 					pfpix1 |= pfpix0 & 0x2a;
898 
899 				/* reset bit offsets if needed */
900 				if (ebitoffs < 0)
901 					ebitoffs = defbitoffs;
902 			}
903 
904 			/* compute playfield/sprite collisions for first pixel */
905 			collide = pfpix0 ^ CUSTOM_REG(REG_CLXCON);
906 			if ((collide & ocolmask) == 0)
907 				CUSTOM_REG(REG_CLXDAT) |= (sprpix >> 5) & 0x01e;
908 			if ((collide & ecolmask) == 0)
909 				CUSTOM_REG(REG_CLXDAT) |= (sprpix >> 1) & 0x1e0;
910 			if ((collide & (ecolmask | ocolmask)) == 0)
911 				CUSTOM_REG(REG_CLXDAT) |= 0x001;
912 
913 			/* compute playfield/sprite collisions for second pixel */
914 			collide = pfpix1 ^ CUSTOM_REG(REG_CLXCON);
915 			if ((collide & ocolmask) == 0)
916 				CUSTOM_REG(REG_CLXDAT) |= (sprpix >> 5) & 0x01e;
917 			if ((collide & ecolmask) == 0)
918 				CUSTOM_REG(REG_CLXDAT) |= (sprpix >> 1) & 0x1e0;
919 			if ((collide & (ecolmask | ocolmask)) == 0)
920 				CUSTOM_REG(REG_CLXDAT) |= 0x001;
921 
922 			/* if we are within the display region, render */
923 			if (dst != nullptr && x >= m_diw.left() && x < m_diw.right())
924 			{
925 				int pix, pri;
926 
927 				/* hold-and-modify mode -- assume low-res (hi-res not supported by the hardware) */
928 				if (ham)
929 				{
930 					/* update the HAM color */
931 					pfpix0 = update_ham(pfpix0);
932 
933 					pix = sprpix & 0x1f;
934 					pri = (sprpix >> 10);
935 
936 					/* sprite has priority */
937 					if (sprpix && pf1pri > pri)
938 					{
939 						dst[x*2+0] =
940 						dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pix));
941 					}
942 
943 					/* playfield has priority */
944 					else
945 					{
946 						dst[x*2+0] =
947 						dst[x*2+1] = m_palette->pen(pfpix0);
948 					}
949 				}
950 
951 				/* dual playfield mode */
952 				else if (dualpf)
953 				{
954 					/* mask out the sprite if it doesn't have priority */
955 					pix = sprpix & 0x1f;
956 					pri = (sprpix >> 10);
957 					if (pix)
958 					{
959 						if ((pfpix0 & 0x15) && pf1pri <= pri)
960 							pix = 0;
961 						if ((pfpix0 & 0x2a) && pf2pri <= pri)
962 							pix = 0;
963 					}
964 
965 					/* write out the left pixel */
966 					if (pix)
967 						dst[x*2+0] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pix));
968 					else
969 						dst[x*2+0] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + m_separate_bitplanes[(CUSTOM_REG(REG_BPLCON2) >> 6) & 1][pfpix0]));
970 
971 					/* mask out the sprite if it doesn't have priority */
972 					pix = sprpix & 0x1f;
973 					if (pix)
974 					{
975 						if ((pfpix1 & 0x15) && pf1pri <= pri)
976 							pix = 0;
977 						if ((pfpix1 & 0x2a) && pf2pri <= pri)
978 							pix = 0;
979 					}
980 
981 					/* write out the right pixel */
982 					if (pix)
983 						dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pix));
984 					else
985 						dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + m_separate_bitplanes[(CUSTOM_REG(REG_BPLCON2) >> 6) & 1][pfpix1]));
986 				}
987 
988 				/* single playfield mode */
989 				else
990 				{
991 					pix = sprpix & 0x1f;
992 					pri = (sprpix >> 10);
993 
994 					/* sprite has priority */
995 					if (sprpix && pf1pri > pri)
996 					{
997 						dst[x*2+0] =
998 						dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pix));
999 					}
1000 
1001 					/* playfield has priority */
1002 					else
1003 					{
1004 						dst[x*2+0] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pfpix0));
1005 						dst[x*2+1] = m_palette->pen(CUSTOM_REG(REG_COLOR00 + pfpix1));
1006 					}
1007 				}
1008 			}
1009 		}
1010 	}
1011 
1012 	// end of the line: time to add the modulos
1013 	if (scanline >= m_diw.top() && scanline < m_diw.bottom())
1014 	{
1015 		// update odd planes
1016 		for (pl = 0; pl < planes; pl += 2)
1017 			CUSTOM_REG_LONG(REG_BPL1PTH + pl * 2) += CUSTOM_REG_SIGNED(REG_BPL1MOD);
1018 
1019 		// update even planes
1020 		for (pl = 1; pl < planes; pl += 2)
1021 			CUSTOM_REG_LONG(REG_BPL1PTH + pl * 2) += CUSTOM_REG_SIGNED(REG_BPL2MOD);
1022 	}
1023 
1024 	// restore color00
1025 	CUSTOM_REG(REG_COLOR00) = save_color0;
1026 
1027 	// save
1028 	if (dst != nullptr)
1029 		std::copy_n(dst, amiga_state::SCREEN_WIDTH, &m_flickerfixer.pix(save_scanline));
1030 
1031 #if GUESS_COPPER_OFFSET
1032 	if (m_screen->frame_number() % 64 == 0 && scanline == 0)
1033 	{
1034 		if (machine().input().code_pressed(KEYCODE_Q))
1035 			popmessage("%d", m_wait_offset -= 1);
1036 		if (machine().input().code_pressed(KEYCODE_W))
1037 			popmessage("%d", m_wait_offset += 1);
1038 	}
1039 #endif
1040 }
1041 
1042 
1043 
1044 /*************************************
1045  *
1046  *  Update
1047  *
1048  *************************************/
1049 
screen_update_amiga(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)1050 uint32_t amiga_state::screen_update_amiga(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1051 {
1052 	// sometimes the core tells us to render a bunch of lines to keep up (resolution change, for example)
1053 	// this causes trouble for us since it can happen at any time
1054 	if (cliprect.top() != cliprect.bottom())
1055 		return 0;
1056 
1057 	// render each scanline in the visible region
1058 	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
1059 		render_scanline(bitmap, y);
1060 
1061 	return 0;
1062 }
1063 
update_screenmode()1064 void amiga_state::update_screenmode()
1065 {
1066 	bool pal;
1067 
1068 	// first let's see if we're PAL or NTSC
1069 	if (m_agnus_id >= AGNUS_HR_PAL)
1070 		// we support dynamic switching between PAL and NTSC, determine mode from register
1071 		pal = CUSTOM_REG(REG_BEAMCON0) & 0x20;
1072 	else
1073 		// old agnus, agnus id determines PAL or NTSC
1074 		pal = !(m_agnus_id & 0x10);
1075 
1076 	// basic height & vblank length
1077 	int height = pal ? SCREEN_HEIGHT_PAL : SCREEN_HEIGHT_NTSC;
1078 	int vblank = pal ? VBLANK_PAL : VBLANK_NTSC;
1079 
1080 	// frame period
1081 	attoseconds_t period = HZ_TO_ATTOSECONDS(m_screen->clock()) * SCREEN_WIDTH * height;
1082 
1083 	// adjust visible area
1084 	rectangle visarea = m_screen->visible_area();
1085 	visarea.sety(vblank, height - 1);
1086 
1087 	// finally set our new mode
1088 	m_screen->configure(SCREEN_WIDTH, height, visarea, period);
1089 }
1090 
1091 
1092 //**************************************************************************
1093 //  MACHINE DRIVER FRAGMENTS
1094 //**************************************************************************
1095 
pal_video(machine_config & config)1096 void amiga_state::pal_video(machine_config &config)
1097 {
1098 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
1099 	m_screen->set_raw
1100 	(
1101 		(amiga_state::CLK_28M_PAL / 4) * 2 * 2,
1102 		amiga_state::SCREEN_WIDTH, amiga_state::HBLANK, amiga_state::SCREEN_WIDTH,
1103 		amiga_state::SCREEN_HEIGHT_PAL, amiga_state::VBLANK_PAL, amiga_state::SCREEN_HEIGHT_PAL
1104 	);
1105 	m_screen->set_screen_update(FUNC(amiga_state::screen_update_amiga));
1106 }
1107 
ntsc_video(machine_config & config)1108 void amiga_state::ntsc_video(machine_config &config)
1109 {
1110 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
1111 	m_screen->set_raw
1112 	(
1113 		(amiga_state::CLK_28M_NTSC / 4) * 2 * 2,
1114 		amiga_state::SCREEN_WIDTH, amiga_state::HBLANK, amiga_state::SCREEN_WIDTH,
1115 		amiga_state::SCREEN_HEIGHT_NTSC, amiga_state::VBLANK_NTSC, amiga_state::SCREEN_HEIGHT_NTSC
1116 	);
1117 	m_screen->set_screen_update(FUNC(amiga_state::screen_update_amiga));
1118 }
1119