miSim DE Back Index Next

Chapter 6 - Technical Details Continued

Developing Plugins

To develop Plugins you will need some Java experience. You will also need a Java SDK (Software Development Kit) and the miSim DE package. The Plugin interface defined in Version 1.7+ is compatible with Version 2.x - and so the JavaDocs and source code available for the earlier version can help provide useful information and examples for writing Plugins. Plugins are developed as fairly simple Java classes that must extend the basic Plugin class with their own functionality.

However, the basic Plugin class only provides facilities to display and configure the Plugin. To make the Plugin interact with the simulated microcontroller, additional interfaces must be implemented. Which interface needs to be implemented depends on the functions that your Plugin wishes to perform. The interfaces will also decide how much your Plugin slows down the simulator. The following table indicates which interfaces you must implement for different functions.

If your Plugin wishes to:ImplementPlugin Type
Respond to output generated by the microcontroller.Port ListenerMedium
Update only when simulation stops - for example the Memory MonitorProcessor ListenerFast
Perform actions independantly of the microcontroller's output, but in time with its clockClock ListenerSlow

In addition, if you wish to connect many signals to different port pins, you may wish to use the Port Mapper class.

Note that the Clock Listener interface is recommended for performing actions at the same time as the processor - rather than using a Java thread. This technique slows down simulation a bit more than threading would. However the unpredictable nature of Java threads and the difficulty of providing robust synchronisation with microcontroller activities makes the Clock Listener interface both more portable and more efficient.

Installing Your Plugin

Plugins installed through Web Update automatically let miSim DE know about themselves by altering the settings file. When you write your own Plugin, you will need to make the appropriate changes by hand, and to re-start the program to be able to use or test your plugin.

Having written and compiled a Plugin, the following steps must be carried out to install it. First the compiled class files must be on your classpath. The miSim launcher (The 'StartMiSim' class) includes the local directory in the class path, so the class files may be copied into the same directory as miSim DE itself. Alternatively, you may edit your CLASSPATH environment variable to include the directory that.

Having included the Plugin classes in your classpath, the next step is to inform miSim DE of the new classes. The 'settings.txt' file in the miSim DE directory is a text file that controls various settings of the simulator, including the Available Plugins list. Again, it is probably worth making a backup of this file before editing it. Having made a backup, open the file to edit, and scroll down to the Available Plugins section, which will look something like this:

[available-plugins]
Television = picem.plugins.TVFrame
Simple Led = picem.plugins.LedPlugin
Better Led = picem.plugins.BetterLed:Port A:0
.
.
.

To add a new Plugin, simply add another line to this list. Each line lists the name of the Plugin, and the Java class that implements it. If the Plugin is configurable, then the default configuration is given after the class name, separated by a colon (':').

Therefore, if your plugin is in a file 'MyPlugin.java' that compiles to 'MyPlugin.class', and it takes a single port signal from the microcontroller, you might add a line like this:

My New Plugin = MyPlugin:Port A:0

This will give the plugin the name 'My New Plugin' and give it a default configuration of 'Port A:0'. Note that Java class names are case sensitive, so be sure to get the class name right in the settings file.

This only adds the Plugin to the list of available Plugins - you can add it to a Circuit by clicking the 'Add New' button on the Plugins Window. To manually add it to a Circuit (particularly the default 'All Plugins' circuit) examine the settings file again. After the 'available-plugins' section there are one or more subsections (indicated by a colon ':' character before their name) which each define a separate circuit and might look like something this:

[:Circuit Name]
Television = Television
Switch One = Simple Switch:Port A:1
Switch Two = Simple Switch:Port A:2

Each line adds a plugin to the circuit's plugin list. The format is to list the dislpay name of the Plugin in this circuit and then the actual name of the Plugin (as defined in the Available Plugins list) and optionally its configuration string, again separated by a colon (':').

Once these changes are made to the settings.txt file, save it and re-start miSim DE. Your new Plugin should be shown on the current plugins list, ready to use.


Plugin Class

