@DATABASE AmigaMail @NODE MAIN "I-43: Callback Hooks" @TOC "Table_of_Contents/I" by David Junod The callback features of Release 2 provide a standard means for applications to extend the functionality of libraries, devices, and their applications. This standard makes it easy for the operating system to use custom modules from different high level programming languages as part of the operating system. For example, the layers library, which takes care of treating a display as a series of layered regions, allows an application to attach a pattern function to a display layer. Instead of filling in the background of a layer with the background color, the layers library calls the custom pattern function which fills in the layer display with a custom background pattern. @{" Callback Hook Functions and Structures " link I-43-1} @{" Simple Callback Hook Usage " link I-43-2} @ENDNODE @NODE I-43-1 "Callback Hook Functions and Structures" An application passes a custom function in the form of a callback Hook (from ): /* Standard hook structure */ struct Hook { struct MinNode h_MinNode; ULONG (*h_Entry)(); /* stub function entry point */ ULONG (*h_SubEntry)(); /* the custom function entry point */ VOID *h_Data; /* owner specific */ }; h_MinNode - This field is reserved for use by the module that will call the Hook. h_Entry - This is the address of the Hook stub. When the OS calls a callback function, it puts parameters for the callback function in CPU registers A0, A1, and A2. This makes it tough for higher level language programmers to use a callback function because most higher level languages don't have a way to manipulate CPU registers directly. The solution is a stub function which first copies the parameters from the CPU registers to a place where a high level language function can get to them. The stub function then calls the callback function. Typically, the stub pushes the registers onto the stack in a specific order and the high level language callback function pops them off the stack. h_SubEntry - This is the address of the actual callback function that the application has defined. The stub calls this function. h_Data - This field is for the application to use. It could point to a global storage structure that the callback function utilizes. There is only one function defined in utility.library for callback functions. ULONG CallHookPkt(struct Hook *hook, VOID *object, VOID *paramPkt); A0 A2 A1 This function invokes a standard callback Hook function. @ENDNODE @NODE I-43-2 "Simple Callback Hook Usage" A Hook function must accept the following three parameters in these specific registers: A0 Pointer to the Hook structure. A2 Pointer to an object to manipulate. The object is context specific. A1 Pointer to a message packet. This is also context specific. For a callback function written in C, the parameters should appear in this order: myCallbackFunction(Pointer to Hook (A0), Pointer to Object (A2), Pointer to message (A1)); This is because the standard C stub pushes the parameters onto the stack in the following order: A1, A2, A0. The following assembly language routine is a callback stub for C: INCLUDE 'exec/types.i' INCLUDE 'utility/hooks.i' xdef _hookEntry _hookEntry: move.l a1,-(sp) ; push message packet pointer move.l a2,-(sp) ; push object pointer move.l a0,-(sp) ; push hook pointer move.l h_SubEntry(a0),a0 ; fetch actual Hook entry point ... jsr (a0) ; and call it lea 12(sp),sp ; fix stack rts If your C compiler supports registerized parameters, your callback functions can get the parameters directly from the CPU registers instead of having to use a stub to push them on the stack. The following C language routine uses registerized parameters to put parameters in the right registers. This routine requires a C compiler that supports registerized parameters. #include #include #define ASM __asm #define REG(x) register __ ## x /* This function converts register-parameter hook calling * convention into standard C conventions. It requires a C * compiler that supports registerized parameters, such as * SAS/C 5.xx or greater. */ ULONG ASM hookEntry(REG(a0) struct Hook *h, REG(a2) VOID *o, REG(a1) VOID *msg) { return ((*h->h_SubEntry)(h, o, msg)); } A callback function is executed on the context of the module that invoked it. This usually means that callback functions cannot call functions that need to look at environment specific data. For example, printf() needs to look at the current process's input and output stream. Entities like Intuition have no input and output stream. The limitations on a callback function depend heavily upon the subsystem that is using them. See that subsystem's documentation for more information. For the callback function to access any of its global data, it needs to make sure the CPU can find the function's data segment. It does this by forcing the function to load the offset for the program's data segment into CPU register A4. See your compiler documentation for details. The following is a simple function that can be used as a callback Hook. ULONG MyFunction (struct Hook *h, VOID *o, VOID *msg) { /* A SASC and Manx function that obtains access to the global data segment */ geta4(); /* Debugging function to send a string to the serial port */ KPrintF("Inside MyFunction()\n"); return (1); } The next step is to initialize the Hook for use. This basically means that the fields of the Hook structure must be filled with appropriate values. The following simple function initializes a Hook structure. /* This simple function is used to initialize a Hook */ VOID InitHook (struct Hook *h, ULONG (*func)(), VOID *data) { /* Make sure a pointer was passed */ if (h) { /* Fill in the hook fields */ h->h_Entry = (ULONG (*)()) hookEntry; h->h_SubEntry = func; h->h_Data = data; } } @{"Hooks1.c" link I-43/Hooks1.c/MAIN} is a simple example of initializing and using a callback Hook function. @ENDNODE