Skip to main content Link Search Menu Expand Document (external link)

Plugin root class

A CPU plugin root class must implement either a CPU interface, or extend AbstractCPU class.

AbstractCPU implements several mechanisms which save developer time, like:

  • emulation control methods (run, step, stop, pause, reset) are queued for execution in the same thread, and implements run state notification. It means that it is possible to call them from any other thread (e.g. also from Swing Event dispatch thread)
  • implements notification of run states (see next chapter)
  • implements breakpoints management

Sample implementation follows (only core methods are implemented):

@PluginRoot(
        type = PLUGIN_TYPE.CPU,
        title = "Sample CPU emulator"
)
@SuppressWarnings("unused")
public class MyCpuImpl extends AbstractCPU {
    private final ContextImpl context = new ContextImpl();

    private EmulatorEngine engine;
    private Disassembler disassembler;
    private StatusPanel statusPanel;

    public CpuImpl(long pluginID, ApplicationApi applicationApi, PluginSettings settings) {
        super(pluginID, applicationApi, settings);

        try {
            applicationApi.getContextPool().register(pluginID, context, MyCpuContext.class);
        } catch (InvalidContextException | ContextAlreadyRegisteredException e) {
            LOGGER.error("Could not register CPU context", e);
            applicationApi.getDialogs().showError(
                    "Could not register CPU Context. Please see log file for details.", super.getTitle()
            );
        }
    }

    @Override
    public void initialize() throws PluginInitializationException {
        MemoryContext<Byte> memory = contextPool.getMemoryContext(pluginId, MemoryContext.class);
        if (memory.getDataType() != Byte.class) {
            throw new InvalidContextException(
                    "Unexpected memory cell type. Expected Byte but was: " + memory.getDataType()
            );
        }

        // create disassembler and debug columns
        this.disassembler = new DisassemblerImpl(memory, new DecoderImpl(memory));
        this.engine = new EmulatorEngine(memory, context);
    }

    @Override
    protected void destroyInternal() {
        context.clearDevices();
    }

    @Override
    public void resetInternal(int location) {
        engine.reset(location);
    }

    @Override
    protected RunState stepInternal() throws Exception {
        return engine.step();
    }

    @Override
    public JPanel getStatusPanel() {
        if (statusPanel == null) {
            statusPanel = new StatusPanel(engine, context);
        }
        return statusPanel;
    }

    @Override
    public RunState call() {
        return engine.run(this);
    }

    @Override
    public Disassembler getDisassembler() {
        return disassembler;
    }

    @Override
    public int getInstructionLocation() {
        return engine.PC;
    }

    @Override
    public boolean setInstructionLocation(int position) {
        if (position < 0) {
            return false;
        }
        engine.PC = position & 0xFFFF;
        return true;
    }
}

What can be noticed here is:

  • this CPU register custom context in constructor. The context is custom, because it will allow attaching devices to specific CPU “ports”.
  • obtaining MemoryContext is vital. If it is not successful, the PluginInitializationException will be propagated to the caller.
  • actual CPU emulation is implemented in EmulatorEngine class, which requires a memory and CPU context.
  • classes DisassemblerImpl and DecoderImpl are generated by Edigen

CPU Context

A CPU context is a way how to bring CPU-specific features to other plugins. Remember, plugins communicate only using contexts. A CPU context must extend from net.emustudio.emulib.plugins.cpu.CPUContext and must be annotated with @PluginContext annotation. In order a CPU context could be used by another plugins, it must be registered in a context pool obtained in plugin root class constructor (the registration must be performed in the same constructor).

Therefore, a CPU context is often a pair of interface and implementation, for example context for 8080 CPU looks as follows:

/**
 * Extended CPU context for 8080 processor.
 */
@PluginContext
public interface Context8080 extends CPUContext {

    /**
     * Attach a device into the CPU.
     *
     * @param device the device
     * @param port   CPU port where the device should be attached
     * @return true on success, false otherwise
     */
    boolean attachDevice(DeviceContext<Byte> device, int port);

    /**
     * Detach a device from the CPU.
     *
     * @param port the CPU port number which will be freed.
     */
    void detachDevice(int port);

    /**
     * Set CPU frequency in kHZ
     *
     * @param freq new frequency in kHZ
     */
    void setCPUFrequency(int freq);
}