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