Connecting to the Monitors API

MicroMonitor provides a hookup mechanism through which the application accesses the MicroMonitor API. This mechanism is similar in purpose to the BIOS jump table on a DOS PC. The goal is to provide a mechanism that allows the application to use some of the functionality of the monitor, without having the application know anything about where the functionality actually resides in the monitors instruction space. A second goal is to make this hookup entirely independent of the underlying CPU. Thus, the BIOS approach, which uses a CPU-specific software interrupt, is not an option.

As you recall from Chapter 4, the reset code contains a tag called moncomptr . An excerpt from that code is repeated in Listing 11.1.

Listing 11.1: Another look at moncomptr .
image from book
 coldstart:     Initialize "something" to store away a state variable.     StateOfTarget = INITIIALIZE     JumpTo warmstart moncomptr:     .long moncom warmstart:     /* Load into StateOfTarget the parameter passed      * to warmstart as if it was the C function:       * warmstart(unsigned long state).      */ 
image from book
 

The moncomptr tag is a location in the boot flash memory that does not move even if the monitor is rebuilt. The location does not change because of the tags position relative to the reset vector code. [1] The location of this pointer is at a fixed location in the monitors memory map, and that fixed location contains a pointer to the moncom() function. The location of the moncom() function can change each time the monitor is rebuilt, but that doesnt matter. Because the pointer is in a fixed location, the function is accessible.

The moncom() Function

The application must be aware of this one fixed address in the monitor ( moncomptr ) that contains a pointer to a function. This functions API is known to the application, and the hookup is made through that API. A few header files from the monitor are shared with the application, and one function, monConnect() , is provided to the application in source code form. The application does not need to know anything else about the memory map of the monitor. [2] The function in monitor space is called moncom() , for monitor communication. Listing 11.2 is a snippet of the moncom() function.

Listing 11.2: Function-to-Function Pointer Connection.
image from book
 #include "monlib.h" int moncom(int cmd, void *arg1, void *arg2, void *arg3) {     int retval;     retval = 0;     switch(cmd) {         case GETMONFUNC_PUTCHAR:             *(ulong *)arg1 = (ulong)rputchar;             break;         case GETMONFUNC_GETCHAR:             *(ulong *)arg1 = (ulong)getchar;             break;         case GETMONFUNC_GOTACHAR:             *(ulong *)arg1 = (ulong)gotachar;             break;         etc......     } } 
image from book
 

The monConnect() Function

The application knows where this moncom() function is in monitor space because a pointer to this function is stored in the well-known address (the application must know this well-known address). Now, other functions in application space can use this function to "connect" the application to the monitor, and this juncture is where monConnect() comes in. The monConnect() function takes as one of its arguments, the well-known address.

Listing 11.3: The monConnect() Function.
image from book
 static int  (*_rputchar)(), (*_getchar)(), (*_gotachar)();   /* monConnect():  * This must be the first call by the application code to talk to the  * monitor.  It is expecting three incoming function pointers:  *  * mon: Points to the monitor's _moncom function;  *      This is a "well-known" address because the monitor and  *      application code (two separately linked binaries) must  *      know it.  * lock:    Points to a function in the application code that will be  *      used by the monitor as a lock-out function (some kind of  *      semaphore in the application).    * unlock:  Points to a function in the application code that will be  *      used by the monitor as an un-lock-out function (undo  *      whatever lock-out mechanism was done by lock).  */ void monConnect(int (*mon)(), void (*lock)(), void (*unlock)()) {     /* Assign incoming lock and unlock functions... */     _monlock = lock;     _monunlock = unlock;     /* If the mon pointer is non-zero, then make the      * mon_ connections...      */     if (mon) {         _moncom = mon;     /* Make the connections between "mon_" functions that are      * symbolically accessible by the application and the      * corresponding functions that exist in the monitor...      */         _moncom(GETMONFUNC_PUTCHAR,&_rputchar,0,0);         _moncom(GETMONFUNC_GETCHAR,&_getchar,0,0);         _moncom(GETMONFUNC_GOTACHAR,&_gotachar,0,0);     } } 
image from book
 

Both the moncom() function in the monitor and the monConnect() function in the application compile with the same monlib.h file. This header file contains all of the GETMONFUNC_XXX definitions. When the application calls monConnect , the application connects the function pointers _rputchar , _getchar , and _gotachar to the corresponding rputchar , getchar , and gotachar functions in the monitors space. At this point, the application could just use those function pointers. However, it would be nicer for the application to have functions (not pointers) to access the monitor code. Moreover, if the monitor is used in a multitasking environment, then it (and all of its API) becomes a shared resource requiring some form of synchronized access control. If the application always accessed the monitor through a function, then that function could wrap the monitor access with a semaphore or some other synchronization mechanism prior to entering monitor code space. This code resides in application space and can be used to call the monitor rputchar service. The code in Listing 11.4 implements this kind of protection.

Listing 11.4: Adding Locking to putchar () .
image from book
 int mon_putchar(uchar c) {     int ret;          if (_monlock)         _monlock();     ret = _rputchar(c);     if (_monunlock)         _monunlock();     return(ret); } 
image from book
 

The monitor synchronization functions are optional and would be set up using the second and third arguments of the monConnect() function (see Listing 11.3).

To summarize the mechanism:

  • At startup, the application calls monConnect() , passing it the well-known address and pointers to lock and unlock functions (NULL pointers will effectively disable this synchronization feature.).

  • The monConnect() function calls moncom() in monitor space through the well-known address. Each call to moncom() loads a function pointer in application space with the address of the corresponding function in monitor space, using the GETMONFUNC_XXX macro referenced in the call to moncom() .

  • When monConnect() completes, the application can call directly to a full set of mon_xxx functions (like mon_putchar() , see Listing 11.4) in application space.

Note 

monConnect() establishes a lot more than the three functional connections shown in Listing 11.3. The other connections are all constructed in the same way as the three shown in this example.

[1] The reset vector is at a fixed location, and moncomptr is at a fixed position relative to the reset vec tor address; hence, the address of moncomptr is fixed.

[2] Not quite true but accurate for the context of this discussion. The application does need to know of the monitors memory map so that it does not use space that is already occupied by the monitor.



Embedded Systems Firmware Demystified
Embedded Systems Firmware Demystified (With CD-ROM)
ISBN: 1578200997
EAN: 2147483647
Year: 2002
Pages: 118
Authors: Ed Sutter

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net