ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/video.cpp
Revision: 1.16
Committed: 2008-06-25T02:52:22Z (15 years, 10 months ago) by asvitkine
Branch: MAIN
Changes since 1.15: +102 -12 lines
Log Message:
[patch from Kelvin Delbarre]
Software cursor mode is now supported, although currently the existing hardware
cursor mode is used whenever possible. (Software mode will be used if you are
running with a recent version of SDL's Quartz video driver, since a bug in SDL
1.2.11 and later prevents the hardware cursor from working properly with that
driver.)

In hardware cursor mode, the hot-spot is now determined heuristically. Formerly
it could not be determined and was always (1,1), an annoyance for many cursors
other than the arrow.

In hardware cursor mode, the cursor will now be hidden when requested by the
emulated OS (such as when you are typing in a text field).

In hardware cursor mode, some cursor image formats that the code does not handle
correctly will now be rejected, causing the emulated OS to revert temporarily to
software cursor mode. Formerly you would just end up with random garbage for a
cursor. This typically happened for grayscale or color cursors; rejecting images
with rowBytes != 2 eliminates the worst cases.

File Contents

# Content
1 /*
2 * video.cpp - Video/graphics emulation
3 *
4 * SheepShaver (C) 1997-2008 Marc Hellwig and Christian Bauer
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 /*
22 * TODO
23 * - check for supported modes ???
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "sysdeps.h"
30 #include "video.h"
31 #include "video_defs.h"
32 #include "main.h"
33 #include "adb.h"
34 #include "macos_util.h"
35 #include "user_strings.h"
36 #include "version.h"
37 #include "thunks.h"
38
39 #define DEBUG 0
40 #include "debug.h"
41
42
43 // Global variables
44 bool video_activated = false; // Flag: video display activated, mouse and keyboard data valid
45 uint32 screen_base = 0; // Frame buffer base address
46 int cur_mode; // Number of current video mode (index in VModes array)
47 int display_type = DIS_INVALID; // Current display type
48 rgb_color mac_pal[256];
49 uint8 remap_mac_be[256];
50 uint8 MacCursor[68] = {16, 1}; // Mac cursor image
51
52
53 bool keyfile_valid; // Flag: Keyfile is valid, enable full-screen modes
54
55
56 /*
57 * Video mode information (constructed by VideoInit())
58 */
59
60 struct VideoInfo VModes[64];
61
62
63 /*
64 * Driver local variables
65 */
66
67 VidLocals *private_data = NULL; // Pointer to driver local variables (there is only one display, so this is ok)
68
69 static long save_conf_id = APPLE_W_640x480;
70 static long save_conf_mode = APPLE_8_BIT;
71
72
73 // Function pointers of imported functions
74 typedef int16 (*iocic_ptr)(void *, int16);
75 static uint32 iocic_tvect = 0;
76 static inline int16 IOCommandIsComplete(uintptr arg1, int16 arg2)
77 {
78 return (int16)CallMacOS2(iocic_ptr, iocic_tvect, (void *)arg1, arg2);
79 }
80 typedef int16 (*vslnewis_ptr)(void *, uint32, uint32 *);
81 static uint32 vslnewis_tvect = 0;
82 static inline int16 VSLNewInterruptService(uintptr arg1, uint32 arg2, uintptr arg3)
83 {
84 return (int16)CallMacOS3(vslnewis_ptr, vslnewis_tvect, (void *)arg1, arg2, (uint32 *)arg3);
85 }
86 typedef int16 (*vsldisposeis_ptr)(uint32);
87 static uint32 vsldisposeis_tvect = 0;
88 static inline int16 VSLDisposeInterruptService(uint32 arg1)
89 {
90 return (int16)CallMacOS1(vsldisposeis_ptr, vsldisposeis_tvect, arg1);
91 }
92 typedef int16 (*vsldois_ptr)(uint32);
93 static uint32 vsldois_tvect = 0;
94 int16 VSLDoInterruptService(uint32 arg1)
95 {
96 return (int16)CallMacOS1(vsldois_ptr, vsldois_tvect, arg1);
97 }
98 typedef void (*nqdmisc_ptr)(uint32, void *);
99 static uint32 nqdmisc_tvect = 0;
100 void NQDMisc(uint32 arg1, uintptr arg2)
101 {
102 CallMacOS2(nqdmisc_ptr, nqdmisc_tvect, arg1, (void *)arg2);
103 }
104
105
106 // Prototypes
107 static int16 set_gamma(VidLocals *csSave, uint32 gamma);
108
109
110 /*
111 * Tell whether window/screen is activated or not (for mouse/keyboard polling)
112 */
113
114 bool VideoActivated(void)
115 {
116 return video_activated;
117 }
118
119
120 /*
121 * Create RGB snapshot of current screen
122 */
123
124 bool VideoSnapshot(int xsize, int ysize, uint8 *p)
125 {
126 if (display_type == DIS_WINDOW) {
127 uint8 *screen = (uint8 *)private_data->saveBaseAddr;
128 uint32 row_bytes = VModes[cur_mode].viRowBytes;
129 uint32 y2size = VModes[cur_mode].viYsize;
130 uint32 x2size = VModes[cur_mode].viXsize;
131 for (int j=0;j<ysize;j++) {
132 for (int i=0;i<xsize;i++) {
133 *p++ = mac_pal[screen[uint32(float(j)*float(y2size)/float(ysize))*row_bytes+uint32(float(i)*float(x2size)/float(xsize))]].red;
134 *p++ = mac_pal[screen[uint32(float(j)*float(y2size)/float(ysize))*row_bytes+uint32(float(i)*float(x2size)/float(xsize))]].green;
135 *p++ = mac_pal[screen[uint32(float(j)*float(y2size)/float(ysize))*row_bytes+uint32(float(i)*float(x2size)/float(xsize))]].blue;
136 }
137 }
138 return true;
139 }
140 return false;
141 }
142
143
144 /*
145 * Determine whether we should use the hardware or software cursor, and return true for the former, false for the latter.
146 * Currently we use the hardware cursor if we can, but perhaps this can be made a preference someday.
147 */
148
149 static bool UseHardwareCursor(void)
150 {
151 return video_can_change_cursor();
152 }
153
154
155 /*
156 * Video driver open routine
157 */
158
159 static int16 VideoOpen(uint32 pb, VidLocals *csSave)
160 {
161 D(bug("Video Open\n"));
162
163 // Set up VidLocals
164 csSave->saveBaseAddr = screen_base;
165 csSave->saveData = VModes[cur_mode].viAppleID;// First mode ...
166 csSave->saveMode = VModes[cur_mode].viAppleMode;
167 csSave->savePage = 0;
168 csSave->saveVidParms = 0; // Add the right table
169 csSave->luminanceMapping = false;
170 csSave->cursorHardware = UseHardwareCursor();
171 csSave->cursorX = 0;
172 csSave->cursorY = 0;
173 csSave->cursorVisible = 0;
174 csSave->cursorSet = 0;
175 csSave->cursorHotFlag = false;
176 csSave->cursorHotX = 0;
177 csSave->cursorHotY = 0;
178
179 // Find and set default gamma table
180 csSave->gammaTable = 0;
181 csSave->maxGammaTableSize = 0;
182 set_gamma(csSave, 0);
183
184 // Install and activate interrupt service
185 SheepVar32 theServiceID = 0;
186 VSLNewInterruptService(csSave->regEntryID, FOURCC('v','b','l',' '), theServiceID.addr());
187 csSave->vslServiceID = theServiceID.value();
188 D(bug(" Interrupt ServiceID %08lx\n", csSave->vslServiceID));
189 csSave->interruptsEnabled = true;
190
191 return noErr;
192 }
193
194
195 /*
196 * Video driver control routine
197 */
198
199 static bool allocate_gamma_table(VidLocals *csSave, uint32 size)
200 {
201 if (size > csSave->maxGammaTableSize) {
202 if (csSave->gammaTable) {
203 Mac_sysfree(csSave->gammaTable);
204 csSave->gammaTable = 0;
205 csSave->maxGammaTableSize = 0;
206 }
207 if ((csSave->gammaTable = Mac_sysalloc(size)) == 0)
208 return false;
209 csSave->maxGammaTableSize = size;
210 }
211 return true;
212 }
213
214 static int16 set_gamma(VidLocals *csSave, uint32 gamma)
215 {
216 return paramErr;
217
218 if (gamma == 0) { // Build linear ramp, 256 entries
219
220 // Allocate new table, if necessary
221 if (!allocate_gamma_table(csSave, SIZEOF_GammaTbl + 256))
222 return memFullErr;
223
224 // Initialize header
225 WriteMacInt16(csSave->gammaTable + gVersion, 0); // A version 0 style of the GammaTbl structure
226 WriteMacInt16(csSave->gammaTable + gType, 0); // Frame buffer hardware invariant
227 WriteMacInt16(csSave->gammaTable + gFormulaSize, 0); // No formula data, just correction data
228 WriteMacInt16(csSave->gammaTable + gChanCnt, 1); // Apply same correction to Red, Green, & Blue
229 WriteMacInt16(csSave->gammaTable + gDataCnt, 256); // gDataCnt == 2^^gDataWidth
230 WriteMacInt16(csSave->gammaTable + gDataWidth, 8); // 8 bits of significant data per entry
231
232 // Build the linear ramp
233 uint32 p = csSave->gammaTable + gFormulaData;
234 for (int i=0; i<256; i++)
235 WriteMacInt8(p + i, i);
236
237 } else { // User-supplied gamma table
238
239 // Validate header
240 if (ReadMacInt16(gamma + gVersion) != 0)
241 return paramErr;
242 if (ReadMacInt16(gamma + gType) != 0)
243 return paramErr;
244 int chan_cnt = ReadMacInt16(gamma + gChanCnt);
245 if (chan_cnt != 1 && chan_cnt != 3)
246 return paramErr;
247 int data_width = ReadMacInt16(gamma + gDataWidth);
248 if (data_width > 8)
249 return paramErr;
250 int data_cnt = ReadMacInt16(gamma + gDataWidth);
251 if (data_cnt != (1 << data_width))
252 return paramErr;
253
254 // Allocate new table, if necessary
255 int size = SIZEOF_GammaTbl + ReadMacInt16(gamma + gFormulaSize) + chan_cnt * data_cnt;
256 if (!allocate_gamma_table(csSave, size))
257 return memFullErr;
258
259 // Copy table
260 Mac2Mac_memcpy(csSave->gammaTable, gamma, size);
261 }
262 return noErr;
263 }
264
265 static int16 VideoControl(uint32 pb, VidLocals *csSave)
266 {
267 int16 code = ReadMacInt16(pb + csCode);
268 D(bug("VideoControl %d: ", code));
269 uint32 param = ReadMacInt32(pb + csParam);
270 switch (code) {
271
272 case cscReset: // VidReset
273 D(bug("VidReset\n"));
274 return controlErr;
275
276 case cscKillIO: // VidKillIO
277 D(bug("VidKillIO\n"));
278 return controlErr;
279
280 case cscSetMode: // SetVidMode
281 D(bug("SetVidMode\n"));
282 D(bug("mode:%04x page:%04x \n", ReadMacInt16(param + csMode),
283 ReadMacInt16(param + csPage)));
284 WriteMacInt32(param + csData, csSave->saveData);
285 return video_mode_change(csSave, param);
286
287 case cscSetEntries: { // SetEntries
288 D(bug("SetEntries\n"));
289 if (VModes[cur_mode].viAppleMode > APPLE_8_BIT) return controlErr;
290 uint32 s_pal = ReadMacInt32(param + csTable);
291 uint16 start = ReadMacInt16(param + csStart);
292 uint16 count = ReadMacInt16(param + csCount);
293 if (s_pal == 0 || count > 256) return controlErr;
294
295 // Preparations for gamma correction
296 bool do_gamma = false;
297 uint8 *red_gamma = NULL;
298 uint8 *green_gamma = NULL;
299 uint8 *blue_gamma = NULL;
300 int gamma_data_width = 0;
301 if (csSave->gammaTable) {
302 #ifdef __BEOS__
303 // Windows are gamma-corrected by BeOS
304 const bool can_do_gamma = (display_type == DIS_SCREEN);
305 #else
306 const bool can_do_gamma = true;
307 #endif
308 if (can_do_gamma) {
309 uint32 gamma_table = csSave->gammaTable;
310 red_gamma = Mac2HostAddr(gamma_table + gFormulaData + ReadMacInt16(gamma_table + gFormulaSize));
311 int chan_cnt = ReadMacInt16(gamma_table + gChanCnt);
312 if (chan_cnt == 1)
313 green_gamma = blue_gamma = red_gamma;
314 else {
315 int ofs = ReadMacInt16(gamma_table + gDataCnt);
316 green_gamma = red_gamma + ofs;
317 blue_gamma = green_gamma + ofs;
318 }
319 gamma_data_width = ReadMacInt16(gamma_table + gDataWidth);
320 do_gamma = true;
321 }
322 }
323
324 // Set palette
325 rgb_color *d_pal;
326 if (start == 0xffff) { // Indexed
327 for (int i=0; i<=count; i++) {
328 d_pal = mac_pal + (ReadMacInt16(s_pal + csValue) & 0xff);
329 uint8 red = (uint16)ReadMacInt16(s_pal + csRed) >> 8;
330 uint8 green = (uint16)ReadMacInt16(s_pal + csGreen) >> 8;
331 uint8 blue = (uint16)ReadMacInt16(s_pal + csBlue) >> 8;
332 if (csSave->luminanceMapping)
333 red = green = blue = (red * 0x4ccc + green * 0x970a + blue * 0x1c29) >> 16;
334 if (do_gamma) {
335 red = red_gamma[red >> (8 - gamma_data_width)];
336 green = green_gamma[green >> (8 - gamma_data_width)];
337 blue = blue_gamma[blue >> (8 - gamma_data_width)];
338 }
339 (*d_pal).red = red;
340 (*d_pal).green = green;
341 (*d_pal).blue = blue;
342 s_pal += 8;
343 }
344 } else { // Sequential
345 d_pal = mac_pal + start;
346 for (int i=0; i<=count; i++) {
347 uint8 red = (uint16)ReadMacInt16(s_pal + csRed) >> 8;
348 uint8 green = (uint16)ReadMacInt16(s_pal + csGreen) >> 8;
349 uint8 blue = (uint16)ReadMacInt16(s_pal + csBlue) >> 8;
350 if (csSave->luminanceMapping)
351 red = green = blue = (red * 0x4ccc + green * 0x970a + blue * 0x1c29) >> 16;
352 if (do_gamma) {
353 red = red_gamma[red >> (8 - gamma_data_width)];
354 green = green_gamma[green >> (8 - gamma_data_width)];
355 blue = blue_gamma[blue >> (8 - gamma_data_width)];
356 }
357 (*d_pal).red = red;
358 (*d_pal).green = green;
359 (*d_pal).blue = blue;
360 d_pal++;
361 s_pal += 8;
362 }
363 }
364 video_set_palette();
365 return noErr;
366 }
367
368 case cscSetGamma: { // SetGamma
369 uint32 user_table = ReadMacInt32(param + csGTable);
370 D(bug("SetGamma %08x\n", user_table));
371 return set_gamma(csSave, ReadMacInt32(user_table));
372 }
373
374 case cscGrayPage: { // GrayPage
375 D(bug("GrayPage %d\n", ReadMacInt16(param + csPage)));
376 if (ReadMacInt16(param + csPage))
377 return paramErr;
378
379 uint32 pattern[6] = {
380 0xaaaaaaaa, // 1 bpp
381 0xcccccccc, // 2 bpp
382 0xf0f0f0f0, // 4 bpp
383 0xff00ff00, // 8 bpp
384 0xffff0000, // 16 bpp
385 0xffffffff // 32 bpp
386 };
387 uint32 p = csSave->saveBaseAddr;
388 uint32 pat = pattern[VModes[cur_mode].viAppleMode - APPLE_1_BIT];
389 bool invert = (VModes[cur_mode].viAppleMode == APPLE_32_BIT);
390 for (uint32 y=0; y<VModes[cur_mode].viYsize; y++) {
391 for (uint32 x=0; x<VModes[cur_mode].viRowBytes; x+=4) {
392 WriteMacInt32(p + x, pat);
393 if (invert)
394 pat = ~pat;
395 }
396 p += VModes[cur_mode].viRowBytes;
397 pat = ~pat;
398 }
399 return noErr;
400 }
401
402 case cscSetGray: // SetGray
403 D(bug("SetGray %02x\n", ReadMacInt8(param)));
404 csSave->luminanceMapping = ReadMacInt8(param);
405 return noErr;
406
407 case cscSetInterrupt: // SetInterrupt
408 D(bug("SetInterrupt\n"));
409 csSave->interruptsEnabled = !ReadMacInt8(param);
410 return noErr;
411
412 case cscDirectSetEntries: // DirectSetEntries
413 D(bug("DirectSetEntries\n"));
414 return controlErr;
415
416 case cscSetDefaultMode: // SetDefaultMode
417 D(bug("SetDefaultMode\n"));
418 return controlErr;
419
420 case cscSwitchMode:
421 D(bug("cscSwitchMode (Display Manager support) \nMode:%02x ID:%04x Page:%d\n",
422 ReadMacInt16(param + csMode), ReadMacInt32(param + csData), ReadMacInt16(param + csPage)));
423 return video_mode_change(csSave, param);
424
425 case cscSavePreferredConfiguration:
426 D(bug("SavePreferredConfiguration\n"));
427 save_conf_id = ReadMacInt32(param + csData);
428 save_conf_mode = ReadMacInt16(param + csMode);
429 return noErr;
430
431 case cscSetHardwareCursor: {
432 // D(bug("SetHardwareCursor\n"));
433
434 if (!csSave->cursorHardware)
435 return controlErr;
436
437 csSave->cursorSet = false;
438 bool changed = false;
439
440 // Image
441 uint32 cursor = ReadMacInt32(param); // Pointer to CursorImage
442 uint32 pmhandle = ReadMacInt32(cursor + ciCursorPixMap);
443 if (pmhandle == 0 || ReadMacInt32(pmhandle) == 0)
444 return controlErr;
445 uint32 pixmap = ReadMacInt32(pmhandle);
446
447 // XXX: only certain image formats are handled properly at the moment
448 uint16 rowBytes = ReadMacInt16(pixmap + 4) & 0x7FFF;
449 if (rowBytes != 2)
450 return controlErr;
451
452 // Mask
453 uint32 bmhandle = ReadMacInt32(cursor + ciCursorBitMask);
454 if (bmhandle == 0 || ReadMacInt32(bmhandle) == 0)
455 return controlErr;
456 uint32 bitmap = ReadMacInt32(bmhandle);
457
458 // Get cursor data even on a screen, to set the right cursor image when switching back to a window.
459 // Hotspot is stale, but will be fixed by the next call to DrawHardwareCursor, which is likely to
460 // occur immediately hereafter.
461
462 if (memcmp(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32)) {
463 memcpy(MacCursor + 4, Mac2HostAddr(ReadMacInt32(pixmap)), 32);
464 changed = true;
465 }
466 if (memcmp(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32)) {
467 memcpy(MacCursor + 4 + 32, Mac2HostAddr(ReadMacInt32(bitmap)), 32);
468 changed = true;
469 }
470
471 // Set new cursor image
472 if (!video_can_change_cursor())
473 return controlErr;
474 if (changed)
475 video_set_cursor();
476
477 csSave->cursorSet = true;
478 csSave->cursorHotFlag = true;
479 return noErr;
480 }
481
482 case cscDrawHardwareCursor: {
483 // D(bug("DrawHardwareCursor\n"));
484
485 if (!csSave->cursorHardware)
486 return controlErr;
487
488 int32 oldX = csSave->cursorX;
489 int32 oldY = csSave->cursorY;
490 uint32 oldVisible = csSave->cursorVisible;
491
492 csSave->cursorX = ReadMacInt32(param + csCursorX);
493 csSave->cursorY = ReadMacInt32(param + csCursorY);
494 csSave->cursorVisible = ReadMacInt32(param + csCursorVisible);
495 bool changed = (csSave->cursorVisible != oldVisible);
496
497 // If this is the first DrawHardwareCursor call since the cursor was last set (via SetHardwareCursor),
498 // attempt to set an appropriate cursor hotspot. SetHardwareCursor itself does not know what the
499 // hotspot should be; it knows only the cursor image and mask. The hotspot is known only to the caller,
500 // and we have to try to infer it here. The usual sequence of calls when changing the cursor is:
501 //
502 // DrawHardwareCursor with (oldX, oldY, invisible)
503 // SetHardwareCursor with (cursor)
504 // DrawHardwareCursor with (newX, newY, visible)
505 //
506 // The key thing to note is that the sequence is intended not to change the current screen pixel location
507 // indicated by the hotspot. Thus, the difference between (newX, newY) and (oldX, oldY) reflects precisely
508 // the difference between the old cursor hotspot and the new one. For example, if you change from a
509 // cursor whose hotspot is (1, 1) to one whose hotspot is (7, 4), then you must adjust the cursor position
510 // by (-6, -3) in order for the same screen pixel to remain under the new hotspot.
511 //
512 // Alas, on rare occasions this heuristic can fail, and if you did nothing else you could even get stuck
513 // with the wrong hotspot from then on. To address that possibility, we force the hotspot to (1, 1)
514 // whenever the cursor being drawn is the standard arrow. Thus, while it is very unlikely that you will
515 // ever have the wrong hotspot, if you do, it is easy to recover.
516
517 if (csSave->cursorHotFlag) {
518 csSave->cursorHotFlag = false;
519 D(bug("old hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY));
520
521 static uint8 arrow[] = {
522 0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00, 0x78, 0x00, 0x7C, 0x00, 0x7E, 0x00, 0x7F, 0x00,
523 0x7F, 0x80, 0x7C, 0x00, 0x6C, 0x00, 0x46, 0x00, 0x06, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
524 };
525 if (memcmp(MacCursor + 4, arrow, 32) == 0) {
526 csSave->cursorHotX = 1;
527 csSave->cursorHotY = 1;
528 } else if (csSave->cursorX != oldX || csSave->cursorY != oldY) {
529 int32 hotX = csSave->cursorHotX + (oldX - csSave->cursorX);
530 int32 hotY = csSave->cursorHotY + (oldY - csSave->cursorY);
531
532 if (0 <= hotX && hotX <= 15 && 0 <= hotY && hotY <= 15) {
533 csSave->cursorHotX = hotX;
534 csSave->cursorHotY = hotY;
535 }
536 }
537 if (MacCursor[2] != csSave->cursorHotX || MacCursor[3] != csSave->cursorHotY) {
538 MacCursor[2] = csSave->cursorHotX;
539 MacCursor[3] = csSave->cursorHotY;
540 changed = true;
541 }
542 D(bug("new hotspot (%d, %d)\n", csSave->cursorHotX, csSave->cursorHotY));
543 }
544
545 if (changed && video_can_change_cursor())
546 video_set_cursor();
547
548 return noErr;
549 }
550
551 case 43: { // Driver Gestalt
552 uint32 sel = ReadMacInt32(pb + csParam);
553 D(bug(" driver gestalt %c%c%c%c\n", sel >> 24, sel >> 16, sel >> 8, sel));
554 switch (sel) {
555 case FOURCC('v','e','r','s'):
556 WriteMacInt32(pb + csParam + 4, 0x01008000);
557 break;
558 case FOURCC('i','n','t','f'):
559 WriteMacInt32(pb + csParam + 4, FOURCC('c','a','r','d'));
560 break;
561 case FOURCC('s','y','n','c'):
562 WriteMacInt32(pb + csParam + 4, 0x01000000);
563 break;
564 default:
565 return statusErr;
566 };
567 return noErr;
568 }
569
570 default:
571 D(bug(" unknown control code %d\n", code));
572 return controlErr;
573 }
574 }
575
576
577 /*
578 * Video driver status routine
579 */
580
581 // Search for given AppleID in mode table
582 static bool has_mode(uint32 id)
583 {
584 VideoInfo *p = VModes;
585 while (p->viType != DIS_INVALID) {
586 if (p->viAppleID == id)
587 return true;
588 p++;
589 }
590 return false;
591 }
592
593 // Find maximum depth for given AppleID
594 static uint32 max_depth(uint32 id)
595 {
596 uint32 max = APPLE_1_BIT;
597 VideoInfo *p = VModes;
598 while (p->viType != DIS_INVALID) {
599 if (p->viAppleID == id && p->viAppleMode > max)
600 max = p->viAppleMode;
601 p++;
602 }
603 return max;
604 }
605
606 // Get X/Y size of specified resolution
607 static void get_size_of_resolution(int id, uint32 &x, uint32 &y)
608 {
609 VideoInfo *p = VModes;
610 while (p->viType != DIS_INVALID) {
611 if (p->viAppleID == id) {
612 x = p->viXsize;
613 y = p->viYsize;
614 return;
615 }
616 p++;
617 }
618 x = y = 0;
619 }
620
621 static int16 VideoStatus(uint32 pb, VidLocals *csSave)
622 {
623 int16 code = ReadMacInt16(pb + csCode);
624 D(bug("VideoStatus %d: ", code));
625 uint32 param = ReadMacInt32(pb + csParam);
626 switch (code) {
627
628 case cscGetMode: // GetMode
629 D(bug("GetMode\n"));
630 WriteMacInt32(param + csBaseAddr, csSave->saveBaseAddr);
631 WriteMacInt16(param + csMode, csSave->saveMode);
632 WriteMacInt16(param + csPage, csSave->savePage);
633 D(bug("return: mode:%04x page:%04x ", ReadMacInt16(param + csMode),
634 ReadMacInt16(param + csPage)));
635 D(bug("base adress %08lx\n", ReadMacInt32(param + csBaseAddr)));
636 return noErr;
637
638 case cscGetEntries: { // GetEntries
639 D(bug("GetEntries\n"));
640 uint32 d_pal = ReadMacInt32(param + csTable);
641 uint16 start = ReadMacInt16(param + csStart);
642 uint16 count = ReadMacInt16(param + csCount);
643 rgb_color *s_pal;
644 if ((VModes[cur_mode].viAppleMode == APPLE_32_BIT)||
645 (VModes[cur_mode].viAppleMode == APPLE_16_BIT)) {
646 D(bug("ERROR: GetEntries in direct mode \n"));
647 return statusErr;
648 }
649
650 if (start == 0xffff) { // Indexed
651 for (uint16 i=0;i<count;i++) {
652 s_pal = mac_pal + (ReadMacInt16(d_pal + csValue) & 0xff);
653 uint8 red = (*s_pal).red;
654 uint8 green = (*s_pal).green;
655 uint8 blue = (*s_pal).blue;
656 WriteMacInt16(d_pal + csRed, red * 0x0101);
657 WriteMacInt16(d_pal + csGreen, green * 0x0101);
658 WriteMacInt16(d_pal + csBlue, blue * 0x0101);
659 d_pal += 8;
660 }
661 } else { // Sequential
662 if (start + count > 255)
663 return paramErr;
664 s_pal = mac_pal + start;
665 for (uint16 i=0;i<count;i++) {
666 uint8 red = (*s_pal).red;
667 uint8 green = (*s_pal).green;
668 uint8 blue = (*s_pal).blue;
669 s_pal++;
670 WriteMacInt16(d_pal + csRed, red * 0x0101);
671 WriteMacInt16(d_pal + csGreen, green * 0x0101);
672 WriteMacInt16(d_pal + csBlue, blue * 0x0101);
673 d_pal += 8;
674 }
675 };
676 return noErr;
677 }
678
679 case cscGetPageCnt: // GetPage
680 D(bug("GetPage\n"));
681 WriteMacInt16(param + csPage, 1);
682 return noErr;
683
684 case cscGetPageBase: // GetPageBase
685 D(bug("GetPageBase\n"));
686 WriteMacInt32(param + csBaseAddr, csSave->saveBaseAddr);
687 return noErr;
688
689 case cscGetGray: // GetGray
690 D(bug("GetGray\n"));
691 WriteMacInt8(param, csSave->luminanceMapping ? 1 : 0);
692 return noErr;
693
694 case cscGetInterrupt: // GetInterrupt
695 D(bug("GetInterrupt\n"));
696 WriteMacInt8(param, csSave->interruptsEnabled ? 0 : 1);
697 return noErr;
698
699 case cscGetGamma: // GetGamma
700 D(bug("GetGamma\n"));
701 WriteMacInt32(param, (uint32)csSave->gammaTable);
702 return statusErr;
703
704 case cscGetDefaultMode: // GetDefaultMode
705 D(bug("GetDefaultMode\n"));
706 return statusErr;
707
708 case cscGetCurMode: // GetCurMode
709 D(bug("GetCurMode\n"));
710 WriteMacInt16(param + csMode, csSave->saveMode);
711 WriteMacInt32(param + csData, csSave->saveData);
712 WriteMacInt16(param + csPage, csSave->savePage);
713 WriteMacInt32(param + csBaseAddr, csSave->saveBaseAddr);
714
715 D(bug("return: mode:%04x ID:%08lx page:%04x ", ReadMacInt16(param + csMode),
716 ReadMacInt32(param + csData), ReadMacInt16(param + csPage)));
717 D(bug("base adress %08lx\n", ReadMacInt32(param + csBaseAddr)));
718 return noErr;
719
720 case cscGetConnection: // GetConnection
721 D(bug("GetConnection\n"));
722 WriteMacInt16(param + csDisplayType, kMultiModeCRT3Connect);
723 WriteMacInt8(param + csConnectTaggedType, 6);
724 WriteMacInt8(param + csConnectTaggedData, 0x23);
725 WriteMacInt32(param + csConnectFlags, (1<<kAllModesValid)|(1<<kAllModesSafe));
726 WriteMacInt32(param + csDisplayComponent, 0);
727 return noErr;
728
729 case cscGetModeBaseAddress:
730 D(bug("GetModeBaseAddress (obsolete !) \n"));
731 return statusErr;
732
733 case cscGetPreferredConfiguration:
734 D(bug("GetPreferredConfiguration \n"));
735 WriteMacInt16(param + csMode, save_conf_mode);
736 WriteMacInt32(param + csData, save_conf_id);
737 return noErr;
738
739 case cscGetNextResolution: {
740 D(bug("GetNextResolution \n"));
741 int work_id = ReadMacInt32(param + csPreviousDisplayModeID);
742 switch (work_id) {
743 case kDisplayModeIDCurrent:
744 work_id = csSave->saveData;
745 break;
746 case kDisplayModeIDFindFirstResolution:
747 work_id = APPLE_ID_MIN;
748 while (!has_mode(work_id))
749 work_id ++;
750 break;
751 default:
752 if (!has_mode(work_id))
753 return paramErr;
754 work_id++;
755 while (!has_mode(work_id)) {
756 work_id++;
757 if (work_id > APPLE_ID_MAX) {
758 WriteMacInt32(param + csRIDisplayModeID, kDisplayModeIDNoMoreResolutions);
759 return noErr;
760 }
761 }
762 break;
763 }
764 WriteMacInt32(param + csRIDisplayModeID, work_id);
765 WriteMacInt16(param + csMaxDepthMode, max_depth(work_id));
766 switch (work_id) {
767 case APPLE_640x480:
768 WriteMacInt32(param + csHorizontalPixels, 640);
769 WriteMacInt32(param + csVerticalLines, 480);
770 WriteMacInt32(param + csRefreshRate, 75<<16);
771 break;
772 case APPLE_W_640x480:
773 WriteMacInt32(param + csHorizontalPixels, 640);
774 WriteMacInt32(param + csVerticalLines, 480);
775 WriteMacInt32(param + csRefreshRate, 60<<16);
776 break;
777 case APPLE_800x600:
778 WriteMacInt32(param + csHorizontalPixels, 800);
779 WriteMacInt32(param + csVerticalLines, 600);
780 WriteMacInt32(param + csRefreshRate, 75<<16);
781 break;
782 case APPLE_W_800x600:
783 WriteMacInt32(param + csHorizontalPixels, 800);
784 WriteMacInt32(param + csVerticalLines, 600);
785 WriteMacInt32(param + csRefreshRate, 60<<16);
786 break;
787 case APPLE_1024x768:
788 WriteMacInt32(param + csHorizontalPixels, 1024);
789 WriteMacInt32(param + csVerticalLines, 768);
790 WriteMacInt32(param + csRefreshRate, 75<<16);
791 break;
792 case APPLE_1152x768:
793 WriteMacInt32(param + csHorizontalPixels, 1152);
794 WriteMacInt32(param + csVerticalLines, 768);
795 WriteMacInt32(param + csRefreshRate, 75<<16);
796 break;
797 case APPLE_1152x900:
798 WriteMacInt32(param + csHorizontalPixels, 1152);
799 WriteMacInt32(param + csVerticalLines, 900);
800 WriteMacInt32(param + csRefreshRate, 75<<16);
801 break;
802 case APPLE_1280x1024:
803 WriteMacInt32(param + csHorizontalPixels, 1280);
804 WriteMacInt32(param + csVerticalLines, 1024);
805 WriteMacInt32(param + csRefreshRate, 75<<16);
806 break;
807 case APPLE_1600x1200:
808 WriteMacInt32(param + csHorizontalPixels, 1600);
809 WriteMacInt32(param + csVerticalLines, 1200);
810 WriteMacInt32(param + csRefreshRate, 75<<16);
811 break;
812 case APPLE_CUSTOM: {
813 uint32 x, y;
814 get_size_of_resolution(work_id, x, y);
815 WriteMacInt32(param + csHorizontalPixels, x);
816 WriteMacInt32(param + csVerticalLines, y);
817 WriteMacInt32(param + csRefreshRate, 75<<16);
818 break;
819 }
820 }
821 return noErr;
822 }
823
824 case cscGetVideoParameters: // GetVideoParameters
825 D(bug("GetVideoParameters ID:%08lx Depth:%04x\n",
826 ReadMacInt32(param + csDisplayModeID),
827 ReadMacInt16(param + csDepthMode)));
828
829 // find right video mode
830 for (int i=0; VModes[i].viType!=DIS_INVALID; i++) {
831 if ((ReadMacInt16(param + csDepthMode) == VModes[i].viAppleMode) &&
832 (ReadMacInt32(param + csDisplayModeID) == VModes[i].viAppleID)) {
833 uint32 vpb = ReadMacInt32(param + csVPBlockPtr);
834 WriteMacInt32(vpb + vpBaseOffset, 0);
835 WriteMacInt16(vpb + vpRowBytes, VModes[i].viRowBytes);
836 WriteMacInt16(vpb + vpBounds, 0);
837 WriteMacInt16(vpb + vpBounds + 2, 0);
838 WriteMacInt16(vpb + vpBounds + 4, VModes[i].viYsize);
839 WriteMacInt16(vpb + vpBounds + 6, VModes[i].viXsize);
840 WriteMacInt16(vpb + vpVersion, 0); // Pixel Map version number
841 WriteMacInt16(vpb + vpPackType, 0);
842 WriteMacInt32(vpb + vpPackSize, 0);
843 WriteMacInt32(vpb + vpHRes, 0x00480000); // horiz res of the device (ppi)
844 WriteMacInt32(vpb + vpVRes, 0x00480000); // vert res of the device (ppi)
845 switch (VModes[i].viAppleMode) {
846 case APPLE_1_BIT:
847 WriteMacInt16(vpb + vpPixelType, 0);
848 WriteMacInt16(vpb + vpPixelSize, 1);
849 WriteMacInt16(vpb + vpCmpCount, 1);
850 WriteMacInt16(vpb + vpCmpSize, 1);
851 WriteMacInt32(param + csDeviceType, 0); // CLUT
852 break;
853 case APPLE_2_BIT:
854 WriteMacInt16(vpb + vpPixelType, 0);
855 WriteMacInt16(vpb + vpPixelSize, 2);
856 WriteMacInt16(vpb + vpCmpCount, 1);
857 WriteMacInt16(vpb + vpCmpSize, 2);
858 WriteMacInt32(param + csDeviceType, 0); // CLUT
859 break;
860 case APPLE_4_BIT:
861 WriteMacInt16(vpb + vpPixelType, 0);
862 WriteMacInt16(vpb + vpPixelSize, 4);
863 WriteMacInt16(vpb + vpCmpCount, 1);
864 WriteMacInt16(vpb + vpCmpSize, 4);
865 WriteMacInt32(param + csDeviceType, 0); // CLUT
866 break;
867 case APPLE_8_BIT:
868 WriteMacInt16(vpb + vpPixelType, 0);
869 WriteMacInt16(vpb + vpPixelSize, 8);
870 WriteMacInt16(vpb + vpCmpCount, 1);
871 WriteMacInt16(vpb + vpCmpSize, 8);
872 WriteMacInt32(param + csDeviceType, 0); // CLUT
873 break;
874 case APPLE_16_BIT:
875 WriteMacInt16(vpb + vpPixelType, 0x10);
876 WriteMacInt16(vpb + vpPixelSize, 16);
877 WriteMacInt16(vpb + vpCmpCount, 3);
878 WriteMacInt16(vpb + vpCmpSize, 5);
879 WriteMacInt32(param + csDeviceType, 2); // DIRECT
880 break;
881 case APPLE_32_BIT:
882 WriteMacInt16(vpb + vpPixelType, 0x10);
883 WriteMacInt16(vpb + vpPixelSize, 32);
884 WriteMacInt16(vpb + vpCmpCount, 3);
885 WriteMacInt16(vpb + vpCmpSize, 8);
886 WriteMacInt32(param + csDeviceType, 2); // DIRECT
887 break;
888 }
889 WriteMacInt32(param + csPageCount, 1);
890 return noErr;
891 }
892 }
893 return paramErr;
894
895 case cscGetModeTiming:
896 D(bug("GetModeTiming mode %08lx\n", ReadMacInt32(param + csTimingMode)));
897 WriteMacInt32(param + csTimingFormat, kDeclROMtables);
898 WriteMacInt32(param + csTimingFlags, (1<<kModeValid)|(1<<kModeSafe)|(1<<kShowModeNow)); // Mode valid, safe, default and shown in Monitors panel
899 for (int i=0; VModes[i].viType!=DIS_INVALID; i++) {
900 if (ReadMacInt32(param + csTimingMode) == VModes[i].viAppleID) {
901 uint32 timing = timingUnknown;
902 uint32 flags = (1<<kModeValid) | (1<<kShowModeNow);
903 switch (VModes[i].viAppleID) {
904 case APPLE_640x480:
905 timing = timingVESA_640x480_75hz;
906 flags |= (1<<kModeSafe);
907 break;
908 case APPLE_W_640x480:
909 timing = timingVESA_640x480_60hz;
910 flags |= (1<<kModeSafe);
911 break;
912 case APPLE_800x600:
913 timing = timingVESA_800x600_75hz;
914 flags |= (1<<kModeSafe);
915 break;
916 case APPLE_W_800x600:
917 timing = timingVESA_800x600_60hz;
918 flags |= (1<<kModeSafe);
919 break;
920 case APPLE_1024x768:
921 timing = timingVESA_1024x768_75hz;
922 break;
923 case APPLE_1152x768:
924 timing = timingApple_1152x870_75hz; // FIXME
925 break;
926 case APPLE_1152x900:
927 timing = timingApple_1152x870_75hz;
928 break;
929 case APPLE_1280x1024:
930 timing = timingVESA_1280x960_75hz;
931 break;
932 case APPLE_1600x1200:
933 timing = timingVESA_1600x1200_75hz;
934 break;
935 default:
936 timing = timingUnknown;
937 break;
938 }
939 WriteMacInt32(param + csTimingData, timing);
940 WriteMacInt32(param + csTimingFlags, flags);
941 return noErr;
942 }
943 }
944 return paramErr;
945
946 case cscSupportsHardwareCursor:
947 D(bug("SupportsHardwareCursor\n"));
948 WriteMacInt32(param, csSave->cursorHardware);
949 return noErr;
950
951 case cscGetHardwareCursorDrawState:
952 D(bug("GetHardwareCursorDrawState\n"));
953
954 if (!csSave->cursorHardware)
955 return statusErr;
956
957 WriteMacInt32(param + csCursorX, csSave->cursorX);
958 WriteMacInt32(param + csCursorY, csSave->cursorY);
959 WriteMacInt32(param + csCursorVisible, csSave->cursorVisible);
960 WriteMacInt32(param + csCursorSet, csSave->cursorSet);
961 return noErr;
962
963 default:
964 D(bug(" unknown status code %d\n", code));
965 return statusErr;
966 }
967 }
968
969
970 /*
971 * Video driver close routine
972 */
973
974 static int16 VideoClose(uint32 pb, VidLocals *csSave)
975 {
976 D(bug("VideoClose\n"));
977
978 // Delete interrupt service
979 csSave->interruptsEnabled = false;
980 VSLDisposeInterruptService(csSave->vslServiceID);
981
982 return noErr;
983 }
984
985
986 /*
987 * Native (PCI) driver entry
988 */
989
990 int16 VideoDoDriverIO(uint32 spaceID, uint32 commandID, uint32 commandContents, uint32 commandCode, uint32 commandKind)
991 {
992 // D(bug("VideoDoDriverIO space %08x, command %08x, contents %08x, code %d, kind %d\n", spaceID, commandID, commandContents, commandCode, commandKind));
993 int16 err = noErr;
994
995 switch (commandCode) {
996 case kInitializeCommand:
997 case kReplaceCommand:
998 if (private_data != NULL) { // Might be left over from a reboot
999 if (private_data->gammaTable)
1000 Mac_sysfree(private_data->gammaTable);
1001 if (private_data->regEntryID)
1002 Mac_sysfree(private_data->regEntryID);
1003 }
1004 delete private_data;
1005
1006 iocic_tvect = FindLibSymbol("\021DriverServicesLib", "\023IOCommandIsComplete");
1007 D(bug("IOCommandIsComplete TVECT at %08lx\n", iocic_tvect));
1008 if (iocic_tvect == 0) {
1009 printf("FATAL: VideoDoDriverIO(): Can't find IOCommandIsComplete()\n");
1010 err = -1;
1011 break;
1012 }
1013 vslnewis_tvect = FindLibSymbol("\020VideoServicesLib", "\026VSLNewInterruptService");
1014 D(bug("VSLNewInterruptService TVECT at %08lx\n", vslnewis_tvect));
1015 if (vslnewis_tvect == 0) {
1016 printf("FATAL: VideoDoDriverIO(): Can't find VSLNewInterruptService()\n");
1017 err = -1;
1018 break;
1019 }
1020 vsldisposeis_tvect = FindLibSymbol("\020VideoServicesLib", "\032VSLDisposeInterruptService");
1021 D(bug("VSLDisposeInterruptService TVECT at %08lx\n", vsldisposeis_tvect));
1022 if (vsldisposeis_tvect == 0) {
1023 printf("FATAL: VideoDoDriverIO(): Can't find VSLDisposeInterruptService()\n");
1024 err = -1;
1025 break;
1026 }
1027 vsldois_tvect = FindLibSymbol("\020VideoServicesLib", "\025VSLDoInterruptService");
1028 D(bug("VSLDoInterruptService TVECT at %08lx\n", vsldois_tvect));
1029 if (vsldois_tvect == 0) {
1030 printf("FATAL: VideoDoDriverIO(): Can't find VSLDoInterruptService()\n");
1031 err = -1;
1032 break;
1033 }
1034 nqdmisc_tvect = FindLibSymbol("\014InterfaceLib", "\007NQDMisc");
1035 D(bug("NQDMisc TVECT at %08lx\n", nqdmisc_tvect));
1036 if (nqdmisc_tvect == 0) {
1037 printf("FATAL: VideoDoDriverIO(): Can't find NQDMisc()\n");
1038 err = -1;
1039 break;
1040 }
1041
1042 private_data = new VidLocals;
1043 private_data->gammaTable = 0;
1044 private_data->regEntryID = Mac_sysalloc(sizeof(RegEntryID));
1045 if (private_data->regEntryID == 0) {
1046 printf("FATAL: VideoDoDriverIO(): Can't allocate service owner\n");
1047 err = -1;
1048 break;
1049 }
1050 Mac2Mac_memcpy(private_data->regEntryID, commandContents + 2, 16); // DriverInitInfo.deviceEntry
1051 private_data->interruptsEnabled = false; // Disable interrupts
1052 break;
1053
1054 case kFinalizeCommand:
1055 case kSupersededCommand:
1056 if (private_data != NULL) {
1057 if (private_data->gammaTable)
1058 Mac_sysfree(private_data->gammaTable);
1059 if (private_data->regEntryID)
1060 Mac_sysfree(private_data->regEntryID);
1061 }
1062 delete private_data;
1063 private_data = NULL;
1064 break;
1065
1066 case kOpenCommand:
1067 err = VideoOpen(commandContents, private_data);
1068 break;
1069
1070 case kCloseCommand:
1071 err = VideoClose(commandContents, private_data);
1072 break;
1073
1074 case kControlCommand:
1075 err = VideoControl(commandContents, private_data);
1076 break;
1077
1078 case kStatusCommand:
1079 err = VideoStatus(commandContents, private_data);
1080 break;
1081
1082 case kReadCommand:
1083 case kWriteCommand:
1084 break;
1085
1086 case kKillIOCommand:
1087 err = abortErr;
1088 break;
1089
1090 default:
1091 err = paramErr;
1092 break;
1093 }
1094
1095 if (commandKind == kImmediateIOCommandKind)
1096 return err;
1097 else
1098 return IOCommandIsComplete(commandID, err);
1099 }