Link Search Menu Expand Document

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 CpuImpl 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, ExtendedContext.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<Short> memory = contextPool.getMemoryContext(pluginId, MemoryContext.class);
        if (memory.getDataType() != Short.class) {
            throw new InvalidContextException(
                "Unexpected memory cell type. Expected Short 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