Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Multiuser DOS for Control Systems: Part II


MAY92: MULTIUSER DOS FOR CONTROL SYSTEMS: PART II

Richard is a senior engineer at Rockwell International Graphic Systems. He has a BSEE from the University of Illinois in Chicago and a MSMC from Illinois Institute of Technology. He can be contacted at 9616 South 49th Avenue, Oak Lawn, IL 60453.


In last month's installment of this two-part article, I described Digital Research's Multiuser DOS (DRMDOS) in general terms, focusing on the interface library, interprocess communication, multitasking features, and the like. This month, I present specific details of how I use DRMDOS as the basis for industrial control systems on the factory floor. Also, I'll describe how to use the interface library to develop a system that consists of three independent processes. The first process is the owner of a memory-resident database, the second an I/O process, and the third a logic function that monitors data in the input portion of the database for changes.

The first process will use a queue to pass the pointer to the base of the database to any other process that wishes to access the database. The I/O process has two responsibilities: It must read the contents of a digital input port and deposit them into a location in the database. It must also read another location in the database and write the data to a digital output port. This process will utilize the delay function and the priority function to ensure that it runs on a timely basis. The last function, the logic function, monitors the data in the input portion of the database for changes. When a change is encountered, the data is operated on to produce an output that is put back into the database.

Database Process

