
This regime makes the message queue compact and easy to handle. A message queue entry is thus defined in 'C' as follows:
struct msg_queue_entry {
void(*pa)(void *); //pointer to the appropriate MPF
void *pd; //pointer to the message data
};
*pa points to a void function whose single input parameter is a pointer to an as yet undetermined data type. *pd is a pointer-to-void. A 256-entry Message Queue is therefore declared as follows:
struct msg_queue_entry msg_queue[256];The elements of the msg_queue[] array each contain pointers pa and pd to functions and data buffers. However, further pointers to the elements of the msg_queue[] array itself are needed in order to allow messages to be taken off and put on the message queue. Pointers ps and pf point to the start and finish of the message queue itself - ie the currently occupied part of the message queue array. In fact, to make the program simpler, pf actually points to the next free element after the current end of the queue - ie the place where the next new message entry should be placed. The pointer pt is a constant. It permanently points to the highest (last) element in the message queue array.
The following diagram illustrates how these pointers are used. The shading represents the currently occupied portion of the message queue array.

Note that a 256-entry message queue can hold only 255 messages. At least one element must be considered unoccupied. This is so that ps and pf coincide only when the queue is empty and not when it is full also.
The pointers ps, pf and pt are pointers to msg_queue_entry structures and are defined as follows:
struct msg_queue_entry *ps, *pf, *pt;
extern void _far Initialise(void _far *);
#include <stddef.h> //For NULL pointer definition
struct msg_queue_entry { //DEFINE A MESSAGE QUEUE ENTRY
void(_far *pa)(void _far *); //to message processor
void _far *pd; //to message data
}
_near msg_queue[256] = //DECLARE CYCLIC MESSAGE QUEUE
{ //Place in its first element a
{Initialise, NULL} //message instructing the appli-
}; //cation to initialise itself.
struct msg_queue_entry //Queue management pointers to:
_near *pt = msg_queue + 255, //last element of queue array
_near *ps = msg_queue, //next message to be processed
_near *pf = msg_queue + 1; //where to put next new message
int active = 1; //main()'s 'on/off' switch
main() { //MESSAGE DISPATCHER FUNCTION
while(active) { //While '`active' not zeroed
if(ps != pf) { //Provided msg queue not empty,
(*ps->pa)(ps->pd); //call required msg processor
if(++ps > pt) //then advance pointer to next
ps = msg_queue; //message in queue ready for
} //the next pass.
}
}
int _far PutMsg ( //MESSAGE ENTRY FUNCTION
void(_far *pa)(void _far *), //points to message action
void _far *pd //points to message data
) {
struct msg_queue_entry _near *p = pf; //temporary pointer
if(++p > pt)
p = msg_queue; //Advance 'feeler' pointer
if(p != ps) { //If room on queue...
pf->pa = pa; //Store 'action' pointer
pf->pd = pd; //Store 'data' pointer
pf = p; //Advance to next free entry
return(0); //Means 'Message stored OK'
}
return(1); //'Couldn't store message'
}
If your curiosity persists, you may now go through a detailed explanation of the above source code.