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, 2 months ago) by cebix
Branch: MAIN
Branch point for: cebix
Log Message:
Initial revision

File Contents

# User Rev Content
1 cebix 1.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     }