The code for the database process is shown in Listing Three. (Note: Listing One and Listing Two were included with last month's installment.) This is a simple memory-resident database. It begins by declaring local variables. A call is made using the s_memory() function to obtain a pointer to a block of system memory. This is saved as a member of a union. If the pointer cones back with a NULL value, there is not enough system memory available, and the program exits. The program then creates and opens a queue called "database." This queue will be used to pass the database pointer to any process that wishes to access the database. The "result" values should be used for error checking and logging. Next, the pointer to the base of the database is written to the queue. The union is used because the 4-byte pointer must be broken into bytes to be written to the byte-wide queue. The process then makes a c_detach() call to detach from the system console. Finally, it goes into a loop that simply delays the process. In a more realistic system, the memory-resident database would be initialized at startup or loaded with data that has been saved to disk. This is an approach I have taken. In the while loop, the data is periodically written to the hard disk along with a checksum. The next time the system is started. the data is read in and put into the database. Another issue that must be addressed is contention for data. If necessary, a set of semaphores could be used to control access to certain areas of the database.

If a very large database is needed, it might be necessary to resort to a disk-based system. Another alternative is to use a data server process that is accessed through a queue. To implement this type of system would require exclusive read/write access which could be controlled by a database semaphore. A process would first get the semaphore and then submit a request for data through a queue and get the resultant data from another queue.

Database Support Code

There are three operations that can be performed with respect to the database. They are dbopen(), dbread(), and dbwrite(). In order for a process to access the database, it must open it. Once the database is open, the process can perform read or write operations on the database. Listing Four shows the code that is supplied to interact with the database.

The first function is dbopen(). This function is used to read the database pointer from the queue. The global pointer dbase_ptr is first assigned a NULL value. A while loop is then entered that calls the function opens dbase() until it returns a non-NULL value. This function declares some local variables, including a union to hold the pointer from the queue. A while loop is then used to wait for the queue to be created. This is a multitasking system, so this function might be called before the queue is actually created by the database process. Once the queue is opened, the information is read into the union. The message buffer is then written back to the queue to allow other processes to obtain the pointer. Finally, the pointer is returned. Two other functions allow access to the data in the database. The dbwrite() function allows a word in the database to be set to a desired value. These are the two parameters passed to the function. The previous contents are destroyed. The dbread() function takes a database address as a parameter and returns the value in that location.

There are several other possible functions that can be constructed to ease interaction with the database. It is useful to be able to easily set or clear individual bits in a database word and to read and write a range of words.

I/O Scanner Process

The I/O scanner process reads data from a digital input board that has four channels of both 8-bit input and 8-bit output. The base address of the board is 0x300 in the I/O map of the CPU. In this example, the data will be read from the first input port and stored in location 0 of the database. The information in location 1 of the database will be written to the last output port. The code for the I/O process is contained in Listing Five. The process starts out by changing its priority to 199, which puts it above all other normal user processes. It then uses a database support call to open a link to the central database. Next, it detaches from the console to become a background process. Finally, a loop is entered in which the actual I/O is performed. The inp() function is used to read the byte contained at the digital input port. This data is then written to the central database using the dbwrite() call. The data to be written to the output port is read from the database and written to the output port using the outp() call. The program then executes a p_delay() call to allow other processes to run. This delay call delays the process for at least 3*16.67 ms, or about 50 ms. This function will continue running as long as the computer is running.

Logic Process

This process is simple compared to those normally used in a true control system. It is only monitoring one input byte and performing a simple logic operation. In a real system there would be more extensive calculations to do. This is the reason the data is monitored for change rather than simply executing the logic on the data. If the input data is not changed, there is no need to do more calculations. The code for this process is contained in Listing Six. The process starts out by declaring local variables and then establishes a link to the memory-resident database. Next, it detaches from the console to become a background process. Finally, a loop is entered in which the input data (from the I/O process) is read and compared to the last data. If a change has occurred, the data is inverted and written back to the database. This new data will be used by the I/O process and written to the output port. At the end of each loop the p_dispatch() call is made to allow other processes to run.

Getting Started

Getting things started is pretty simple. It can be done as a batch file or from the command line. The commands are the same at any rate. First the database is started by typing dbase. Next, the I/O process can be started by typing ioboard. Finally the logic process can be started by typing logic. If everything goes okay, a normal DRMDOS prompt will be all that is left on the screen. Now is happening? One simple way is to hook up signals to the input port on the I/O board and check the outputs. This is fine if everything is working, but if not, this method does not tell us much. I do quite a bit of debugging using the QuickC environment. The other processes are compiled and run as above, but the one being debugged is run under QuickC. There are also other methods of seeing what is going on. I've written a monitor program that allows the database to be viewed and updated in binary, decimal, and hexidecimal. There are also system monitor programs available for watching over system resources. These can be used to see if queues are opened and if messages are being put into and taken out of the queue. One such system is available from Digital Research as part of the developer~s package.

Conclusion

I've presented a basic overview of using Digital Research's Multiuser DOS for industrial control. The basic functions needed to make use of the multitasking features are contained in the listings. They do not include the error checking necessary for a robust system. DRMDOS has proven capable of handling the demands of "lightweight" real-time systems. The information provided here can be expanded to provide a graphical user interface module that displays the condition of the database and allows the user to interact with the system. Another feature I've taken advantage of under DRMDOS is the use of intelligent multichannel serial boards to allow communications. It is also possible to network the control computer onto a LAN. DRMDOS is multiuser, so terminals can be connected to the system to allow remote monitoring of controls used in harsh environments. Although DRMDOS was not originally designed to be used on the factory floor, it seems to be adapting to its new environment just fine.

DDJ



_MULTIUSER DOS FOR CONTROL SYSTEMS, PART II_
by Richard Kryszak


[LISTING ONE]
<a name="0119_000b">

/* file name: system.c */

#include <dos.h>
#include "queues.h"
#include <stdio.h>

/*===============*/
/* local defines */
/*===============*/
#define CCPM        0xE0        /* cdos call int value */
#define C_DETACH    0x93        /* console detach CL register value */
#define P_DELAY     0x8D        /* process delay CL register value */
#define P_DISPATCH  0x8E        /* process dispatch CL register value */
#define P_PRIOR     0x91        /* process priority CL register value */
#define Q_CREAD     0x8A        /* queue cread CL register value */
#define Q_CWRITE    0x8C        /* queue cwrite CL register value */
#define Q_MAKE      0x86        /* queue make CL register value */
#define Q_OPEN      0x87        /* queue open CL register value */
#define Q_READ      0x89        /* queue read CL register value */
#define Q_WRITE     0x8B        /* queue write CL register value */
#define S_MEMORY    0x59        /* system memory allocation request */

/*=====================*/
/* function prototypes */
/*=====================*/
unsigned int c_detach(void);
void p_dispatch(void);
void p_priority(unsigned char data);
void p_delay(unsigned int del);
unsigned int far *  s_memory(int mem_size);
int q_make(struct q_descriptor *descript_ptr,
       unsigned int msg_length,
       unsigned int num_msg,
       char que_name[8],
       int *err_ptr);
int q_open(struct q_parameter_blk *param_blk_ptr,
       char que_name[8],
       int *err_ptr);
int q_read(struct q_parameter_blk *param_blk_ptr,
       unsigned char *buff_ptr,
       int *err_ptr);
int q_write(struct q_parameter_blk *param_blk_ptr,
        unsigned char *buff_ptr,
        int *err_ptr);
int q_cread(struct q_parameter_blk *param_blk_ptr,
        unsigned char *buff_ptr,
        int *err_ptr);
int q_cwrite(struct q_parameter_blk *param_blk_ptr,
         unsigned char *buff_ptr,
         int *err_ptr);

/*======================*/
/* function definitions */
/*======================*/
unsigned int c_detach()
   { union REGS inregs,outregs;

     inregs.h.cl = C_DETACH;                    /* detach function call */
     int86(CCPM,&inregs,&outregs);              /* call cdos */
     return(outregs.x.ax);                      /* return call status */
   }
void p_dispatch()
   { union  REGS inregs,outregs;

     inregs.h.cl = P_DISPATCH;                  /* dispatch function call */
     int86(CCPM,&inregs,&outregs);              /* call cdos */
   }
void p_priority(unsigned char priority)
   { union REGS inregs,outregs;

     inregs.h.cl = P_PRIOR;                     /* priority change call */
     inregs.h.dl = priority;                    /* desired priority */
     int86(CCPM,&inregs,&outregs);              /* call cdos */
   }
void p_delay(unsigned int del)
   { union  REGS inregs,outregs;
     inregs.h.cl = P_DELAY;                     /* delay function call */
     inregs.x.dx = del;                         /* number of ticks */
     int86(CCPM,&inregs,&outregs);              /* call cdos */
   }
unsigned int far * s_memory(int mem_size)
   { union  REGS inregs,outregs;
     struct SREGS seg_regs;                     /* segment registers */
     unsigned int _far *mem_ptr=NULL;           /* pointer to memory block */
     mem_size *= 2;                             /* compute # of bytes */
     inregs.h.cl = S_MEMORY;                    /* system memory allocation */
     inregs.x.dx = mem_size;                    /* # of bytes requested */
     int86x(CCPM,&inregs,&outregs,&seg_regs);   /* call cdos */
     if(outregs.x.ax == 0xFFFF)                 /* if not successful */
    { return(NULL);                         /* return a null pointer */
    }
     mem_ptr = (unsigned int far *)
            ((0x10000 * seg_regs.es)
             + outregs.x.ax);           /* convert into a pointer */
     return(mem_ptr);                           /* return the pointer */
   }
int q_make(struct q_descriptor *descript_ptr,
       unsigned int msg_length,
       unsigned int num_msg,
       char que_name[8],
       int *err_ptr)
   { int int86_error;                           /* return status */
     int i;                                     /* index variable */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     descript_ptr->internal_1 = 0;              /* must be 0 */
     descript_ptr->internal_2 = 0;              /* must be 0 */
     descript_ptr->internal_3 = 0;              /* must be 0 */
     descript_ptr->internal_4 = 0;              /* must be 0 */
     descript_ptr->internal_5 = 0;              /* must be 0 */
     descript_ptr->internal_6 = 0;              /* must be 0 */
     descript_ptr->msglen = msg_length;         /* add message length */
     descript_ptr->nmsgs = num_msg;             /* add number of messages */
     descript_ptr->flags = 0;                   /* no flags used */
     for(i = 0; i < 8;  ++i)                    /* copy queue name */
    { descript_ptr->name[i]=que_name[i];
    }
     descript_ptr->buffer = 0;                  /* buffer in system area */
     inregs.h.cl = Q_MAKE;                      /* queue make call */
     inregs.x.dx = FP_OFF(descript_ptr);        /* put offset into dx */
     int86_error=int86x(CCPM,&inregs,
            &outregs,&seg_regs);    /* call cdos */
     *err_ptr = outregs.x.cx;                   /* write error code */
     return(int86_error);                       /* int86 return status */
   }
int q_open(struct q_parameter_blk *param_blk_ptr,
       char que_name[8],
       int *err_ptr)
   { int int86_error;                           /* return status */
     int i;                                     /* index variable */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     param_blk_ptr->internal_1 = 0;             /* must be 0 */
     param_blk_ptr->internal_2 = 0;             /* must be 0 */
     for(i = 0; i < 8;  ++i)
    { param_blk_ptr->name[i] = que_name[i]; /* copy queue name */
    }
     inregs.h.cl = Q_OPEN;                      /* q_open call */
     inregs.x.dx = FP_OFF(param_blk_ptr);       /* put offset into dx */
     int86_error=int86x(CCPM,&inregs,
            &outregs,&seg_regs);    /* call cdos */
     *err_ptr = outregs.x.cx;                   /* write error code */
     return(int86_error);                       /* int86 return status */
   }
int q_write(struct q_parameter_blk *param_blk_ptr,
        unsigned char *buff_ptr,
        int *err_ptr)
   { int int86_error;                           /* return status */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     param_blk_ptr->buffer=FP_OFF(buff_ptr);    /* pointer to the buffer */
     inregs.h.cl = Q_WRITE;                     /* q_write call */
     inregs.x.dx = FP_OFF(param_blk_ptr);       /* put offset into dx */
     int86_error=int86x(CCPM,&inregs, &outregs,&seg_regs);    /* call cdos */
     *err_ptr = outregs.x.cx;                   /* write error code */
     return(int86_error);                       /* int86 return status */
   }
int q_read(struct q_parameter_blk *param_blk_ptr,
       unsigned char *buff_ptr,
       int *err_ptr)
   { unsigned int int86_error;                  /* return status */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     param_blk_ptr->buffer=FP_OFF(buff_ptr);    /* pointer to the buffer */
     inregs.h.cl = Q_READ;                      /* q_read call */
     inregs.x.dx = FP_OFF(param_blk_ptr);       /* put offset into dx */
     int86_error=int86x(CCPM,&inregs,&outregs,&seg_regs); /* int86 call */
     *err_ptr = outregs.x.cx;                   /* write error code */
     return(int86_error);                       /* int86 return status */
   }
int q_cwrite(struct q_parameter_blk *param_blk_ptr,unsigned char *buff_ptr,
                                                                 int *err_ptr)
   { int int86_error;                           /* return status */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     param_blk_ptr->buffer=FP_OFF(buff_ptr);    /* pointer to the buffer */
     inregs.h.cl = Q_CWRITE;                    /* q_write call */
     inregs.x.dx = FP_OFF(param_blk_ptr);       /* put offset into dx */
     int86_error=int86x(CCPM,&inregs,&outregs,&seg_regs);    /* call cdos */
     *err_ptr = outregs.x.cx;                   /* write error code */
     return(int86_error);                       /* int86 return status */
   }
int q_cread(struct q_parameter_blk *param_blk_ptr,unsigned char *buff_ptr,
                                                        int *err_ptr)
   { int int86_error;                           /* return status */
     union REGS inregs, outregs;                /* processor registers */
     struct SREGS seg_regs;                     /* segment registers */
     segread(&seg_regs);                        /* read segment registers */
     param_blk_ptr->buffer=FP_OFF(buff_ptr);    /* pointer to the buffer */
     inregs.h.cl = Q_CREAD;                     /* q_cread call */
     inregs.x.dx = FP_OFF(param_blk_ptr);       /* put offset into dx */
     int86_error=int86x(CCPM,&inregs,&outregs,&seg_regs);    /* call cdos */
     *err_ptr = outregs.x.cx;                   /* write error */
     return(int86_error);                       /* int86 return status */
   }




<a name="0119_000c">
<a name="0119_000d">
[LISTING TWO]
<a name="0119_000d">

/* file name: queues.h */

struct q_descriptor
{ unsigned int internal_1;      /* for internal use ; must be zero */
  unsigned int internal_2;      /* for internal use ; must be zero */
  int flags;                    /* for internal use ; queue flags */
  char name[8];                 /* queue name */
  int msglen;                   /* number of bytes in each logical message */
  int nmsgs;                    /* maximum number of messages supported */
  unsigned int  internal_3;     /* for internal use ; must be zero */
  unsigned int  internal_4;     /* for internal use ; must be zero */
  unsigned int  internal_5;     /* for internal use ; must be zero */
  unsigned int  internal_6;     /* for internal use ; must be zero */
  unsigned int  buffer;         /* address of the queue buffer */
 };

struct q_parameter_blk
{ unsigned int internal_1;      /* for internal use ; must be zero */
  int queueid;                  /* queue number field ; filled by q_open */
  unsigned int internal_2;      /* for internal use ; must be zero */
  unsigned int buffer;          /* offset of queue message buffer */
  char name[8];                 /* queue name */
 };




<a name="0119_000e">
<a name="0119_000f">
[LISTING THREE]
<a name="0119_000f">

/* file name: database.c */

#include <stdio.h>
#include "queues.h"

/*=====================*/
/* function prototypes */
/*=====================*/
void main(void);

/*===============*/
/* local defines */
/*===============*/
#define Q_DEPTH         1               /* queue contains 1 message */
#define DBASE_SIZE      2048            /* size of the database */
#define TRUE            1

/*================================*/
/* external function declarations */
/*================================*/
extern unsigned int c_detach(void);
extern void p_delay(unsigned int del);
extern unsigned int far *s_memory(int mem_size);
extern int q_make(struct q_descriptor *descript_ptr,
          unsigned int msg_length,
          unsigned int num_msg,
          char que_name[8],
          int *err_ptr);
extern int q_open(struct q_parameter_blk *param_blk_ptr,
          char que_name[8],
          int *err_ptr);
extern int q_write(struct q_parameter_blk *param_blk_ptr,
           unsigned char *buff_ptr,
           int *err_ptr);
/*=====================*/
/* function definition */
/*=====================*/
void main()
   { int result;                                /* result of q_make */
     int error_type;                            /* cdos return code */
     union base
    { unsigned int far *base_ptr;
      unsigned char base[sizeof(unsigned int far *)];
    }base_union;                             /* composite pointer */
     struct q_descriptor dbase_descript;         /* descriptor block */
     struct q_parameter_blk dbase_parameters;    /* parameter block */
     base_union.base_ptr = s_memory(DBASE_SIZE); /* request system memory */
     if(base_union.base_ptr == NULL)             /* if NULL pointer */
    { puts("No System Memory Available");    /* print an error message */
      exit(-1);                              /* exit, memory error */
    }
     result = q_make(&dbase_descript,            /* pointer to descriptor */
             sizeof(base_union),         /* length of messages */
              Q_DEPTH,               /* number of messages */
              "database",            /* queue name */
              &error_type);          /* error return */
      result = q_open(&dbase_parameters,     /* pointer to parameter */
              "database",            /* queue name */
              &error_type);          /* error return */
      result = q_cwrite(&dbase_parameters,   /* write to queue */
                &base_union.base[0], /* pointer to database */
                &error_type);        /* error return */
      c_detach();                            /* detach from console */
      while(TRUE)                            /* loop */
        { p_delay(1800);                 /* delay 30 seconds */
        }
    }
/* NOTE: DATABASE.EXE is made up of database.c and system.c */




<a name="0119_0010">
<a name="0119_0011">
[LISTING FOUR]
<a name="0119_0011">

/* file name: dbsuport.c */

#include <stdio.h>
#include "queues.h"

/*=====================*/
/* function prototypes */
/*=====================*/
void dbopen(void);
unsigned int dbread(int index);
void dbwrit(int index, unsigned int value);
unsigned int far *open_dbase(void);

/*==============================*/
/* external function prototypes */
/*==============================*/
extern int q_open(struct q_parameter_blk *param_blk_ptr,
       char que_name[8],
       int *err_ptr);
extern void p_dispatch(void);
extern int q_read(struct q_parameter_blk *param_blk_ptr,
          unsigned char *buff_ptr,
          int *err_ptr);
extern int q_write(struct q_parameter_blk *param_blk_ptr,
           unsigned char *buff_ptr,
           int *err_ptr);

/*================*/
/* global storage */
/*================*/
unsigned int far *dbase_ptr;                    /* pointer to database */

/*===============*/
/* local defines */
/*===============*/
#define FAILURE         1
#define SUCCESS         0

/*======================*/
/* function definitions */
/*======================*/
void dbopen()
   { dbase_ptr = NULL;                             /* initialize pointer */
     while(dbase_ptr == NULL)                   /* loop while still NULL */
    { dbase_ptr = open_dbase();             /* call open database */
    }
   }
void dbwrit(int index, unsigned int value)
    { *(dbase_ptr + index) = value;        /* write value to database */
    }
unsigned int dbread(int index)
    { return(*(dbase_ptr + index));        /* return value at index */
    }
unsigned int far *open_dbase()
   { struct q_parameter_blk dbase_parameters;   /* parameter block */
     int result;                                /* result of q_make */
     int error_type;                            /* cdos return code */
     union base
    { unsigned int far *base_ptr;
      unsigned char base[sizeof(unsigned int far *)];
    }base_union;                            /* composite pointer */
     result = FAILURE;                          /* preset the variable */
     while(result != SUCCESS)                   /* loop til we can open */
    { result = q_open(&dbase_parameters,    /* pointer to param block */
              "database",           /* queue name */
              &error_type);         /* error type return */
      p_dispatch();                         /* let someone else run */
    }
     result = q_read(&dbase_parameters,         /* read the dbase_queue */
             &base_union.base[0],       /* msg read is in union */
             &error_type);              /* error return type */
     result = q_write(&dbase_parameters,        /* write to dbase_queue */
              &base_union.base[0],      /* msg sent is pointer */
              &error_type);             /* error return type */
     return(base_union.base_ptr);               /* return the pointer */
   }




<a name="0119_0012">
<a name="0119_0013">
[LISTING FIVE]
<a name="0119_0013">

/* file name ioboard.c */

#include <conio.h>

/*====================*/
/* function prototype */
/*====================*/
void main(void);

/*==============================*/
/* external function prototypes */
/*==============================*/
extern void dbopen(void);
extern void dbwrit(int index, unsigned int value);
extern unsigned int dbread(int index);
extern unsigned int c_detach(void);
extern void p_priority(unsigned char data);
extern void p_delay(unsigned int del);

/*===============*/
/* local defines */
/*===============*/
#define INPUT_BASE_ADDR  0x300                  /* hardware input address */
#define OUTPUT_BASE_ADDR 0x300                  /* hardware output address */
#define DBASE_WRITE_ADDR 0                      /* database write location */
#define DBASE_READ_ADDR  1                      /* database read location */
#define TRUE             1

/*=====================*/
/* function definition */
/*=====================*/
void main()
   { unsigned int temp_data;                    /* for reading database */
     p_priority(199);                           /* set priority */
     dbopen();                                  /* link to database */
     c_detach();                                /* detach from console */
     while(TRUE)
    { temp_data = inp(INPUT_BASE_ADDR);     /* read data from port */
      dbwrit(DBASE_WRITE_ADDR, temp_data);  /* write to the database */
      temp_data = dbread(DBASE_READ_ADDR);  /* read data from database */
      outp(OUTPUT_BASE_ADDR+3, temp_data);  /* write to output port */
      p_delay(3);                           /* delay for 50 ms */
    }
   }

/* NOTE: IOBOARD.EXE is made up of ioboard.c, system.c, and dbsuport.c */




<a name="0119_0014">
<a name="0119_0015">
[LISTING SIX]
<a name="0119_0015">

/* file name logic.c */

/*====================*/
/* function prototype */
/*====================*/
void main(void);

/*==============================*/
/* external function prototypes */
/*==============================*/
extern void dbopen(void);
extern unsigned int dbread(int index);
extern void dbwrit(int index, unsigned int value);
extern unsigned int c_detach(void);
extern void p_dispatch(void);

/*===============*/
/* local defines */
/*===============*/
#define DATA_IN  0                      /* data written by I/O process */
#define DATA_OUT 1                      /* data read by I/O process */
#define TRUE     1

/*=====================*/
/* function definition */
/*=====================*/
void main()
   { static unsigned int last_data_read;        /* old data retainer */
     unsigned int temp_data;                    /* for reading database */

     dbopen();                                  /* link to database */
     c_detach();                                /* detach from console */
     while(TRUE)                                /* continuous loop */
    { temp_data = dbread(DATA_IN);          /* read input data */
      if(temp_data ^ last_data_read)        /* if there was a change */
         { dbwrit(DATA_OUT, ~temp_data);    /* write the to the port */
           last_data_read = temp_data;      /* save the new value */
         }
      p_dispatch();                         /* let another process run */
    }
   }
/* NOTE: LOGIC.EXE is made up of logic.c, system.c, and dbsuport.c */


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.