<launchConfigurationType
delegate="net.mograsim.plugin.launch.MachineLaunchConfigType"
id="net.mograsim.plugin.core.launchmachine.type"
- modes="run, debug"
+ modes="debug"
name="Mograsim machine">
</launchConfigurationType>
</extension>
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStepFilters;
+import org.eclipse.debug.core.model.IThread;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineDebugTarget extends PlatformObject implements IDebugTarget
+{
+ private final MachineProcess process;
+
+ public MachineDebugTarget(MachineProcess process)
+ {
+ this.process = process;
+
+ DebugPlugin.getDefault().addDebugEventListener(es -> Arrays.stream(es).filter(e -> e.getSource() == process).forEach(e ->
+ {
+ switch (e.getKind())
+ {
+ case DebugEvent.RESUME:
+ fireResumeEvent(e.getDetail());
+ break;
+ case DebugEvent.SUSPEND:
+ fireSuspendEvent(e.getDetail());
+ break;
+ case DebugEvent.TERMINATE:
+ fireTerminateEvent();
+ break;
+ default:
+ // ignore
+ }
+ }));
+
+ getLaunch().addDebugTarget(this);
+ fireCreationEvent();
+ }
+
+ public Machine getMachine()
+ {
+ return process.getMachine();
+ }
+
+ @Override
+ public String getName() throws DebugException
+ {
+ return process.getName();
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return this;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return process.getLaunch();
+ }
+
+ public void setExecutionSpeed(double speed)
+ {
+ process.setExecutionSpeed(speed);
+ }
+
+ @Override
+ public boolean isSuspended()
+ {
+ return process.isSuspended();
+ }
+
+ @Override
+ public boolean canSuspend()
+ {
+ return process.canSuspend();
+ }
+
+ @Override
+ public void suspend() throws DebugException
+ {
+ process.suspend();
+ }
+
+ @Override
+ public boolean canResume()
+ {
+ return process.canResume();
+ }
+
+ @Override
+ public void resume() throws DebugException
+ {
+ process.resume();
+ }
+
+ @Override
+ public boolean isTerminated()
+ {
+ return process.isTerminated();
+ }
+
+ @Override
+ public boolean canTerminate()
+ {
+ return process.canTerminate();
+ }
+
+ @Override
+ public void terminate() throws DebugException
+ {
+ process.terminate();
+ }
+
+ @Override
+ public boolean supportsBreakpoint(IBreakpoint breakpoint)
+ {
+ return false;
+ }
+
+ @Override
+ public void breakpointAdded(IBreakpoint breakpoint)
+ {
+ // ignore; we don't support breakpoints
+ }
+
+ @Override
+ public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta)
+ {
+ // ignore; we don't support breakpoints
+ }
+
+ @Override
+ public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta)
+ {
+ // ignore; we don't support breakpoints
+ }
+
+ @Override
+ public boolean isDisconnected()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean canDisconnect()
+ {
+ return false;
+ }
+
+ @Override
+ public void disconnect() throws DebugException
+ {
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.NOT_SUPPORTED,
+ "Can't disconnect from a MachineDebugTarget", null));
+ }
+
+ @Override
+ public boolean supportsStorageRetrieval()
+ {
+ return true;
+ }
+
+ @Override
+ public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
+ {
+ return new MainMemoryBlock(this, startAddress, length);
+ }
+
+ @Override
+ public MachineProcess getProcess()
+ {
+ return process;
+ }
+
+ @Override
+ public boolean hasThreads() throws DebugException
+ {
+ return false;
+ }
+
+ @Override
+ public IThread[] getThreads() throws DebugException
+ {
+ return new IThread[0];
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getAdapter(Class<T> adapter)
+ {
+ if (adapter == IDebugElement.class)
+ return (T) this;
+
+ // leave this here; maybe we implement IStepFilters someday
+ if (adapter == IStepFilters.class)
+ if (this instanceof IStepFilters)
+ return (T) getDebugTarget();
+
+ if (adapter == IDebugTarget.class)
+ return (T) getDebugTarget();
+
+ if (adapter == ILaunch.class)
+ return (T) getLaunch();
+
+ if (adapter == IProcess.class)
+ return (T) getProcess();
+
+ // CONTEXTLAUNCHING
+ if (adapter == ILaunchConfiguration.class)
+ return (T) getLaunch().getLaunchConfiguration();
+
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Fires a creation event for this debug element.
+ */
+ private void fireCreationEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+ }
+
+ /**
+ * Fires a resume for this debug element with the specified detail code.
+ *
+ * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
+ */
+ private void fireResumeEvent(int detail)
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+ }
+
+ /**
+ * Fires a suspend event for this debug element with the specified detail code.
+ *
+ * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
+ */
+ private void fireSuspendEvent(int detail)
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+ }
+
+ /**
+ * Fires a terminate event for this debug element.
+ */
+ private void fireTerminateEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ /**
+ * Fires a debug event.
+ *
+ * @param event debug event to fire
+ */
+ private static void fireEvent(DebugEvent event)
+ {
+ DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
+ }
+}
\ No newline at end of file
package net.mograsim.plugin.launch;
-import java.io.IOException;
-
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import net.mograsim.machine.MachineDefinition;
import net.mograsim.plugin.MograsimActivator;
+import net.mograsim.plugin.nature.MachineContext;
+import net.mograsim.plugin.nature.ProjectMachineContext;
public class MachineLaunchConfigType extends LaunchConfigurationDelegate
{
public MachineLaunchConfigType()
{
this.resChangedListener = this::resourceChanged;
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener,
+ // IResourceChangeEvent.POST_BUILD |
+ IResourceChangeEvent.POST_CHANGE |
+ // IResourceChangeEvent.PRE_BUILD |
+ // IResourceChangeEvent.PRE_CLOSE |
+ // IResourceChangeEvent.PRE_DELETE |
+ // IResourceChangeEvent.PRE_REFRESH |
+ 0);
}
@Override
public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException
{
- ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener,
- IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_BUILD
- | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_REFRESH);
- System.out.println("launch");
- // TODO start a machine
- ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "\"echo Press Enter... &&pause>NUL && echo finished\"");
- try
- {
- launch.addProcess(DebugPlugin.newProcess(launch, pb.start(), ""));
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
+ String projName = configuration.getAttribute(PROJECT_ATTR, "");
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);
+ MachineContext machineContext = ProjectMachineContext.getMachineContextOf(project);
+ MachineDefinition machineDefinition = machineContext.getMachineDefinition().orElseThrow();
+
+ MachineDebugTarget debugTarget = new MachineDebugTarget(new MachineProcess(launch, machineDefinition));
+ debugTarget.setExecutionSpeed(10d);
}
private void resourceChanged(IResourceChangeEvent event)
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamsProxy;
+import org.eclipse.debug.core.model.ISuspendResume;
+
+import net.mograsim.logic.model.LogicExecuter;
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.MachineDefinition;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineProcess extends PlatformObject implements IProcess, ISuspendResume
+{
+ private final ILaunch launch;
+ private final Machine machine;
+ private final LogicExecuter exec;
+
+ private boolean running;
+
+ public MachineProcess(ILaunch launch, MachineDefinition machineDefinition)
+ {
+ this.launch = launch;
+ this.machine = machineDefinition.createNew();
+ this.exec = new LogicExecuter(machine.getTimeline());
+
+ exec.startLiveExecution();
+ running = true;
+
+ launch.addProcess(this);
+ fireCreationEvent();
+ }
+
+ public Machine getMachine()
+ {
+ return machine;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return launch;
+ }
+
+ public String getName()
+ {
+ return "Mograsim machine \"" + machine.getDefinition().getId() + '"';
+ }
+
+ @Override
+ public String getLabel()
+ {
+ return getName() + " (" + getStatusString() + ')';
+ }
+
+ public String getStatusString()
+ {
+ return isTerminated() ? "terminated" : isSuspended() ? "paused" : "running";
+ }
+
+ public void setExecutionSpeed(double speed)
+ {
+ exec.setSpeedFactor(speed);
+ }
+
+ @Override
+ public boolean isSuspended()
+ {
+ return exec.isPaused();
+ }
+
+ @Override
+ public boolean canSuspend()
+ {
+ return !isTerminated() && !isSuspended();
+ }
+
+ @Override
+ public void suspend() throws DebugException
+ {
+ if (isTerminated())
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+ "Can't suspend a terminated MachineProcess", null));
+ if (isSuspended())
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+ "Can't suspend a suspended MachineProcess", null));
+
+ exec.pauseLiveExecution();
+ fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+ }
+
+ @Override
+ public boolean canResume()
+ {
+ return !isTerminated() && isSuspended();
+ }
+
+ @Override
+ public void resume() throws DebugException
+ {
+ if (isTerminated())
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+ "Can't resume a terminated MachineProcess", null));
+ if (!isSuspended())
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+ "Can't resume a non-suspended MachineProcess", null));
+
+ exec.unpauseLiveExecution();
+ fireResumeEvent(DebugEvent.CLIENT_REQUEST);
+ }
+
+ @Override
+ public boolean isTerminated()
+ {
+ return !running;
+ }
+
+ @Override
+ public boolean canTerminate()
+ {
+ return !isTerminated();
+ }
+
+ @Override
+ public void terminate() throws DebugException
+ {
+ if (isTerminated())
+ return;
+
+ exec.stopLiveExecution();
+ running = false;
+ fireTerminateEvent();
+ }
+
+ @Override
+ public int getExitValue() throws DebugException
+ {
+ if (!isTerminated())
+ throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+ "Can't get the exit value of a running process", null));
+
+ return 0;
+ }
+
+ @Override
+ public IStreamsProxy getStreamsProxy()
+ {
+ return null;
+ }
+
+ private Map<String, String> attributes;
+
+ @Override
+ public void setAttribute(String key, String value)
+ {
+ if (attributes == null)
+ attributes = new HashMap<>(5);
+ Object origVal = attributes.get(key);
+ if (origVal != null && origVal.equals(value))
+ return;
+ attributes.put(key, value);
+ fireChangeEvent();
+ }
+
+ @Override
+ public String getAttribute(String key)
+ {
+ if (attributes == null)
+ return null;
+ return attributes.get(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> T getAdapter(Class<T> adapter)
+ {
+ if (adapter.equals(IProcess.class))
+ return (T) this;
+
+ if (adapter.equals(IDebugTarget.class))
+ {
+ ILaunch launch = getLaunch();
+ IDebugTarget[] targets = launch.getDebugTargets();
+ for (int i = 0; i < targets.length; i++)
+ if (this.equals(targets[i].getProcess()))
+ return (T) targets[i];
+ return null;
+ }
+
+ if (adapter.equals(ILaunch.class))
+ return (T) getLaunch();
+
+ if (adapter.equals(ILaunchConfiguration.class))
+ return (T) getLaunch().getLaunchConfiguration();
+
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Fires a creation event.
+ */
+ private void fireCreationEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+ }
+
+ /**
+ * Fires a change event.
+ */
+ private void fireChangeEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+ }
+
+ /**
+ * Fires a resume for this debug element with the specified detail code.
+ *
+ * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
+ */
+ private void fireResumeEvent(int detail)
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+ }
+
+ /**
+ * Fires a suspend event for this debug element with the specified detail code.
+ *
+ * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
+ */
+ private void fireSuspendEvent(int detail)
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+ }
+
+ /**
+ * Fires a terminate event.
+ */
+ private void fireTerminateEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ /**
+ * Fires the given debug event.
+ *
+ * @param event debug event to fire
+ */
+ private static void fireEvent(DebugEvent event)
+ {
+ DebugPlugin manager = DebugPlugin.getDefault();
+ if (manager != null)
+ manager.fireDebugEventSet(new DebugEvent[] { event });
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+
+import net.mograsim.machine.MainMemory;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MainMemoryBlock implements IMemoryBlock
+{
+ private final MachineDebugTarget debugTarget;
+ private final MainMemory mem;
+ private final long startAddress;
+ private final int length;
+
+ public MainMemoryBlock(MachineDebugTarget debugTarget, long startAddress, long length)
+ {
+ if (length < 0)
+ throw new IllegalArgumentException("Can't create a memory block of negative size");
+ if (length > Integer.MAX_VALUE)
+ throw new IllegalArgumentException("Can't create a memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\"");
+ if (startAddress % 2 != 0)
+ throw new IllegalArgumentException("Can't create an unaligned memory block");
+
+ this.debugTarget = debugTarget;
+ this.mem = debugTarget.getMachine().getMainMemory();
+ this.startAddress = startAddress;
+ this.length = (int) length;
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return debugTarget.getLaunch();
+ }
+
+ @Override
+ public <T> T getAdapter(Class<T> adapter)
+ {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public long getStartAddress()
+ {
+ return startAddress;
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public byte[] getBytes() throws DebugException
+ {
+ // TODO speedup. Maybe make a method for this in MainMemory?
+ byte[] bs = new byte[length];
+ int i;
+ long j;
+ for (i = 0, j = startAddress / 2; i < length; i++)
+ {
+ short word = mem.getCellAsBigInteger(j).shortValueExact();
+ bs[i + 0] = (byte) (word & 0xFF);
+ bs[i + 1] = (byte) (word >>> 8);
+ }
+ return bs;
+ }
+
+ @Override
+ public boolean supportsValueModification()
+ {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public void setValue(long offset, byte[] bytes) throws DebugException
+ {
+ // TODO
+ }
+}
\ No newline at end of file