ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/video_amiga.cpp
Revision: 1.5
Committed: 2000-07-13T17:45:33Z (23 years, 11 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-13072000
Changes since 1.4: +3 -3 lines
Log Message:
- Picasso 96 is given preference over CyberGfx because of P96's CyberGfx
  emulation

File Contents

# Content
1 /*
2 * video_amiga.cpp - Video/graphics emulation, AmigaOS specific stuff
3 *
4 * Basilisk II (C) 1997-2000 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 #include <exec/types.h>
22 #include <intuition/intuition.h>
23 #include <graphics/rastport.h>
24 #include <graphics/gfx.h>
25 #include <cybergraphx/cybergraphics.h>
26 #include <dos/dostags.h>
27 #include <devices/timer.h>
28 #include <proto/exec.h>
29 #include <proto/dos.h>
30 #include <proto/intuition.h>
31 #include <proto/graphics.h>
32 #include <proto/Picasso96.h>
33 #include <proto/cybergraphics.h>
34
35 #include "sysdeps.h"
36 #include "main.h"
37 #include "adb.h"
38 #include "prefs.h"
39 #include "user_strings.h"
40 #include "video.h"
41
42 #define DEBUG 0
43 #include "debug.h"
44
45
46 // Display types
47 enum {
48 DISPLAY_WINDOW,
49 DISPLAY_PIP,
50 DISPLAY_SCREEN
51 };
52
53 // Global variables
54 static int32 frame_skip;
55 static int display_type = DISPLAY_WINDOW; // See enum above
56 static struct Screen *the_screen = NULL;
57 static struct Window *the_win = NULL;
58 static struct BitMap *the_bitmap = NULL;
59 static LONG black_pen = -1, white_pen = -1;
60 static struct Process *periodic_proc = NULL; // Periodic process
61 static bool is_cgfx = false; // Flag: screen mode is a CyberGfx mode
62 static bool is_p96 = false; // Flag: screen mode is a Picasso96 mode
63
64 extern struct Task *MainTask; // Pointer to main task (from main_amiga.cpp)
65
66
67 // Amiga -> Mac raw keycode translation table
68 static const uint8 keycode2mac[0x80] = {
69 0x0a, 0x12, 0x13, 0x14, 0x15, 0x17, 0x16, 0x1a, // ` 1 2 3 4 5 6 7
70 0x1c, 0x19, 0x1d, 0x1b, 0x18, 0x2a, 0xff, 0x52, // 8 9 0 - = \ inv 0
71 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x10, 0x20, 0x22, // Q W E R T Y U I
72 0x1f, 0x23, 0x21, 0x1e, 0xff, 0x53, 0x54, 0x55, // O P [ ] inv 1 2 3
73 0x00, 0x01, 0x02, 0x03, 0x05, 0x04, 0x26, 0x28, // A S D F G H J K
74 0x25, 0x29, 0x27, 0x2a, 0xff, 0x56, 0x57, 0x58, // L ; ' # inv 4 5 6
75 0x32, 0x06, 0x07, 0x08, 0x09, 0x0b, 0x2d, 0x2e, // < Z X C V B N M
76 0x2b, 0x2f, 0x2c, 0xff, 0x41, 0x59, 0x5b, 0x5c, // , . / inv . 7 8 9
77 0x31, 0x33, 0x30, 0x4c, 0x24, 0x35, 0x75, 0xff, // SPC BSP TAB ENT RET ESC DEL inv
78 0xff, 0xff, 0x4e, 0xff, 0x3e, 0x3d, 0x3c, 0x3b, // inv inv - inv CUP CDN CRT CLF
79 0x7a, 0x78, 0x63, 0x76, 0x60, 0x61, 0x62, 0x64, // F1 F2 F3 F4 F5 F6 F7 F8
80 0x65, 0x6d, 0x47, 0x51, 0x4b, 0x43, 0x45, 0x72, // F9 F10 ( ) / * + HLP
81 0x38, 0x38, 0x39, 0x36, 0x3a, 0x3a, 0x37, 0x37, // SHL SHR CAP CTL ALL ALR AML AMR
82 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // inv inv inv inv inv inv inv inv
83 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // inv inv inv inv inv inv inv inv
84 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // inv inv inv inv inv inv inv inv
85 };
86
87
88 // Prototypes
89 static void periodic_func(void);
90
91
92 /*
93 * Initialization
94 */
95
96 // Open window
97 static bool init_window(int width, int height)
98 {
99 // Set absolute mouse mode
100 ADBSetRelMouseMode(false);
101
102 // Open window
103 the_win = OpenWindowTags(NULL,
104 WA_Left, 0, WA_Top, 0,
105 WA_InnerWidth, width, WA_InnerHeight, height,
106 WA_SimpleRefresh, TRUE,
107 WA_NoCareRefresh, TRUE,
108 WA_Activate, TRUE,
109 WA_RMBTrap, TRUE,
110 WA_ReportMouse, TRUE,
111 WA_DragBar, TRUE,
112 WA_DepthGadget, TRUE,
113 WA_SizeGadget, FALSE,
114 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
115 TAG_END
116 );
117 if (the_win == NULL) {
118 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
119 return false;
120 }
121
122 // Create bitmap ("height + 2" for safety)
123 the_bitmap = AllocBitMap(width, height + 2, 1, BMF_CLEAR, NULL);
124 if (the_bitmap == NULL) {
125 ErrorAlert(GetString(STR_NO_MEM_ERR));
126 return false;
127 }
128
129 // Set VideoMonitor
130 VideoMonitor.mac_frame_base = (uint32)the_bitmap->Planes[0];
131 VideoMonitor.bytes_per_row = the_bitmap->BytesPerRow;
132 VideoMonitor.x = width;
133 VideoMonitor.y = height;
134 VideoMonitor.mode = VMODE_1BIT;
135
136 // Set FgPen and BgPen
137 black_pen = ObtainBestPenA(the_win->WScreen->ViewPort.ColorMap, 0, 0, 0, NULL);
138 white_pen = ObtainBestPenA(the_win->WScreen->ViewPort.ColorMap, 0xffffffff, 0xffffffff, 0xffffffff, NULL);
139 SetAPen(the_win->RPort, black_pen);
140 SetBPen(the_win->RPort, white_pen);
141 SetDrMd(the_win->RPort, JAM2);
142 return true;
143 }
144
145 // Open PIP (requires Picasso96)
146 static bool init_pip(int width, int height)
147 {
148 // Set absolute mouse mode
149 ADBSetRelMouseMode(false);
150
151 // Open window
152 ULONG error = 0;
153 the_win = p96PIP_OpenTags(
154 P96PIP_SourceFormat, RGBFB_R5G5B5,
155 P96PIP_SourceWidth, width,
156 P96PIP_SourceHeight, height,
157 P96PIP_ErrorCode, (ULONG)&error,
158 WA_Left, 0, WA_Top, 0,
159 WA_InnerWidth, width, WA_InnerHeight, height,
160 WA_SimpleRefresh, TRUE,
161 WA_NoCareRefresh, TRUE,
162 WA_Activate, TRUE,
163 WA_RMBTrap, TRUE,
164 WA_ReportMouse, TRUE,
165 WA_DragBar, TRUE,
166 WA_DepthGadget, TRUE,
167 WA_SizeGadget, FALSE,
168 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
169 WA_PubScreenName, (ULONG)"Workbench",
170 TAG_END
171 );
172 if (the_win == NULL || error) {
173 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
174 return false;
175 }
176
177 // Find bitmap
178 p96PIP_GetTags(the_win, P96PIP_SourceBitMap, (ULONG)&the_bitmap, TAG_END);
179
180 // Set VideoMonitor
181 VideoMonitor.mac_frame_base = p96GetBitMapAttr(the_bitmap, P96BMA_MEMORY);
182 VideoMonitor.bytes_per_row = p96GetBitMapAttr(the_bitmap, P96BMA_BYTESPERROW);
183 VideoMonitor.x = width;
184 VideoMonitor.y = height;
185 VideoMonitor.mode = VMODE_16BIT;
186 return true;
187 }
188
189 // Open screen (requires Picasso96/CyberGfx as we need chunky modes)
190 static bool init_screen(ULONG mode_id)
191 {
192 // Set relative mouse mode
193 ADBSetRelMouseMode(true);
194
195 // Check whether the mode is a Picasso96 mode or a CyberGfx mode
196 if (P96Base && p96GetModeIDAttr(mode_id, P96IDA_ISP96))
197 is_p96 = true;
198 else if (CyberGfxBase && IsCyberModeID(mode_id))
199 is_cgfx = true;
200 else {
201 ErrorAlert(GetString(STR_NO_P96_MODE_ERR));
202 return false;
203 }
204
205 uint32 depth;
206 uint32 format;
207
208 // Check if the mode is one we can handle
209 if (is_p96) {
210 depth = p96GetModeIDAttr(mode_id, P96IDA_DEPTH);
211 format = p96GetModeIDAttr(mode_id, P96IDA_RGBFORMAT);
212 } else {
213 depth = GetCyberIDAttr(CYBRIDATTR_DEPTH, mode_id);
214 format = GetCyberIDAttr(CYBRIDATTR_PIXFMT, mode_id);
215 }
216
217 switch (depth) {
218 case 8:
219 VideoMonitor.mode = VMODE_8BIT;
220 break;
221 case 15:
222 case 16:
223 if (format != RGBFB_R5G5B5 && format != PIXFMT_RGB16) {
224 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
225 return false;
226 }
227 VideoMonitor.mode = VMODE_16BIT;
228 break;
229 case 24:
230 case 32:
231 if (format != RGBFB_A8R8G8B8 && format != PIXFMT_ARGB32) {
232 ErrorAlert(GetString(STR_WRONG_SCREEN_FORMAT_ERR));
233 return false;
234 }
235 VideoMonitor.mode = VMODE_32BIT;
236 break;
237 default:
238 ErrorAlert(GetString(STR_WRONG_SCREEN_DEPTH_ERR));
239 return false;
240 }
241
242 // Yes, get width and height
243 uint32 width;
244 uint32 height;
245
246 if (is_p96) {
247 width = p96GetModeIDAttr(mode_id, P96IDA_WIDTH);
248 height = p96GetModeIDAttr(mode_id, P96IDA_HEIGHT);
249 } else {
250 width = GetCyberIDAttr(CYBRIDATTR_WIDTH, mode_id);
251 height = GetCyberIDAttr(CYBRIDATTR_HEIGHT, mode_id);
252 }
253 VideoMonitor.x = width;
254 VideoMonitor.y = height;
255
256 // Open screen
257 if (is_p96) {
258 the_screen = p96OpenScreenTags(
259 P96SA_DisplayID, mode_id,
260 P96SA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
261 P96SA_Quiet, TRUE,
262 P96SA_NoMemory, TRUE,
263 P96SA_NoSprite, TRUE,
264 P96SA_Exclusive, TRUE,
265 TAG_END
266 );
267 } else {
268 the_screen = OpenScreenTags(NULL,
269 SA_DisplayID, mode_id,
270 SA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
271 SA_Quiet, TRUE,
272 SA_Exclusive, TRUE,
273 TAG_END
274 );
275 }
276
277 if (the_screen == NULL) {
278 ErrorAlert(GetString(STR_OPEN_SCREEN_ERR));
279 return false;
280 }
281
282 // Open window
283 the_win = OpenWindowTags(NULL,
284 WA_Left, 0, WA_Top, 0,
285 WA_Width, width, WA_Height, height,
286 WA_NoCareRefresh, TRUE,
287 WA_Borderless, TRUE,
288 WA_Activate, TRUE,
289 WA_RMBTrap, TRUE,
290 WA_ReportMouse, TRUE,
291 WA_CustomScreen, (ULONG)the_screen,
292 TAG_END
293 );
294 if (the_win == NULL) {
295 ErrorAlert(GetString(STR_OPEN_WINDOW_ERR));
296 return false;
297 }
298
299 // Set VideoMonitor
300 ScreenToFront(the_screen);
301 if (is_p96) {
302 VideoMonitor.mac_frame_base = p96GetBitMapAttr(the_screen->RastPort.BitMap, P96BMA_MEMORY);
303 VideoMonitor.bytes_per_row = p96GetBitMapAttr(the_screen->RastPort.BitMap, P96BMA_BYTESPERROW);
304 } else {
305 static UWORD ptr[] = { 0, 0, 0, 0 };
306 SetPointer(the_win, ptr, 0, 0, 0, 0); // Hide Pointer
307
308 APTR handle = LockBitMapTags(the_screen->RastPort.BitMap,
309 LBMI_BASEADDRESS, (ULONG)&VideoMonitor.mac_frame_base,
310 TAG_END);
311 UnLockBitMap(handle);
312 VideoMonitor.bytes_per_row = GetCyberMapAttr(the_screen->RastPort.BitMap, CYBRMATTR_XMOD);
313 }
314 return true;
315 }
316
317 bool VideoInit(bool classic)
318 {
319 // Read frame skip prefs
320 frame_skip = PrefsFindInt32("frameskip");
321 if (frame_skip == 0)
322 frame_skip = 1;
323
324 // Get screen mode from preferences
325 const char *mode_str;
326 if (classic)
327 mode_str = "win/512/342";
328 else
329 mode_str = PrefsFindString("screen");
330
331 // Determine type and mode
332 display_type = DISPLAY_WINDOW;
333 int width = 512, height = 384;
334 ULONG mode_id = 0;
335 if (mode_str) {
336 if (sscanf(mode_str, "win/%d/%d", &width, &height) == 2)
337 display_type = DISPLAY_WINDOW;
338 else if (sscanf(mode_str, "pip/%d/%d", &width, &height) == 2 && P96Base)
339 display_type = DISPLAY_PIP;
340 else if (sscanf(mode_str, "scr/%08lx", &mode_id) == 1 && (CyberGfxBase || P96Base))
341 display_type = DISPLAY_SCREEN;
342 }
343
344 // Open display
345 switch (display_type) {
346 case DISPLAY_WINDOW:
347 if (!init_window(width, height))
348 return false;
349 break;
350
351 case DISPLAY_PIP:
352 if (!init_pip(width, height))
353 return false;
354 break;
355
356 case DISPLAY_SCREEN:
357 if (!init_screen(mode_id))
358 return false;
359 break;
360 }
361
362 // Start periodic process
363 periodic_proc = CreateNewProcTags(
364 NP_Entry, (ULONG)periodic_func,
365 NP_Name, (ULONG)"Basilisk II IDCMP Handler",
366 NP_Priority, 0,
367 TAG_END
368 );
369 if (periodic_proc == NULL) {
370 ErrorAlert(GetString(STR_NO_MEM_ERR));
371 return false;
372 }
373 return true;
374 }
375
376
377 /*
378 * Deinitialization
379 */
380
381 void VideoExit(void)
382 {
383 // Stop periodic process
384 if (periodic_proc) {
385 SetSignal(0, SIGF_SINGLE);
386 Signal(&periodic_proc->pr_Task, SIGBREAKF_CTRL_C);
387 Wait(SIGF_SINGLE);
388 }
389
390 switch (display_type) {
391
392 case DISPLAY_WINDOW:
393
394 // Window mode, free bitmap
395 if (the_bitmap) {
396 WaitBlit();
397 FreeBitMap(the_bitmap);
398 }
399
400 // Free pens and close window
401 if (the_win) {
402 ReleasePen(the_win->WScreen->ViewPort.ColorMap, black_pen);
403 ReleasePen(the_win->WScreen->ViewPort.ColorMap, white_pen);
404
405 CloseWindow(the_win);
406 }
407 break;
408
409 case DISPLAY_PIP:
410
411 // Close PIP
412 if (the_win)
413 p96PIP_Close(the_win);
414 break;
415
416 case DISPLAY_SCREEN:
417
418 // Close window
419 if (the_win)
420 CloseWindow(the_win);
421
422 // Close screen
423 if (the_screen) {
424 if (is_p96)
425 p96CloseScreen(the_screen);
426 else
427 CloseScreen(the_screen);
428
429 the_screen = NULL;
430 }
431 break;
432 }
433 }
434
435
436 /*
437 * Set palette
438 */
439
440 void video_set_palette(uint8 *pal)
441 {
442 if (display_type == DISPLAY_SCREEN) {
443
444 // Convert palette to 32 bits
445 ULONG table[2 + 256 * 3];
446 table[0] = 256 << 16;
447 table[256 * 3 + 1] = 0;
448 for (int i=0; i<256; i++) {
449 table[i*3+1] = pal[i*3] * 0x01010101;
450 table[i*3+2] = pal[i*3+1] * 0x01010101;
451 table[i*3+3] = pal[i*3+2] * 0x01010101;
452 }
453
454 // And load it
455 LoadRGB32(&the_screen->ViewPort, table);
456 }
457 }
458
459
460 /*
461 * Video message handling (not neccessary under AmigaOS, handled by periodic_func())
462 */
463
464 void VideoInterrupt(void)
465 {
466 }
467
468
469 /*
470 * Process for window refresh and message handling
471 */
472
473 static __saveds void periodic_func(void)
474 {
475 struct MsgPort *timer_port = NULL;
476 struct timerequest *timer_io = NULL;
477 struct IntuiMessage *msg;
478 ULONG win_mask = 0, timer_mask = 0;
479
480 // Create message port for window and attach it
481 struct MsgPort *win_port = CreateMsgPort();
482 if (win_port) {
483 win_mask = 1 << win_port->mp_SigBit;
484 the_win->UserPort = win_port;
485 ModifyIDCMP(the_win, IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE | IDCMP_RAWKEY | (display_type == DISPLAY_SCREEN ? IDCMP_DELTAMOVE : 0));
486 }
487
488 // Start 60Hz timer for window refresh
489 if (display_type == DISPLAY_WINDOW) {
490 timer_port = CreateMsgPort();
491 if (timer_port) {
492 timer_io = (struct timerequest *)CreateIORequest(timer_port, sizeof(struct timerequest));
493 if (timer_io) {
494 if (!OpenDevice((UBYTE *)TIMERNAME, UNIT_MICROHZ, (struct IORequest *)timer_io, 0)) {
495 timer_mask = 1 << timer_port->mp_SigBit;
496 timer_io->tr_node.io_Command = TR_ADDREQUEST;
497 timer_io->tr_time.tv_secs = 0;
498 timer_io->tr_time.tv_micro = 16667 * frame_skip;
499 SendIO((struct IORequest *)timer_io);
500 }
501 }
502 }
503 }
504
505 // Main loop
506 for (;;) {
507
508 // Wait for timer and/or window (CTRL_C is used for quitting the task)
509 ULONG sig = Wait(win_mask | timer_mask | SIGBREAKF_CTRL_C);
510
511 if (sig & SIGBREAKF_CTRL_C)
512 break;
513
514 if (sig & timer_mask) {
515
516 // Timer tick, update display
517 BltTemplate(the_bitmap->Planes[0], 0, the_bitmap->BytesPerRow, the_win->RPort,
518 the_win->BorderLeft, the_win->BorderTop, VideoMonitor.x, VideoMonitor.y);
519
520 // Restart timer
521 timer_io->tr_node.io_Command = TR_ADDREQUEST;
522 timer_io->tr_time.tv_secs = 0;
523 timer_io->tr_time.tv_micro = 16667 * frame_skip;
524 SendIO((struct IORequest *)timer_io);
525 }
526
527 if (sig & win_mask) {
528
529 // Handle window messages
530 while (msg = (struct IntuiMessage *)GetMsg(win_port)) {
531
532 // Get data from message and reply
533 ULONG cl = msg->Class;
534 UWORD code = msg->Code;
535 UWORD qualifier = msg->Qualifier;
536 WORD mx = msg->MouseX;
537 WORD my = msg->MouseY;
538 ReplyMsg((struct Message *)msg);
539
540 // Handle message according to class
541 switch (cl) {
542 case IDCMP_MOUSEMOVE:
543 if (display_type == DISPLAY_SCREEN)
544 ADBMouseMoved(mx, my);
545 else
546 ADBMouseMoved(mx - the_win->BorderLeft, my - the_win->BorderTop);
547 break;
548
549 case IDCMP_MOUSEBUTTONS:
550 if (code == SELECTDOWN)
551 ADBMouseDown(0);
552 else if (code == SELECTUP)
553 ADBMouseUp(0);
554 else if (code == MENUDOWN)
555 ADBMouseDown(1);
556 else if (code == MENUUP)
557 ADBMouseUp(1);
558 else if (code == MIDDLEDOWN)
559 ADBMouseDown(2);
560 else if (code == MIDDLEUP)
561 ADBMouseUp(2);
562 break;
563
564 case IDCMP_RAWKEY:
565 if (qualifier & IEQUALIFIER_REPEAT) // Keyboard repeat is done by MacOS
566 break;
567 if (code & IECODE_UP_PREFIX)
568 ADBKeyUp(keycode2mac[code & 0x7f]);
569 else
570 ADBKeyDown(keycode2mac[code & 0x7f]);
571 break;
572 }
573 }
574 }
575 }
576
577 // Stop timer
578 if (timer_io) {
579 if (!CheckIO((struct IORequest *)timer_io))
580 AbortIO((struct IORequest *)timer_io);
581 WaitIO((struct IORequest *)timer_io);
582 CloseDevice((struct IORequest *)timer_io);
583 DeleteIORequest(timer_io);
584 }
585 if (timer_port)
586 DeleteMsgPort(timer_port);
587
588 // Remove port from window and delete it
589 Forbid();
590 msg = (struct IntuiMessage *)win_port->mp_MsgList.lh_Head;
591 struct Node *succ;
592 while (succ = msg->ExecMessage.mn_Node.ln_Succ) {
593 if (msg->IDCMPWindow == the_win) {
594 Remove((struct Node *)msg);
595 ReplyMsg((struct Message *)msg);
596 }
597 msg = (struct IntuiMessage *)succ;
598 }
599 the_win->UserPort = NULL;
600 ModifyIDCMP(the_win, 0);
601 Permit();
602 DeleteMsgPort(win_port);
603
604 // Main task asked for termination, send signal
605 Forbid();
606 Signal(MainTask, SIGF_SINGLE);
607 }