The Plugin class is an abstract Java class that provides the skeleton of Plugin behaviour. To implement a Plugin, you must write a class that at least extends the Plugin class and provides the necessary methods that define your Plugin. In addition, you may need to implement some of the interfaces listed below.

The basic outline of a Plugin class is shown here:

import java.awt.*;
import picem.*;
import picem.util.*;

public class MyPlugin extends Plugin {

  /**
   * Constructor for the plugin. This calls the superclass constructor.
   */
   public MyPlugin( Component owner, MachineState machineState, 
                    RobustReaderIF reader, String params ) throws PicException {
      super( owner, machineState, reader, params );
   }


   .... Your Plugin methods...

}

The superclass constructor sets three member variables 'machineState', 'reader' and 'owner' to the values passed into it. These can then be referenced in any other Plugin methods. In addition the superclass constructor calls the 'init' method shown below to get the Plugin to create its user interface and initialise itself.

The MachineState class is the class that implements the majority of the microcontroller simulation functions. It provides methods for adding and removing listeners, accessing the microcontroller's memory and instructions and other functions. The RobustReaderIF class provides methods for accessing files in a consistent way, independently of whether miSim DE is being run as an Application or Applet.

There are seven methods that you must implement, each of which is shown here:

   /**
    * This is the method that is called to initialise
    * the plugin.
    * @param params Initialisation parameters for the plugin
    */
   public void init( String params ) throws PicException {

   }

In the init method, the Plugin must create and add any appropriate GUI objects. The Plugin class is an extension of the Java AWT Container class, so any standard AWT calls may be used here. In addition, the basic config is passed as a parameter. If the plugin is configurable, it should process this config, usually by invoking the readConfig method listed below at the appropriate time.

   /**
    * This is called when the user wants the Plugin to display or
    * hide itself. The plugin should add or remove itself as a port
    * listener as appropriate
    * @param visible True if the Plugin should display itself.
    */
   public void showPlugin( boolean visible ) {
  
   }

The showPlugin method is called when the user clicks 'Show' in the Plugin Window of miSim DE. It is also called when the Plugin is closed, or if the processor is changed. Usually, it should call the Container method 'setVisible(boolean)' to make the Plugin visible or hidden as appropriate. If the Plugin implements any of the Listener classes, it should also ensure that the appropriate add- or remove-listener methods are called to add the listeners when the Plugin is displayed, or remove them when it is closed. The plugin should always add itself to listeners or take other appropriate actions to connect to the processor when showPlugin is called with visible = true, as this call is also used to indicate to an already visible Plugin that the processor has changed.

   /** 
    * The User Interface calls this to see if the Plugin needs configuration
    * information, and if so whether the information can be controlled by
    * the user interface (in which case the user will edit the config string
    * by hand), or if the Plugin itself will provide a configuration 
    * interface.
    * @return PLUGIN_NONE - if this plugin needs no config information
    *         PLUGIN_GUI  - if the standard GUI should provide config editing
    *         PLUGIN_SELF - if the plugin itself will provide a config GUI
    */
   public int configType() {
   
   }

This method needs only to return the appropriate PLUGIN_ constant to let miSim DE know what sort of configuration the Plugin supports.

   /**
    * Method to retrieve the config for this plugin
    * @return The config string
    */
   public String getConfig() {

   }

This method should let miSim DE know what the current configuration string is for this Plugin. When init or readConfig are called to update the Plugin's configuration, they should store the config string if it is sucessfully applied, so that this method can return it when needed.

   /**
    * This method should read the given config parameter string and
    * update its own settings to match it.
    * If the Plugin has stated that it will edit the configs through its
    * own gui, it should display the GUI and gather the results, before
    * updating itself. In this case, the config and update parameters
    * can be ignored.
    * @param config The new configuration string, or null if the config
    *               type is PLUGIN_SELF.
    * @return Null if the parameters are OK, otherwise an
    *         explanation of what is wrong.
    */
   public String readConfig( String config ) {
   
   }

