Chapter 6 - Technical Details Continued
ProcessorListener Interface
The Processor listener interface allows a class to respond to the starting and
stopping of simulation. There is only one method to implement:
/**
* Called when the simulation starts or stops.
* @param state The new state of the processor, see the Processor STATE_ variables
* @param message A text message describing the event that has occured
* @see Processor#getState
*/
public void processorStateChange( int state, String message );
|
The parameter 'state' is defined in the Processor class and may be one of:
| State | Meaning |
| STATE_CLEAR | - The processor has no program loaded |
| STATE_LOADED | - A program and associated data is loaded |
| STATE_RUNNING | - The processor is running the loaded program |
| STATE_STOPPED | - The running program has been stopped |
| STATE_HALTED | - The running program terminated unexpectedly |
Normally, a Plugin would respond to the STATE_STOPPED event to update any
display at the end of a 'Step' or 'Over' button click, or when 'Stop' is clicked
to stop a running simulation. The STATE_HALTED event may occur if a Plugin causes
an exception, or a breakpoint is reached.
The message parameter to the processorStateChange method is a text description
of the event that has ocurred, and is the same as the messages displayed in the
log file when 'Step', 'Over' etc. are clicked. Note however, that this text cannot
be relied upon, as internationalisation may change the exact values of message.
ClockListener Interface
In fact, a Plugin should not implement the ClockListener interface, but its
sub class ChainedClockListener. A ClockListener that has been added to the
MachineState will be notified after each instruction is simulated, so long as
the miSim DE preferences
are set to 'Accurate' simulation. Typically, the clock
listener will therefore be called after one or two clock cycles (depending on
the instruction being simulated). However, if 'Fast Loop Simulation' is enabled in the
miSim DE preferences
dialog, the clock listener will be called at the end of
empty delay loops, which may take a few hundred clock cycles.
Clock Listeners can therefore perform tasks that are exactly synchronised with
the processor clock - for instance, a listener could use the information to
generate a serial input to the microcontroller. If a Plugin only needs to
respond to output from the microcontroller, and can be coded to only change
its state when the microcontroller changes its output, then a much more efficient
PortListener should be used in the place of a Clock Listener. If however, the
Clock Listener preforms functions completely independantly of the output from
the microcontroller, a ClockListener should be used with the associated speed
penalties.
ChainedClockListener Methods
The following methods must be written for Plugins that implement ChainedClockListener.
/**
* Prepare for simulation, beginning at the given clock count
* @param clocks The clock count that simulation will start at
*/
public void prepare( long clocks ) {
}
|
The Prepare method is called when simulation is about to start. Note that
the clock count may change between simulation runs, so the prepare method
may need to restart any internal timers with the new value.
/**
* This method is called after each instruction is executed. Note that
* the number of clocks since the last call may be more than 1. This
* will occur for some branch instructions, and when loops have been
* optimised - resulting in a sequence of loop instructions being
* simulated in a single atomic event.
* @param clocks The current clock count for the processor
*/
public void clockEvent( long clocks ) {
nextListener.clockEvent( clocks );
... Plugin code...
}
|
The clock event method is called after each instruction is simulated. Note
that the chained clock listener interface requires that at the start of
this method, the next listener in the chain is called. As the ChainedClockListener
is defined as an interface, the implementation is up to the author of the
plugin, but code much like that shown here should be used. This assumes
that a variable 'nextListener' of type 'ClockListener'
is declared in the Plugin class, and set by the following method:
/**
* Set the next event handler in the chain. The implementation of
* this class MUST call nextListener.clockEvent at the start of the
* clockEvent method - ie. it must honour the chain.
* @param nextListener The next clock event listener in the chain
*/
public void setNext( ClockListener nextListener ) {
}
|
When a ChainedClock listener is added to the MachineState so that it can
respond to clock events, the MachineState will immediately call setNext
to inform the listener of the next ClockListener in the chain. This should
be stored by this method so that the clockEvent method above can call
the next listener in the chain, and the getNext method below can return
the next listener.
/**
* Return the next event handler in the chain.
* @return The next event handler in the chain
*/
public ClockListener getNext() {
}
|
This method is used by the MachineState to handle chains of clockListeners,
and should return the next listener in the chain - set by the setNext method
above.
Before a clock listener can be notified of clock events, it must be added
to the simulator's MachineState object. When a Plugin is closed, or no longer
needs to respond to clock events, it should remove the clockListener from
the MachineState. The following two methods are defined in MachineState to
add and remove listeners:
/**
* Add a chained clock listener to this machine. If the new listener
* has already been added, this will do nothing.
* @param newListener The clock listener to add to the chain..
*/
public void addClockListener( ChainedClockListener newListener )
|
To add a clock listener, and:
/**
* Remove a chained clock listener from this machine..
* @param aListener The event handler to remove to the chain..
*/
public void removeClockListener( ChainedClockListener aListener )
|
To remove it.
Why Two Interfaces?
To ensure that processing of many ClockListeners is as efficient as possible,
two interfaces are provided. The basic Clock Listener interface does not need
to concern itself with any other clock listeners, and simply handles clock
events. miSim DE
will always have a Clock Listener defined, and call this after
each instruction in 'Accurate' simulation. Each additional Clock Listener
(for instance a Plugin, or the BreakpointManager which is implemented as
a ClockListener) must implement the ChainedClockListener interface. The
ChainedClockListener is guaranteed to be one of a number of ClockListeners,
and must call the next in the chain to process the clock event. This means
that the simulator can call the ClockListener at the head of the chain and
that can call the next without requiring any tests. The last Listener
in the chain implements only the ClockListener interface (as it must be the
last in the chain), and doesn't need to pass on the clock event.
PortMapper Class
The PortMapper class can be used when a Plugin has many connections to the
microcontroller, and needs to allow the user to join them in different
ways. A PortMapper is expensive (slow) to create, but then can translate signals
into and out of the microcontroller relatively quickly. If there are only
one or two connections to be managed, however, it is likely to be much quicker
to handle them directly. As example of a Plugin the is well suited to using
the PortMapper, the LCD Plugin has three control signals and either four or
eight bi-directional data signals that must be mapped onto the microcontroller's
pins. Infact, the PortMapper was written for the LCD Plugin, but with a generalised
interface allowing other Plugins to use it.
To develop using the PortMapper, it is necessary to understand how it behaves.
Again, the best example of this is the LCD Plugin and both that and how to
use the PortMapper from the user's perspective is explained on the first
Plugins Page - See 'Configuring Plugins'
and 'LCD Plugin'. The Config string is used
to create a new PortMapper, which then provides methods to examine which ports,
pins and signals are mapped between the Plugin and the microcontroller. The most
significant methods for the port mapper are 'prepare', 'read'
and 'writeToPort'. 'Prepare' and 'read' are used
together, when a Plugin wishes to read information from the microcontroller.
'Prepare' stores the microcontroller's outputs in the PortMapper so
that subsequent calls to 'read' can read out individual bits, or
collections of bits. 'WriteToPort' writes data from the Plugin to
the port's inputs.
As the PortMapper class is complex, and many of the methods are important, this
manual will not attempt to list and describe them all. The Class and methods are
all documented in the Javadocs for miSim DE,
and this is an ideal place to start when developing Plugins.