/*************************************************************** * FLIP v2.0 02/25/90 * © Copyright 1990 by Timm Martin and Mike Monaco * Based heavily on the Shuffle program written by Ray Lambert * *** This program is FreeWare *** * * This program is the same as FLIP v1.0 except that it uses * an assembler input handler, eliminating the need to compile * it in large code/large data format. This also means that * it's about 1K smaller than FLIP v1.0! * * You will get a ptr/ptr warning when you compile. You will * also get a linker warning about replacing __main. These * are OK and do not affect the program. ****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #asm InputEvent EQU 0 SOFFSET SET 0 ie_NextEvent EQU SOFFSET SOFFSET SET SOFFSET+4 ie_Class EQU SOFFSET SOFFSET SET SOFFSET+1 ie_SubClass EQU SOFFSET SOFFSET SET SOFFSET+1 ie_Code EQU SOFFSET SOFFSET SET SOFFSET+2 ie_Qualifier EQU SOFFSET SOFFSET SET SOFFSET+2 xdef _LVOSignal #endasm /* name of the message port for input handler to reach this process */ char *port_name = "FlipPort"; /* signals and signal bits */ ULONG window_signal, screen_signal, window_signal_bit = -1L, screen_signal_bit = -1L, all_signal_bits, exit_bits; /* needs to be declared globally for inline assembly */ struct InputEvent *ie; /* flags */ UBYTE input_opened = 0, handler_active = 0; /* global variables */ struct Interrupt *handler = NULL; struct Task *my_task; struct IOStdReq *io_req = NULL; struct MsgPort *message_port = NULL; struct IntuitionBase *IntuitionBase = NULL; #define IO_REQ_SIZE (long)sizeof(struct IOStdReq) /*************** * END PROGRAM ****************/ void end_program() { if (handler_active) { io_req->io_Command = IND_REMHANDLER; io_req->io_Data = (APTR)handler; DoIO( io_req ); } if (input_opened) CloseDevice( io_req ); if (io_req) FreeMem( io_req, IO_REQ_SIZE ); if (handler) FreeMem( handler, (LONG)sizeof(struct Interrupt) ); if (message_port) DeletePort( message_port ); if (screen_signal_bit != -1L) FreeSignal( screen_signal_bit ); if (window_signal_bit != -1L) FreeSignal( window_signal_bit ); if (IntuitionBase) CloseLibrary( IntuitionBase ); exit( 0 ); } /*************** * KEY HANDLER ****************/ /* This function is the input handler code used to monitor for left-Amiga-M and left-Amiga-N key presses. It's not doing too much, so there shouldn't be any noticable degradation in the input process. A pointer to the active input even chain is passed to this function in register A0, hence the need for inline assembler. Thanks to Mike Monaco (Dr. Assembler) for this tidbit! Assembler Notes: To allow for use with the small data and small code models, particularly small data model, variables used in the handler had to be addressable such that the assembler would not use the offset register associated with the small data models. (For the MANX compiler/assembler that is A4) The reason is that the handler is invoked as an interrupt and does not have the luxury of having the registers in the same state as the task which installed it. (ie. A4 would be garbage in relation to the handler code) Also data created during startup and instalation must be available for signaling the task into action. This data must be stored so it is addressable in the small data model. The only way I could think of doing this was to allocate storage space within the code and use (pc) addressing for the constants. Orignally I also used the same method for saving the variable data but wasn't sure if the handler could be invoked while it is was processing other data, so is switched to using the stack for that. I didn't have any problems the other way though. Ok. I could have used the farcode and fardata directives, but that wouldn't have been any fun. --Mike Monaco */ struct InputEvent *key_handler() { ; #asm move.l a0,-(sp) ; save beginning of event chain Key_For_Loop: move.l a0,d0 ; see if its NULL beq.s Key_For_Exit ; yes - with event chain move.b ie_Class(a0),d0 ; RAWKEY = $0001 subq.b #1,d0 ; is it a RAWKEY event bne.s Key_Next ; no - get next event move.w ie_Qualifier(a0),d0 ; IEQUALIFIER_LCOMMAND = $0040 and.w #$0040,d0 ; was Left Amiga pressed beq.s Key_Next ; no - get next event move.w ie_Code(a0),d0 ; looking for $36 for screen ; and $37 for window Key_Check_Screen: sub.b #$36,d0 ; is it screen bne.s Key_Check_Window ; no - might be window move.l _ss_(pc),d0 ; yes - user wants to flip screens bra.s Key_Send_Signal Key_Check_Window: sub.b #$01,d0 ; is it window bne.s Key_Next ; nope - we dont want this baby move.l _ws_(pc),d0 ; yes - user wants to flip windows Key_Send_Signal: ; signal task to perform flip move.l a0,-(sp) ; save a0 move.l _mt_(pc),a1 move.l $4,a6 ;Signal( my_task, window_signal ); jsr _LVOSignal(a6) ; a1 d0 Key_Clear_Event: ; ready to remove event move.l (sp)+,a0 ; restore a0 move.b #0,ie_Class(a0) ; remove event by clearing Class Key_Next: move.l ie_NextEvent(a0),a0 ; get next pointer in a0 bra.s Key_For_Loop Key_For_Exit: move.l (sp)+,d0 ; return beginning of event chain ; for other handlers to process #endasm ; return (); } #asm cseg ; storage must be accessable but not ds.l 0 ; restricted by the small data model _ws_ dc.l 0 ; window signal created during startup _ss_ dc.l 0 ; screen signal created during startup _mt_ dc.l 0 ; task pointer found during starup #endasm /******************* * INSTALL HANDLER ********************/ void install_handler() { /* try to allocate signal bits */ if ((screen_signal_bit = AllocSignal( -1L )) == -1L || (window_signal_bit = AllocSignal( -1L )) == -1L) end_program(); /* calculate the bit masks */ screen_signal = 1L << screen_signal_bit; window_signal = 1L << window_signal_bit; /* get a pointer to this process for signalling purposes */ my_task = FindTask( NULL ); /* get a message port */ if (!(message_port = CreatePort( port_name, 0L ))) end_program(); /* Init Std IO structure */ if (!(io_req = (struct IOStdReq *) AllocMem( IO_REQ_SIZE, MEMF_PUBLIC|MEMF_CLEAR ))) end_program(); io_req->io_Message.mn_Node.ln_Type = NT_MESSAGE; io_req->io_Message.mn_Node.ln_Pri = 0; io_req->io_Message.mn_ReplyPort = message_port; /* open the input.device */ if (!(input_opened = (OpenDevice( "input.device", 0L, io_req, 0L ) == 0))) end_program(); /* store required values in code segment of handler */ ; #asm lea _ws_,a0 move.l _window_signal,(a0) lea _ss_,a0 move.l _screen_signal,(a0) lea _mt_,a0 move.l _my_task,(a0) #endasm ; if (!(handler = (struct Interrupt *) AllocMem( (LONG)sizeof(struct Interrupt), MEMF_PUBLIC|MEMF_CLEAR ))) end_program(); /* add the handler */ handler->is_Code = (void *)key_handler; handler->is_Data = NULL; handler->is_Node.ln_Pri = 51; io_req->io_Command = IND_ADDHANDLER; io_req->io_Data = (APTR)handler; if (!(handler_active = (DoIO( io_req ) == 0) )) end_program(); } /****************** * SHUFFLE SCREEN *******************/ /* This procedure locks Intuition so nothing funny can happen in between the test and the execution portions of the "if" statement (this is a multitasking machine, you know). Then the procedure checks to see if there is another screen in line. If so, the current screen is sent to the back, and the first window (if any) on the new front screen is activated. */ void shuffle_screen() { register ULONG ilock; register struct Screen *screen; register struct Window *window; ilock = LockIBase( NULL ); screen = IntuitionBase->FirstScreen; if (screen->NextScreen) { while (screen->NextScreen) screen = screen->NextScreen; ScreenToFront( screen ); if (window = screen->LayerInfo.top_layer->Window) ActivateWindow( window ); } UnlockIBase( ilock ); SetSignal( 0L, screen_signal ); } /****************** * SHUFFLE WINDOW *******************/ /* This procedure again locks Intuition for reasons mentioned above. Then it sends the current window to the background, and activates the new front window. */ void shuffle_window() { register ULONG ilock; register struct Layer *layer, *get_layer; register struct Window *window; ilock = LockIBase( NULL ); layer = IntuitionBase->FirstScreen->LayerInfo.top_layer; get_layer = NULL; while (layer->back) { if (layer->Window && !(layer->Flags & LAYERBACKDROP)) get_layer = layer; layer = layer->back; } if (get_layer) { WindowToFront( window = get_layer->Window ); ActivateWindow( window ); } UnlockIBase( ilock ); SetSignal( 0L, window_signal ); } /********* * _MAIN **********/ /* This replaces the _main defined in the standard c.lib library. */ void _main() { register ULONG signal; /* if couldn't open Intuition (we're in trouble!) or if this is already * running, end the program */ if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary( "intuition.library", LIBRARY_VERSION )) || FindPort( port_name )) end_program(); install_handler(); exit_bits = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F; all_signal_bits = screen_signal | window_signal | exit_bits; SetSignal( 0L, all_signal_bits ); for (;;) { signal = Wait( all_signal_bits ); if (signal & exit_bits) end_program(); if (signal & screen_signal) shuffle_screen(); if (signal & window_signal) shuffle_window(); } }