Japanese
SGL User's ManualPROGRAMMER'S TUTORIAL
BackForward
PROGRAMMER'S TUTORIAL

10.Event control


This chapter explains the event structure and specific usage of event management functions.
By using the concept of events, it is possible to describe a program as a combination of program events (each element in the program), which makes it very easy to understand the flow of the entire program. Furthermore, since each program event can be used in common, programs can be simplified.

10-1. Event structure

Event processing

Event processing is a process in which a group of functions that appear in a program list are represented by a label called an event, and a list of consecutive events is executed in order from the beginning.
By using event processing, it is possible to process a complex group of functions that are executed sequentially as a simple list of labels. In addition, the execution order can be easily changed by operating the label registered as an event.
This makes it possible to understand the flow of the entire program, as well as to share events and simplify the program.

Figure 10-1 Event image

/* front omitted */

slInitEvent(); /* Initialize the event */

slSetEvent((void*) game_demo ); /* Register event */
slSetEvent((void*) game_play );
slSetEvent((void*) game_over );

slExecuteEvent(); /* Execute event */

void game_demo(EVENT *evptr){ /* Event execution 1(game_demo) */
		:
}

void game_play(EVENT *evptr){ /* Event execution 2(game_play) */
		:
}

void game_over(EVENT *evptr){ /* Event execution 3(game_over) */
		:
}

/* after omitted */

The function group registered as an event and the execution order of the event are processed in an event management area called the EVENT structure secured in the event RAM area.
Also, in SGL, the order in which events are executed is called an event list.

EVENT structure

Events are defined as EVENT structures in the event RAM area (8kbytes). One EVENT structure consists of 128 bytes, and up to 64 EVENT structures can be defined in this RAM area. The EVENT structure is constructed as shown in Figure 10-2.

Figure 10-2 EVENT Structure

Each EVENT structure consists of the following members:

*work: Start address of WORK structure for user area expansion.
*next: Start address of the next event to be executed.
*before: Start address of the event to be executed immediately before.
*exad(): Start address of the area where the function to be executed as an event is stored.
user[]: User area for storing variables used by events registered functions.
       It is also possible to expand the user area using work etc.

Also, if each member of “*work”, “*next”, and “*before” does not have a corresponding address, NULL will be assigned.

When a user uses the user area in the EVENT structure, it is very convenient to cast the user area to a structure defined for the user.

The user area of one EVENT structure consists of 112 bytes, but if you want to use the user area beyond this, you can expand it using work etc. For information on expanding the user area, see "Section 10-4: Expanding the user area."

event list

Each event defined as an EVENT structure in the event RAM area forms a list according to the order of the events to be executed.
The list is connected by "*next" and "*before" reserved in the event structure, and the series of events to be executed is called an event list.
Also, events that are executed consecutively in the event list do not necessarily need to be stored consecutively in the event RAM area; if the pointers are connected with "*next" and "*before", Runs continuously.

Figure 10-3 Event list structure

Note) NULL (0) if the corresponding event work does not exist.

The system has the start address of the EVENT structure registered at the beginning of the event list as the EventTop variable, and when executing events, it will execute them in the order starting from the event specified in "EventTop".
The executed event passes the value of the pointer “*next” in the EVENT structure to the EventNow variable, and the system executes the EVENT structure next starting from the address pointed to by “EventNow”. The system executes events in order according to “EventNow” and ends the event execution process when the value pointed to by “*next” becomes NULL (there is no subsequent event).
The EventLast variable points to the start address of the EVENT structure registered at the end of the list, and is used when adding and registering events. Additionally, the EventLast variable is only modified when the library functions “slSetEvent”, “slSetEventNext”, and “slCloseEvent” are executed.
“*before” is used when inserting, registering, and deleting events.

10-2. Event processing using SGL functions

Here, we will explain actual event processing using SGL library functions.

Initializing the event

