1 /*
2 	Falcon VIDEL emulation
3 
4 	(C) 2001-2008 ARAnyM developer team
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #include "sysdeps.h"
22 #include "SDL_compat.h"
23 
24 #include "sysdeps.h"
25 #include "hardware.h"
26 #include "cpu_emulation.h"
27 #include "memory-uae.h"
28 #include "hostscreen.h"
29 #include "host.h"
30 #include "icio.h"
31 #include "host_surface.h"
32 #include "videl.h"
33 #include "hardware.h"
34 #include "parameters.h"
35 #include "adler32.h"
36 
37 #define DEBUG 0
38 #include "debug.h"
39 
40 /*--- Defines ---*/
41 
42 #define HW	getHWoffset()
43 #define VIDEL_COLOR_REGS_BEGIN	0xff9800UL
44 #define VIDEL_COLOR_REGS_END	(VIDEL_COLOR_REGS_BEGIN+256*4)
45 
46 #define F_COLORS(i) handleRead(VIDEL_COLOR_REGS_BEGIN + (i))
47 #define STE_COLORS(i)	handleReadW(0xff8240 + (i<<1))
48 
49 /*--- Constructor/destructor ---*/
50 
VIDEL(memptr addr,uint32 size)51 VIDEL::VIDEL(memptr addr, uint32 size)
52 	: BASE_IO(addr, size), surface(NULL),
53 	prevVidelWidth(-1), prevVidelHeight(-1), prevVidelBpp(-1),
54 	crcList(NULL)
55 {
56 	reset();
57 }
58 
~VIDEL(void)59 VIDEL::~VIDEL(void)
60 {
61 	if (crcList) {
62 		delete [] crcList;
63 	}
64 	if (surface) {
65 		host->video->destroySurface(surface);
66 	}
67 }
68 
reset(void)69 void VIDEL::reset(void)
70 {
71 	aspect_x = aspect_y = 1;
72 	updatePalette = false;
73 }
74 
isMyHWRegister(memptr addr)75 bool VIDEL::isMyHWRegister(memptr addr)
76 {
77 	// FALCON VIDEL COLOR REGISTERS
78 	if (addr >= VIDEL_COLOR_REGS_BEGIN && addr < VIDEL_COLOR_REGS_END)
79 		return true;
80 
81 	return BASE_IO::isMyHWRegister(addr);
82 }
83 
84 /*--- BASE_IO functions ---*/
85 
handleWrite(uint32 addr,uint8 value)86 void VIDEL::handleWrite(uint32 addr, uint8 value)
87 {
88 	BASE_IO::handleWrite(addr, value);
89 
90 	if ((addr >= VIDEL_COLOR_REGS_BEGIN && addr < VIDEL_COLOR_REGS_END) ||
91 		(addr >= 0xff8240 && addr < 0xff8260)) {
92 		updatePalette = true;
93 		return;
94 	}
95 
96 	if ((addr & ~1) == HW+0x60) {
97 		D(bug("VIDEL st_shift: %06x = %d ($%02x)", addr, value, value));
98 		// writing to st_shift changed scn_width and vid_mode
99 		// BASE_IO::handleWrite(HW+0x10, 0x0028);
100 		// BASE_IO::handleWrite(HW+0xc2, 0x0008);
101 	} else if (((addr & ~1) == HW+0x64) || ((addr & ~1) == HW+0x0e) || ((addr & ~1) == HW+0x10)) {
102 		forceRefresh();
103 	} else if ((addr & ~1) == HW+0x66) {
104 		D(bug("VIDEL f_shift: %06x = %d ($%02x)", addr, value, value));
105 		// IMPORTANT:
106 		// set st_shift to 0, so we can tell the screen-depth if f_shift==0.
107 		// Writing 0 to f_shift enables 4 plane Falcon mode but
108 		// doesn't set st_shift. st_shift!=0 (!=4planes) is impossible
109 		// with Falcon palette so we set st_shift to 0 manually.
110 		if (handleReadW(HW+0x66) == 0) {
111 			BASE_IO::handleWrite(HW+0x60, 0);
112 		}
113 	}
114 
115 	if ((addr & ~1) == HW+0xc2) {
116 		updateAspect();
117 	}
118 }
119 
120 /*--- Private functions ---*/
121 
useStPalette(void)122 bool VIDEL::useStPalette(void)
123 {
124 	bool useStPal = false;
125 
126 	int st_shift = handleReadW(HW + 0x60);
127 	if (st_shift == 0) {
128 		// bpp == 4
129 		int hreg = handleReadW(HW + 0x82); // Too lame!
130 		// Better way how to make out ST LOW mode wanted
131 		if (hreg == 0x10 || hreg == 0x17 || hreg == 0x3e) {
132 			useStPal = true;
133 		}
134 	} else if (st_shift == 0x100) {
135 		// bpp == 2
136 		useStPal = true;
137 	} else {
138 		// bpp == 1	// if (st_shift == 0x200)
139 		useStPal = true;
140 	}
141 
142 	return useStPal;
143 }
144 
updateAspect(void)145 void VIDEL::updateAspect(void)
146 {
147 	int vco = handleReadW(HW + 0xc2);
148 
149 	switch((vco>>2) & 3) {
150 		case 0:
151 			aspect_x = 4;
152 			break;
153 		case 1:
154 			aspect_x = 2;
155 			break;
156 		case 2:
157 			aspect_x = 1;
158 			break;
159 	}
160 
161 	switch (vco & 3) {
162 		case 0:
163 			/* No interlace, no doubling */
164 			aspect_y = 2;
165 			break;
166 		case 1:
167 			/* No interlace, Doubling */
168 			aspect_y = 4;
169 			break;
170 		case 2:
171 			/* Interlace, no doubling */
172 			aspect_y = 1;
173 			break;
174 		case 3:
175 			/* Interlace, Doubling */
176 			aspect_y = 2;
177 			break;
178 	}
179 
180 	if (bx_options.video.monitor == 1) {
181 		/* rvb */
182 		aspect_x >>= 1;
183 		if (aspect_x == 0) {
184 			aspect_x = 1;
185 			aspect_y <<= 1;
186 		}
187 	} else {
188 		/* vga */
189 		aspect_y >>= 1;
190 		if (aspect_y == 0) {
191 			aspect_y = 1;
192 			aspect_x <<= 1;
193 		}
194 	}
195 }
196 
197 /*--- Protected functions ---*/
198 
getVramAddress(void)199 Uint32 VIDEL::getVramAddress(void)
200 {
201 	return (handleRead(HW + 1) << 16) | (handleRead(HW + 3) << 8) | handleRead(HW + 0x0d);
202 }
203 
getWidth(void)204 int VIDEL::getWidth(void)
205 {
206 	return handleReadW(HW + 0x10) * 16 / getBpp();
207 }
208 
getHeight(void)209 int VIDEL::getHeight(void)
210 {
211 	int vdb = handleReadW(HW + 0xa8);
212 	int vde = handleReadW(HW + 0xaa);
213 	int vmode = handleReadW(HW + 0xc2);
214 
215 	/* visible y resolution:
216 	 * Graphics display starts at line VDB and ends at line
217 	 * VDE. If interlace mode off unit of VC-registers is
218 	 * half lines, else lines.
219 	 */
220 	int yres = vde - vdb;
221 	if (!(vmode & 0x02))		// interlace
222 		yres >>= 1;
223 	if (vmode & 0x01)		// double
224 		yres >>= 1;
225 
226 	return yres;
227 }
228 
getBpp(void)229 int VIDEL::getBpp(void)
230 {
231 	int f_shift = handleReadW(HW + 0x66);
232 	int st_shift = handleRead(HW + 0x60);
233 	/* to get bpp, we must examine f_shift and st_shift.
234 	 * f_shift is valid if any of bits no. 10, 8 or 4
235 	 * is set. Priority in f_shift is: 10 ">" 8 ">" 4, i.e.
236 	 * if bit 10 set then bit 8 and bit 4 don't care...
237 	 * If all these bits are 0 get display depth from st_shift
238 	 * (as for ST and STE)
239 	 */
240 	int bits_per_pixel = 1;
241 	if (f_shift & 0x400)		/* 2 colors */
242 		bits_per_pixel = 1;
243 	else if (f_shift & 0x100)	/* hicolor */
244 		bits_per_pixel = 16;
245 	else if (f_shift & 0x010)	/* 8 bitplanes */
246 		bits_per_pixel = 8;
247 	else if (st_shift == 0)
248 		bits_per_pixel = 4;
249 	else if (st_shift == 0x01)
250 		bits_per_pixel = 2;
251 	else /* if (st_shift == 0x02) */
252 		bits_per_pixel = 1;
253 
254 	// D(bug("Videl works in %d bpp, f_shift=%04x, st_shift=%d", bits_per_pixel, f_shift, st_shift));
255 
256 	return bits_per_pixel;
257 }
258 
getSurface(void)259 HostSurface *VIDEL::getSurface(void)
260 {
261 	int videlBpp = getBpp();
262 	int bpp = (videlBpp<=8) ? 8 : 16;
263 	int width = getWidth();
264 	int height = getHeight();
265 
266 	if (width<64) {
267 		width = 64;
268 	}
269 	if (height<64) {
270 		height = 64;
271 	}
272 
273 	if (surface) {
274 		if (prevVidelBpp == bpp) {
275 			if ((prevVidelWidth!=width) || (prevVidelHeight!=height)) {
276 				surface->resize(width, height);
277 				delete [] crcList;
278 				crcList = NULL;
279 			}
280 		} else {
281 			delete surface;
282 			surface = NULL;
283 			delete [] crcList;
284 			crcList = NULL;
285 		}
286 	}
287 	if (surface==NULL) {
288 		surface = host->video->createSurface(width,height,bpp);
289 	}
290 	if (crcList==NULL) {
291 		int crcWidth = surface->getDirtyWidth();
292 		int crcHeight = surface->getDirtyHeight();
293 		crcList = new Uint32[crcWidth*crcHeight];
294 		memset(crcList, 0, sizeof(Uint32)*crcWidth*crcHeight);
295 	}
296 
297 	/* Refresh surface palette if switching from true color to bitplane mode */
298 	if ((prevVidelBpp == 16) && (bpp != 16))
299 		updatePalette = true;
300 
301 	prevVidelWidth = width;
302 	prevVidelHeight = height;
303 	prevVidelBpp = bpp;
304 
305 	refreshScreen();
306 
307 	if (bx_options.opengl.enabled && bx_options.autozoom.enabled) {
308 		surface->setParam(HostSurface::SURF_DRAW, HostSurface::DRAW_RESCALE);
309 	}
310 
311 	return surface;
312 }
313 
refreshPalette(void)314 void VIDEL::refreshPalette(void)
315 {
316 	SDL_Color palette[256];
317 	int i, c, numColors = useStPalette() ? 16 : 256;
318 
319 	if (numColors == 16) {
320 		/* STe palette registers, 4 bits/component */
321 		for (i=0; i<16; i++) {
322 			int color = STE_COLORS(i);
323 
324 			c = (color>>8) & 0x0f;
325 			c = ((c & 7)<<1)|((c & 1)>>3);
326 			palette[i].r = (c<<4)|c;
327 			c = (color>>4) & 0x0f;
328 			c = ((c & 7)<<1)|((c & 1)>>3);
329 			palette[i].g = (c<<4)|c;
330 			c = color & 0x0f;
331 			c = ((c & 7)<<1)|((c & 1)>>3);
332 			palette[i].b = (c<<4)|c;
333 #if SDL_VERSION_ATLEAST(2, 0, 0)
334 			palette[i].a = WINDOW_ALPHA;
335 #endif
336 		}
337 	} else {
338 		/* Falcon palette registers, 6 bits/component */
339 		for (i=0; i<256; i++) {
340 			int offset = i<<2;
341 
342 			c = F_COLORS(offset) & 0xfc;
343 			c |= (c>>6) & 3;
344 			palette[i].r = c;
345 			c = F_COLORS(offset+1) & 0xfc;
346 			c |= (c>>6) & 3;
347 			palette[i].g = c;
348 			c = F_COLORS(offset+3) & 0xfc;
349 			c |= (c>>6) & 3;
350 			palette[i].b = c;
351 #if SDL_VERSION_ATLEAST(2, 0, 0)
352 			palette[i].a = WINDOW_ALPHA;
353 #endif
354 		}
355 	}
356 
357 	surface->setPalette(palette, 0, numColors);
358 }
359 
forceRefresh(void)360 void VIDEL::forceRefresh(void)
361 {
362 	if (!surface) {
363 		return;
364 	}
365 
366 	surface->setDirtyRect(0,0,
367 		surface->getWidth(), surface->getHeight());
368 }
369 
refreshScreen(void)370 void VIDEL::refreshScreen(void)
371 {
372 	SDL_Surface *sdl_surf;
373 
374 	if (!surface) {
375 		return;
376 	}
377 	sdl_surf = surface->getSdlSurface();
378 	if (!sdl_surf) {
379 		return;
380 	}
381 
382 	if (updatePalette) {
383 		refreshPalette();
384 		updatePalette = false;
385 		surface->setDirtyRect(0,0,surface->getWidth(),surface->getHeight());
386 	}
387 
388 	int videlBpp = getBpp();
389 
390 	int lineoffset = handleReadW(HW + 0x0e);
391 	int linewidth = handleReadW(HW + 0x10) & 0x03ff; // 10 bits
392 	int hscroll = handleReadW(HW + 0x64) & 15;
393 	if (hscroll) {
394 		lineoffset += videlBpp;
395 	}
396 
397 	Uint16 *src = (uint16 *) Atari2HostAddr(getVramAddress());
398 	int src_pitch = linewidth + lineoffset;
399 	int dst_pitch = sdl_surf->pitch;
400 	int x,y;
401 
402 	Uint8 *dirtyRects = surface->getDirtyRects();
403 	if (!dirtyRects) {
404 		return;
405 	}
406 
407 	int dirty_w = surface->getDirtyWidth();
408 	int dirty_h = surface->getDirtyHeight();
409 
410 	if (videlBpp==16) {
411 		Uint16 *dst = (Uint16 *) sdl_surf->pixels;
412 		for (y=0; y<dirty_h ;y++) {
413 			Uint16 *src_line = src;
414 			Uint16 *dst_line = dst;
415 			/* Atari screen may not have a multiple of 16 lines */
416 			int num_lines = surface->getHeight() - (y<<4);
417 			if (num_lines>16) {
418 				num_lines=16;
419 			}
420 			for (x=0; x<dirty_w; x++) {
421 				Uint32 newCrc = calc_adler((Uint8 *)src_line, 2*16, num_lines, src_pitch<<1);
422 				if (newCrc != crcList[y*dirty_w+x]) {
423 					crcList[y*dirty_w+x] = newCrc;
424 					dirtyRects[y * dirty_w + x] = 1;
425 				}
426 				if (dirtyRects[y * dirty_w + x]) {
427 					/* Convert 16x16 block */
428 					int i,j;
429 					Uint16 *src_line1 = src_line;
430 					Uint16 *dst_line1 = dst_line;
431 					for (j=0;j<num_lines;j++) {
432 						Uint16 *src_col = src_line1;
433 						Uint16 *dst_col = dst_line1;
434 						for (i=0;i<16;i++) {
435 							Uint16 pixel = *src_col++;
436 							*dst_col++ = SDL_SwapBE16(pixel);
437 						}
438 						src_line1 += src_pitch;
439 						dst_line1 += dst_pitch>>1;
440 					}
441 				}
442 				src_line += 16;
443 				dst_line += 16;
444 			}
445 			src += src_pitch * 16;
446 			dst += (dst_pitch>>1) * 16;
447 		}
448 	} else {
449 		Uint8 *dst = (Uint8 *) sdl_surf->pixels;
450 		for (y=0; y<dirty_h;y++) {
451 			Uint16 *src_line = src;
452 			Uint8 *dst_line = dst;
453 			/* Atari screen may not have a multiple of 16 lines */
454 			int num_lines = surface->getHeight() - (y<<4);
455 			if (num_lines>16) {
456 				num_lines=16;
457 			}
458 			for (x=0; x<dirty_w; x++) {
459 				Uint32 newCrc = calc_adler((Uint8 *)src_line, 2*videlBpp, num_lines, src_pitch<<1);
460 				if (newCrc != crcList[y*dirty_w+x]) {
461 					crcList[y*dirty_w+x] = newCrc;
462 					dirtyRects[y * dirty_w + x] = 1;
463 				}
464 				if (dirtyRects[y * dirty_w + x]) {
465 					/* Convert 16x16 block */
466 					int j;
467 					Uint16 *src_line1 = src_line;
468 					Uint8 *dst_line1 = dst_line;
469 					for (j=0;j<num_lines;j++) {
470 						HostScreen::bitplaneToChunky(src_line1, videlBpp, dst_line1, hscroll);
471 						src_line1 += src_pitch;
472 						dst_line1 += dst_pitch;
473 					}
474 				}
475 				src_line += videlBpp;
476 				dst_line += 16;
477 			}
478 			src += src_pitch * 16;
479 			dst += dst_pitch * 16;
480 		}
481 	}
482 }
483