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 (24 years, 3 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

# User Rev Content
1 cebix 1.1 /*
2     * video_amiga.cpp - Video/graphics emulation, AmigaOS specific stuff
3     *
4 cebix 1.3 * Basilisk II (C) 1997-2000 Christian Bauer
5 cebix 1.1 *
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 cebix 1.4 #include <cybergraphx/cybergraphics.h>
26 cebix 1.1 #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 cebix 1.4 #include <proto/cybergraphics.h>
34 cebix 1.1
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 cebix 1.4 #define DEBUG 0
43 cebix 1.1 #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 cebix 1.4 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 cebix 1.1
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 cebix 1.2 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
115 cebix 1.1 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 cebix 1.2 P96PIP_ErrorCode, (ULONG)&error,
158 cebix 1.1 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 cebix 1.2 WA_Title, (ULONG)GetString(STR_WINDOW_TITLE),
169     WA_PubScreenName, (ULONG)"Workbench",
170 cebix 1.1 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 cebix 1.2 p96PIP_GetTags(the_win, P96PIP_SourceBitMap, (ULONG)&the_bitmap, TAG_END);
179 cebix 1.1
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 cebix 1.4 // Open screen (requires Picasso96/CyberGfx as we need chunky modes)
190 cebix 1.1 static bool init_screen(ULONG mode_id)
191     {
192     // Set relative mouse mode
193     ADBSetRelMouseMode(true);
194    
195 cebix 1.4 // Check whether the mode is a Picasso96 mode or a CyberGfx mode
196 cebix 1.5 if (P96Base && p96GetModeIDAttr(mode_id, P96IDA_ISP96))
197     is_p96 = true;
198     else if (CyberGfxBase && IsCyberModeID(mode_id))
199 cebix 1.4 is_cgfx = true;
200     else {
201 cebix 1.1 ErrorAlert(GetString(STR_NO_P96_MODE_ERR));
202     return false;
203     }
204    
205 cebix 1.4 uint32 depth;
206     uint32 format;
207    
208 cebix 1.1 // Check if the mode is one we can handle
209 cebix 1.4 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 cebix 1.1 switch (depth) {
218     case 8:
219     VideoMonitor.mode = VMODE_8BIT;
220     break;
221     case 15:
222     case 16:
223 cebix 1.4 if (format != RGBFB_R5G5B5 && format != PIXFMT_RGB16) {
224 cebix 1.1 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 cebix 1.4 if (format != RGBFB_A8R8G8B8 && format != PIXFMT_ARGB32) {
232 cebix 1.1 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 cebix 1.4 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 cebix 1.1 VideoMonitor.x = width;
254     VideoMonitor.y = height;
255    
256     // Open screen
257 cebix 1.4 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 cebix 1.1 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 cebix 1.2 WA_CustomScreen, (ULONG)the_screen,
292 cebix 1.1 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 cebix 1.4 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 cebix 1.1 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 cebix 1.4 else if (sscanf(mode_str, "scr/%08lx", &mode_id) == 1 && (CyberGfxBase || P96Base))
341 cebix 1.1 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 cebix 1.2 NP_Entry, (ULONG)periodic_func,
365     NP_Name, (ULONG)"Basilisk II IDCMP Handler",
366 cebix 1.1 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 cebix 1.4 if (the_screen) {
424     if (is_p96)
425     p96CloseScreen(the_screen);
426     else
427     CloseScreen(the_screen);
428    
429     the_screen = NULL;
430     }
431 cebix 1.1 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     }