In order to use event processing in SGL, you must first initialize the buffers and event lists related to events.

To initialize events in SGL, use the library function “slInitEvent”.

[void slInitEvent ( void );]
Performs all initialization related to event processing (event list, buffer initialization, etc.).

 Note
If you use event processing without initializing the event, it will become inconsistent with uninitialized buffers, event lists, etc., and in the worst case, the CPU may stop.
(Detail is (See “Section 10-5: Notes on event processing”).

Creating an event list

To process events, you need to register the events in the order of execution as an event list.
Events are added to the event list using the event registration library function “slSetEvent” as shown in Figure 10-4.
Registered events are registered sequentially at the end of the event list, and are executed sequentially from the beginning of the event list by the event execution library function "slExecuteEvent".

Figure 10-4 Creating an event list

Please use the library function “slSetEvent” to register events.

[EVENT *slSetEvent ( void (*func)() ) ;]
Allocate an EVENT structure (128 bytes) in the event RAM area and add/register a new event at the end of the event list. Assign the execution address value of the event to be registered to the parameter.
The function “slSetEvent” returns the start address of the registered EVENT structure as a return value. Returns NULL if event registration fails.

See the next section for the event format and structure.

Event format

The functions to be registered as events must be formatted as shown in Figure 10-5.

Figure 10-5 Event format
● Event format ●

void function_name(EVENT *evptr){execute_contents} ↑ ↑ ↑ | | Event execution details | Start address of EVENT structure Event name

Additionally, events that are registered and executed are managed by the EVENT structure.
The EVENT structure stores the execution address of the event to be registered and a structure for variables used within the event, and these two are used to manage the event.

Figure 10-6 Event structure
/* front omitted */
slSetEvent((void *)init_cube);

slExecuteEvent();

typedef struct cube{ FIXEDpos[xyz];
				ANGLE ang[xyz]
;
		:
			Sint16 unset_count;
			Sint16 unset_i;
		} CUBE;

void init_cube(EVENT *evptr)
{
	CUBE *cubeptr;

	cubeptr=(CUBE *)evptr -> user;

	cubeptr-> pos[x]=pos_x;

		:

	cubeptr-> unset_count=0;

	disp_cube(evptr);
}

/* after omitted */

  • Functions registered as events are controlled by the EVENT structure.
  • The member “*exad( )” stores the execution address of the event to be registered, and the member “user[ ]” stores the structure used in the event.
  • The structure is used for variables within the event, but please be careful that the structure does not exceed the user area (size: 112 bytes).
  • If you want to use more variables, expand the user area by using a WORK structure, etc., so that the structure itself is also divided and stored.

    About the structure for variables within the event

    If you want to use various variables in an event, you need to define those variables as a structure.
    Therefore, the structure for variables within the event cannot be set to exceed the size of the user area (112 bytes) in which the structure is stored.

    Executing an event

    Events registered in the event list are executed in order by the event management function.
    The library function “slExecuteEvent” is used to execute the event.

    For example, if event A, event B, and event C are registered in the event list in order using the library function "slSetEvent", each event will be executed in the order shown below according to the event list.

    Figure 10-7 Executing an event

    [void slExecuteEvent ( void );]
    Events registered in the event list will be executed in the order listed.

    10-3. Change event list

    Add event

    You can add events to the events registered in the event list.
    The event will be added to the end of the event list.
    To add an event, use the same library function “slSetEvent” used for event registration.

    Figure 10-8 Adding an event

    [EVENT *slSetEvent ( void (*func)() ) ;]
    Allocate an EVENT structure (128 bytes) in the event RAM area and add/register a new event at the end of the event list. Assign the execution address value of the event to be registered to the parameter.
    The function “slSetEvent” returns the start address of the registered EVENT structure as a return value.
    Returns NULL if event registration fails.

    Inserting an event

    You can insert events into the events registered in the event list.
    The event will be inserted after the specified event.
    Use the library function “slSetEventNext” to insert events.

    Figure 10-9 Inserting an event

    [EVENT *slSetEventNext ( EVENT *evptr , void (*func)() ) ;]
    Allocate an EVENT structure (128 bytes) in the event RAM area and insert/register a new event in the middle of the event list. Assign the start address of the EVENT structure immediately before inserting the event and the execution address of the event to be inserted/registered to the parameters.
    The function “slSetEventNext” returns the first address of the registered EVENT structure as a return value.
    Returns NULL if event registration fails.

    Delete event

    Events registered in the event list can be deleted from the event list.
    To delete an event, use the library function “slCloseEvent”.

    Figure 10-10 Deleting an event

    [void slCloseEvent (EVENT *evptr);]
    Removes the event specified by the parameter from the event list.
    At the same time, if the member "work" of the specified event is not NULL, it is assumed that there is a worklist and all attached work is returned to the system.
    Assign the start address of the EVENT structure to be deleted to the parameter.
    Also, the function “slCloseEvent” does not check whether the area to be returned is a registered EVENT structure. Therefore, even if you accidentally return the extended user area to the system, you will not know.
    In this case, various problems may occur, and in the worst case, the CPU may stop (see "Section 10-5: Notes on event processing" for details).

    Changing the event list while an event is running

    If you attempt to change the event list in any way while running an event list that has registered events (such as changing the event list within the event), the event being changed will be affected as shown in the following figure.

    Figure 10-11 Changing the event list while an event is running

    Changes made to the event list prior to the currently running event (executed) will be applied the next time the event list is executed. Changes to the event list that are later (unexecuted) than the currently executed event are executed according to the order of the event list that was changed when the current event was executed. Any changes to addition/insertion/deletion of events will be executed under this application.

    10-4. Expansion of user area

    When writing a program using event processing, in some cases the variables (structures) used by the function executed as an event may not fit in the event's user area (112 bytes).
    In such cases, SGL allows you to expand and use the user area.
    There are two ways to expand the user area: use an expansion-only area called work, and acquire an expansion area of the same size as the EVENT structure from the event RAM area.

    Expansion of user area using work area

    In SGL, the user area of the EVENT structure can be expanded by using a dedicated area for user area expansion called work.
    A work is defined as a WORK structure (64 bytes) in the work RAM area, and up to 256 works can be used.
    The size of the user area that can be expanded per workpiece is 60 bytes.
    The remaining 4 bytes of the WORK structure are used to specify the address of the WORK structure to be concatenated.

    Figure 10-12 WORK structure

    The members of the WORK structure are as follows:

    *next: Start address of the WORK structures that you want to connect as a chain structure.
          If there is no work that follows as a chain structure, NULL is assigned.
    
    user[]: Extended user area (60 bytes).
    

    Work acquired as a WORK structure is linked by assigning the start address of the WORK structure to "*work" in the EVENT structure, creating a chain structure.
    In addition, the user area can be further expanded by acquiring the work again in the WORK structure. At this time, by assigning the start address of the WORK structure to be linked to "*next" in the WORK structure, the works to be linked can be made into a chain structure.
    When there are no more WORK structures following the chain structure, “*next” is assigned NULL.

    Figure 10-13 Connecting workpieces

    To use work in SGL, use the library functions “slGetWork” and “slReturnWork”. “slGetWork” is used to acquire work, and “slReturnWork” is used to release work.

    【WORK *slGetWork ( void ) ;】
    Allocate a WORK structure (64 bytes) in the work RAM area.
    The function "slGetWork" returns the WORK structure address value of the secured work area as a return value. If space allocation fails, the function returns NULL.

    [void slReturnWork (WORK *wkptr);]
    Returns/releases the work area specified by the parameter. Assign the WORK structure address value of the work area to be returned to the parameter. Also, when returning or releasing part of the workpieces connected in a chain structure to the system, adjust the remaining workpieces so that the chain structure is correct. If adjustments are not made, the chain structure and work processing will not be consistent, and in the worst case, the CPU may stop (see "Section 10-5: Notes on event processing" for details).

    About the behavior of connected WORK structure due to release of EVENT structure

    Work does not necessarily need to use "*work" and "*next" to form a chain structure, and the start address of the WORK structure to be linked can also be stored in the user area.
    However, for work linked with "*work" and "*next", the work is returned to the system and released at the same time by releasing the event, but work linked from the user area is released by releasing the event. Also, the WORK structure is not freed.
    Therefore, the WORK structure connected from the user area must be returned to the system and released using the library function "slReturnWork" when the event is released.

    Expansion of user area using event area

    If the size of one user area to be expanded is insufficient for the WORK structure (60 bytes), you can allocate a user area of the same size as the EVENT structure (128 bytes) from the event RAM area.
    However, since the area originally allocated for one event is used as the user area, the maximum number of events that can be registered will be reduced accordingly.

    Figure 10-14 Expanding the user area using event RAM area

    When expanding the user area of the EVENT structure using the event RAM area in SGL, use the library functions “slGetEvent” and “slReturnEvent”. “slGetEvent” is used to secure the user area, and “slReturnEvent” is used to release the user area.

    [EVENT *slGetEvent ( void );]
    Extract a RAM area of the same size as the event (128 bytes) from the event RAM area and use it as the user area. Also, since the area allocated for the EVENT structure is used as the user area, the number of events that can be registered will be reduced accordingly.
    The function “slGetEvent” returns the start address of the allocated RAM area as a return value.
    If space allocation fails, the function returns NULL.

    [void slReturnEvent (EVENT *evptr);]
    Returns/releases the event RAM area specified by the parameter to the system.
    Assign the start address of the RAM area to be returned to the parameter.
    Also, the function “slReturnEvent” does not check whether the area to be returned is an expanded user area. Therefore, even if you return an EVENT structure that was registered as an event by mistake to the system, it will not be understood. In this case, various problems may occur, and in the worst case, the CPU may stop (see "Section 10-5: Notes on event processing" for details).

    10-5. Notes on event processing

    When processing events, be especially careful about returning and releasing various areas to the system. This is caused by the function performing the operation not checking whether the area pointed to by the specified parameter is the type of area that the function should handle during return/release operations.

    For example, when you try to return/release an extended user area using the library function "slReturnEvent", if you accidentally return/release an area that is registered as a normal event, the following problems will occur: This may occur, and in the worst case, the CPU may stop.

    1. return of territory

    2. The EVENT structure specified by the function “slReturnEvent” is returned to the system.

    3. event list

    4. The EVENT structure continues to exist in the event list because it is not released as an event.

    5. New event registration

    6. Since the area is system-free, it can be used freely using functions such as "slSetEvent".

    7. Chain structure contradiction

    8. Although it is registered in the event list, since the area is free, the contents of the EVENT structure are rewritten, which causes a problem in the chain structure.

    9. CPU stopped

    10. The chain structure will become inconsistent, and in the worst case, the CPU will stop.

    Figure 10-15 Incorrect event operation

    10-6. Flow of event processing

    The following flowchart shows the general flow of event processing. In event processing, we also extend the user area as necessary within the executed event and use variables using structures.

    Flow 10-1 Event processing flow

    10-7. Examples of using events

    The sample program “sample_10” (Listings 10-1 and 10-2) is an example of actually performing plumbing using event processing. The program uses events to control and hide the cube.

    The program is roughly divided into two parts: Listing 10-1 “main.c” handles system initialization and event execution, and Listing 10-2 “sample.c” handles event initialization, event content definition, and registration. We are doing

    Listing 10-1 sample_10: Event processing (main.c)
    /*------------------------------------------------ ----------------------*/
    /* Event Demo */
    /*------------------------------------------------ ----------------------*/
    #include "sgl.h"
    
    void ss_main(void)
    {
    	slInitSystem(TV_320x224,NULL,1);
    	slPrint("Sample program 10" , slLocate(9,2)) ;
    
    	set_event();
    	while(-1) {
    		slExecuteEvent();
    		slSynch();
    	}
    }
    

    Flow 10-2 sample_10: Main loop

    Listing 10-2 shows the part of "sample10" where events are registered and event execution details are defined. In addition, the contents of the event list are changed (added/deleted) in the user-defined function “disp_cube”.

    Listing 10-2 sample_10: Event processing (sample.c)
    #include "sgl.h"
    
    extern PDATA PD_CUBE;
    
    #define POS_X toFIXED(0.0)
    #define POS_X_UP 200.0 
    #define POS_Y toFIXED(20.0)
    #define POS_Z toFIXED(270.0)
    #define POS_Z_UP 50.0
    #define SET_COUNT 500
    
    static void init_cube2(EVENT*);
    
    typedef struct cube {
    	FIXED pos[XYZ];
    	ANGLE ang[XYZ];
    	ANGLE angx, angz;
    	ANGLE angx_up , angz_up;
    	PDATA *poly;
    	Sint16 set_count , set_i;
    	Sint16 unset_count , unset_i;
    } CUBE;
    
    static void disp_cube(EVENT *evptr)
    {
    	CUBE *cubeptr;
    
    	cubeptr = (CUBE *)evptr-> user;
    	slPushMatrix();
    	{
    		slTranslate(cubeptr-> pos[X] , cubeptr-> pos[Y] , cubeptr-> pos[Z]);
    		cubeptr-> pos[X] = POS_X + POS_X_UP * slSin(cubeptr-> angx);
    		cubeptr-> pos[Y] = cubeptr-> pos[Y];
    		cubeptr-> pos[Z] = POS_Z + POS_Z_UP * slCos(cubeptr-> angz);
    		cubeptr-> angx += cubeptr-> angx_up;
    		cubeptr-> angz += cubeptr-> angz_up;
    		slRotY(cubeptr-> ang[Y]); 
    		slRotX(cubeptr-> ang[X]); 
    		slRotZ(cubeptr-> ang[Z]); 
    		cubeptr-> ang[X] += DEGtoANG(5.0);
    		cubeptr-> ang[Y] += DEGtoANG(5.0);
    		cubeptr-> ang[Z] += DEGtoANG(5.0);
    		slPutPolygon(cubeptr-> poly);
    	}
    	slPopMatrix();
    	cubeptr-> set_count -= cubeptr-> set_i;
    	if(cubeptr-> set_count< 0 ) {
    		slSetEvent( (void *)init_cube2 );
    		cubeptr-> set_count = SET_COUNT;
    	}
    	cubeptr-> unset_count -= cubeptr-> unset_i;
    	if(cubeptr-> unset_count< 0 ) slCloseEvent(evptr);
    }
    
    static void init_cube1(EVENT *evptr)
    {
    	CUBE *cubeptr;
    
    	cubeptr = (CUBE *)evptr-> user;
    	cubeptr-> pos[X] = POS_X;
    	cubeptr-> pos[Y] = POS_Y;
    	cubeptr-> pos[Z] = POS_Z;
    	cubeptr-> ang[X] = cubeptr-> ang[Y] = cubeptr-> ang[Z] = DEGtoANG(0.0);
    	cubeptr-> angx = cubeptr-> angz = DEGtoANG( 0.0);
    	cubeptr-> angx_up = cubeptr-> angz_up = DEGtoANG( 0.0);
    	cubeptr-> set_count = SET_COUNT;
    	cubeptr-> set_i = 1;
    	cubeptr-> unset_count = 0;
    	cubeptr-> unset_i = 0;
    	cubeptr-> poly = &PD_CUBE;
    	evptr-> exad = (void *)disp_cube;
    	disp_cube(evptr);
    }
    
    static void init_cube2(EVENT *evptr)
    {
    	CUBE *cubeptr;
    
    	cubeptr = (CUBE *)evptr-> user;
    	cubeptr-> pos[X] = POS_X;
    	cubeptr-> pos[Y] = POS_Y - toFIXED(50);
    	cubeptr-> pos[Z] = POS_Z + POS_Z_UP;
    	cubeptr-> ang[X] = cubeptr-> ang[Y] = cubeptr-> ang[Z] = DEGtoANG(0.0);
    	cubeptr-> angx = cubeptr-> angz = DEGtoANG( 0.0);
    	cubeptr-> angx_up = cubeptr-> angz_up = DEGtoANG( 3.0) * (-1);
    	cubeptr-> set_count = 0;
    	cubeptr-> set_i = 0;
    	cubeptr-> unset_count = SET_COUNT / 2 ;
    	cubeptr-> unset_i = 1;
    	cubeptr-> poly = &PD_CUBE;
    	evptr-> exad = (void *)disp_cube;
    	disp_cube(evptr);
    }
     
    static void init_cube3(EVENT *evptr)
    {
    	CUBE *cubeptr;
    
    	cubeptr = (CUBE *)evptr-> user;
    	cubeptr-> pos[X] = POS_X;
    	cubeptr-> pos[Y] = POS_Y + toFIXED(50);
    	cubeptr-> pos[Z] = POS_Z + POS_Z_UP;
    	cubeptr-> ang[X] = cubeptr-> ang[Y] = cubeptr-> ang[Z] = DEGtoANG(0.0);
    	cubeptr-> angx = cubeptr-> angz = DEGtoANG( 0.0);
    	cubeptr-> angx_up = cubeptr-> angz_up = DEGtoANG( 3.0);
    	cubeptr-> set_count = 0;
    	cubeptr-> set_i = 0;
    	cubeptr-> unset_count = 0;
    	cubeptr-> unset_i = 0;
    	cubeptr-> poly = &PD_CUBE;
    	evptr-> exad = (void *)disp_cube;
    	disp_cube(evptr);
    }
     
    void *event_tbl[] = {
    	init_cube1 ,
    	init_cube2 ,
    	init_cube3
    };
    
    void set_event()
    {
    	EVENT *evptr;
    	void **exptr;
    	Uint16cnt;
    
    	slInitEvent();
    	for(exptr = event_tbl , cnt = sizeof(event_tbl) / sizeof(void *); cnt--> 0;) {
    		evptr = slSetEvent(*exptr++);
    	}
    }
    

    The following flow diagram provides a quick summary of what the event does.
    The event execution content differs between the first loop and the second and subsequent loops.
    In the figure, the contents of the first event execution are on the left, and the contents of the second and subsequent events are on the right.

    Flow 10-3 samp_10: Event execution details

    Appendix: SGL library functions that appeared in this chapter

    In this chapter, we explained the functions in the table below.

    Table 10-1 SGL library functions that appeared in this chapter
     functional type
     Function name
     Parameter
             function
    void slInitEvent void Initialize the event
    EVENT *slSetEvent void(*func)() Event list Register/add events
    void slExecuteEvent void Execute events registered in the list in order
    EVENT *slSetEventNext EVENT *evptr,void(*func)() Inserts/registers a new event immediately after the specified event
    void slCloseEvent EVENT *evptr Delete the event from the event list and return/release the attached work to the system at the same time.
    WORK *slGetWork void Secure your work area
    void slReturnWork WORK *wkptr Return/release work area
    EVENT *slGetEvent void Reserve a RAM area of the same size as the event in event RAM.
    void slReturnEvent EVENT *evptr Returns and releases the area secured by the function "slGetEvent" to the system.


  • BackForward
    SGL User's ManualPROGRAMMER'S TUTORIAL
    Copyright SEGA ENTERPRISES, LTD., 1997