ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/video_amiga.cpp
Revision: 1.1
Committed: 1999-10-03T14:16:25Z (25 years, 1 month ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

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