Read config is called when the 'Set' button is clicked in the miSim DE Plugin Window. If the plugin provides its own configuration GUI, it should show it and once the user has completed the changes, return. Otherwise, it should check that the given configuration string is valid and apply it. If the configuration is valid, the method should return null, otherwise, it should return a String that explains what was wrong with the configuration. This method should store the valid configuration string so that the 'getConfig' method above can return it when required.

   /**
    * This method should return any information appropriate to this plugin.
    * If the config parameter is to be edited by the standard GUI, the
    * help text should explain what the parameters mean.
    * @return The about text for this plugin
    */
   public String getAbout() {
   
   }

This method only needs to return a string that describes the Plugin and what it does. The message is shown when the Plugin is selected in the Current Plugin list on the miSim DE Plugin Window, or when 'About' is clicked.

 
   /**
    * Return more detailed information on the configuration string format
    * for this plugin.
    * @return The help text for this plugin
    */
   public String getHelp() {

   }

This method only needs to return a string that describes the format of the configuration string. It is shown when the user clicks 'Help' on the miSim DE Plugin Window. If this Plugin is not configurable, this method can simply return null.


PortListener Interface

The PortListener interface defines two methods that allow the listener to respond to output from the microcontroller. A PortListener is only informed when the output actually changes. When it is, it is told what has changed, and the current clock count for the processor. The clock count allows the plugin to work out timing information for the output.

 
   /**
    * This method is called when the state of a port changes.
    * @param portBits - The current state of the port
    * @param clockCount - the current cpu time (in clock cycles)
    * @param portId - the identifier for the port that generated the event
    */
   public void portEvent( int portBits, long clockCount, int portId ) {

   }

The portEvent method is called when the port output changes. The port output is passed in the portBits parameter. The clockCount paramenter indicates the current cpu time in clock cycles - allowing for timing sensitive plugins to work out how they should respond to the output. As an example, the TV Plugin uses this information to derive the video signal timing data. The portId parameter is used to identify which port called the method. If a Plugin can only listen to one port, this can be ignored. Otherwise, this can be compared with the port Ids of the ports that the Plugin listens to. These can be discovered when the Plugin adds itself as a port listener to ports, using the MachineState class's 'addPortListener' methods.

   /**
    * This method is used to identify which bits this listener
    * is interested in for a given port.
    * @param portId - the port identifier
    * @return an integer with the appropriate bits set.
    */
   public int getBitmask( int portId ) {

   }

When a Plugin adds itself as a port listener, the port calls this method to discover which bits on that specific port the Plugin is interested in. The method should return a bit mask with bits set to one if the equivalent Port bit is read by the listener.

To listen for output, the listener must add itself to the relevant port. This is done through the MachineState class's addPortListener methods, which should be called when the Plugin is made visible. The methods are:

 
  /**
   * Add a port listener.. this adds the listener to the given port.
   * @param portListener The port listener to add.
   * @param aPortName    The name of the port we wish to listen to
   * @return A Vector of the ports that have been added..
   */
   public Vector addPortListener( PortListener portListener, String aPortName )

  /**
   * Add a port listener.. this adds the listener to a Vector of port names.
   * @param portListener The port listener to add.
   * @param whichPorts A Vector of the port names to add this listener to
   * @return A Vector of the ports that have been added..
   */
   public Vector addPortListener( PortListener portListener, Vector whichPorts )

These methods provide the means to add the PortListener to a single port, or to a list of ports. Both methods return a Vector that contain the Port objects that are now being listened to. The Port class provides various access methods. As ports are often referred to through their identifiers, one useful method is 'getIdentifier()' which returns the integer identifier for that port.

When the Plugin is closed, or no longer wishes to respond to port events, it must remove itself from the MachineState, using the following method:

 
  /** 
   * Remove port listener - this asks all ports to remove the given port
   * listener. Note that it does not check to see if this port listener
   * has registered with each port.
   * @param portListener The port listener to remove
   */
   public void removePortListener( PortListener portListener ) 

Index Next