--- /dev/null
+package net.mograsim.machine.standard.memory;
+
+import net.mograsim.machine.MachineException;
+
+public class MainMemoryParseException extends MachineException
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6820101808901789906L;
+
+ public MainMemoryParseException()
+ {
+ super();
+ }
+
+ public MainMemoryParseException(String message)
+ {
+ super(message);
+ }
+
+ public MainMemoryParseException(Throwable cause)
+ {
+ super(cause);
+ }
+
+}
--- /dev/null
+package net.mograsim.machine.standard.memory;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import net.mograsim.logic.core.types.BitVector;
+import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+
+public class MainMemoryParser
+{
+ private final static String lineSeparator = System.getProperty("line.separator");
+
+ public static void parseMemory(final MainMemory memory, String inputPath) throws IOException
+ {
+ try (InputStream input = new FileInputStream(inputPath))
+ {
+ parseMemory(memory, input);
+ }
+ }
+
+ /**
+ * @param input The input to parse must be in csv format; The stream is closed after being consumed.
+ *
+ * @throws IOException
+ */
+ public static MainMemory parseMemory(MainMemoryDefinition memDef, InputStream input) throws IOException
+ {
+ try
+ {
+ MainMemory memory = new WordAddressableMemory(memDef);
+ parseMemory(memory, input);
+ return memory;
+ }
+ catch (NullPointerException e)
+ {
+ throw new MainMemoryParseException(e);
+ }
+ }
+
+ /**
+ *
+ * @param input The input to parse must be in csv format; The stream is closed after being consumed.
+ *
+ * @throws IOException
+ */
+ public static void parseMemory(final MainMemory memory, InputStream input) throws IOException
+ {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(input)))
+ {
+ MainMemoryDefinition def = memory.getDefinition();
+
+ long minAddr = def.getMinimalAddress();
+ long maxAddr = def.getMaximalAddress();
+
+ String line;
+ long i = minAddr;
+ try
+ {
+ for (; i <= maxAddr && reader.ready() && !"".equals((line = reader.readLine())); i++)
+ {
+ memory.setCell(i, BitVector.parse(line));
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static InputStream write(MainMemory memory)
+ {
+ return new InputStream()
+ {
+ long instIndex = memory.getDefinition().getMinimalAddress(), maxAddress = memory.getDefinition().getMaximalAddress();
+ InputStream instStream = new ByteArrayInputStream(new byte[0]);
+
+ @Override
+ public int read() throws IOException
+ {
+ int val = instStream.read();
+ if (val == -1 && instIndex <= maxAddress)
+ {
+ instStream = new ByteArrayInputStream((memory.getCell(instIndex++).toString() + lineSeparator).getBytes());
+ val = instStream.read();
+ }
+ return val;
+ }
+ };
+ }
+}
net.mograsim.plugin.asm.editor,
net.mograsim.plugin.asm.editor.rules,
net.mograsim.plugin.editors,
+ net.mograsim.plugin.launch,
net.mograsim.plugin.nature,
net.mograsim.plugin.nature.properties,
net.mograsim.plugin.tables,
net.mograsim.preferences;bundle-version="0.1.0",
net.mograsim.machine,
org.eclipse.core.expressions,
- SWTInput
+ SWTInput,
+ org.eclipse.debug.core,
+ org.eclipse.debug.ui
Bundle-RequiredExecutionEnvironment: JavaSE-11
Automatic-Module-Name: net.mograsim.plugin.core
Bundle-Vendor: %Bundle-Vendor.0
<selection class="org.eclipse.core.resources.IResource"/>
</wizard>
</extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ delegate="net.mograsim.plugin.launch.MachineLaunchConfigType"
+ id="net.mograsim.plugin.core.launchmachine.type"
+ modes="run, debug"
+ name="Mograsim machine">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="net.mograsim.plugin.launch.EmptyLaunchConfigTabGroup"
+ id="net.mograsim.plugin.core.launchmachine.tabgroup"
+ type="net.mograsim.plugin.core.launchmachine.type">
+ </launchConfigurationTabGroup>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabs">
+ <tab
+ class="net.mograsim.plugin.launch.MainMachineLaunchConfigTab"
+ group="net.mograsim.plugin.core.launchmachine.tabgroup"
+ id="net.mograsim.plugin.core.launchmachine.maintab"
+ name="Common">
+ </tab>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="net.mograsim.plugin.core.launchmachine.type"
+ icon="icons/mograsim/orange/icon_orange_16.png"
+ id="net.mograsim.plugin.core.launchmachine.image">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.memoryRenderings">
+ <renderingBindings
+ defaultIds="org.eclipse.debug.ui.rendering.raw_memory"
+ primaryId="org.eclipse.debug.ui.rendering.raw_memory"
+ renderingIds="org.eclipse.debug.ui.rendering.raw_memory">
+ </renderingBindings>
+ </extension>
</plugin>
RGB rgb = getColorRegistry().getRGB(name);
if (rgb == null)
{
- StatusManager.getManager().handle(new Status(IStatus.ERROR, "net.mograsim.plugin.core", "No color for name " + name));
+ StatusManager.getManager().handle(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "No color for name " + name));
return null;
}
return new ColorDefinition(rgb.red, rgb.green, rgb.blue);
public final class MograsimActivator extends AbstractUIPlugin
{
+ public static final String PLUGIN_ID = "net.mograsim.plugin.core";
+
private static MograsimActivator instance;
public MograsimActivator()
package net.mograsim.plugin.editors;
-import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.math.BigInteger;
import org.eclipse.core.resources.IFile;
import org.eclipse.ui.part.EditorPart;
import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+import net.mograsim.machine.Memory.MemoryCellModifiedListener;
import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
-import net.mograsim.machine.standard.memory.WordAddressableMemory;
+import net.mograsim.machine.standard.memory.MainMemoryParser;
import net.mograsim.plugin.asm.AsmNumberUtil;
import net.mograsim.plugin.nature.MachineContext;
import net.mograsim.plugin.nature.ProjectMachineContext;
private MemoryTableContentProvider provider;
private DisplaySettings displaySettings;
+ private boolean dirty;
+
+ private final MemoryCellModifiedListener memListener;
+
+ public MemoryEditor()
+ {
+ memListener = this::cellModified;
+ }
+
@Override
public void createPartControl(Composite parent)
{
context.activateMachine();
setPartName(fileInput.getName());
- open(fileInput.getFile());
+ try
+ {
+ open(fileInput.getFile());
+ }
+ catch (Exception e)
+ {
+ throw new PartInitException("Failed to read input!", e);
+ }
} else
throw new IllegalArgumentException("MemoryEditor can only be used with Files");
SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
}
- private void save(IFile file, IProgressMonitor monitor) throws CoreException
+ private void save(IFile file, IProgressMonitor monitor) throws CoreException, IOException
{
- file.setContents(new ByteArrayInputStream("actual contents will go here".getBytes()), 0, monitor);
+ if (memory == null)
+ {
+ throw new MicroInstructionMemoryParseException("Failed to write MainMemory to File. No MainMemory assigned.");
+ }
+ try (InputStream toWrite = MainMemoryParser.write(memory))
+ {
+ file.setContents(toWrite, 0, monitor);
+ setDirty(false);
+ }
}
- private void open(IFile file)
+ private void open(IFile file) throws IOException, CoreException
{
- // TODO actually parse the file
- memory = new WordAddressableMemory(context.getMachineDefinition()
- .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition());
+ MainMemoryDefinition memDef = context.getMachineDefinition()
+ .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition();
+ memory = MainMemoryParser.parseMemory(memDef, file.getContents());
+ memory.registerCellModifiedListener(memListener);
if (viewer != null)
viewer.setInput(memory);
}
+ private void cellModified(@SuppressWarnings("unused") long address)
+ {
+ setDirty(true);
+ }
+
+ private void setDirty(boolean newDirty)
+ {
+ dirty = newDirty;
+ firePropertyChange(PROP_DIRTY);
+ }
+
@Override
public void doSaveAs()
{
@Override
public boolean isDirty()
{
- // TODO
- return false;
+ return dirty;
}
@Override
@Override
public void dispose()
{
- // TODO Auto-generated method stub
+ if (memory != null)
+ memory.deregisterCellModifiedListener(memListener);
super.dispose();
}
}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+/**
+ * Useful for specifying launch config tabs via the extension point <code>org.eclipse.debug.ui.launchConfigurationTabs</code>.
+ *
+ * @author Daniel Kirschten
+ */
+public class EmptyLaunchConfigTabGroup extends AbstractLaunchConfigurationTabGroup
+{
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode)
+ {
+ setTabs(new ILaunchConfigurationTab[0]);
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+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.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStepFilters;
+import org.eclipse.debug.core.model.IThread;
+
+import net.mograsim.logic.model.LogicExecuter;
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.MachineDefinition;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension
+{
+ private final ILaunch launch;
+ private final Machine machine;
+ private final LogicExecuter exec;
+
+ private boolean running;
+
+ public MachineDebugTarget(ILaunch launch, MachineDefinition machineDefinition)
+ {
+ this.launch = launch;
+ this.machine = machineDefinition.createNew();
+ this.exec = new LogicExecuter(machine.getTimeline());
+
+ exec.startLiveExecution();
+ running = true;
+
+ getLaunch().addDebugTarget(this);
+ fireCreationEvent();
+ }
+
+ public Machine getMachine()
+ {
+ return machine;
+ }
+
+ @Override
+ public String getName() throws DebugException
+ {
+ return "Mograsim machine \"" + machine.getDefinition().getId() + '"';
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return this;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return launch;
+ }
+
+ 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())
+ throwDebugException("Can't suspend a terminated MachineProcess");
+ if (isSuspended())
+ throwDebugException("Can't suspend a suspended MachineProcess");
+
+ exec.pauseLiveExecution();
+ fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+ }
+
+ @Override
+ public boolean canResume()
+ {
+ return !isTerminated() && isSuspended();
+ }
+
+ @Override
+ public void resume() throws DebugException
+ {
+ if (isTerminated())
+ throwDebugException("Can't resume a terminated MachineProcess");
+ if (!isSuspended())
+ throwDebugException("Can't resume a non-suspended MachineProcess");
+
+ 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 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;
+ }
+
+ @SuppressWarnings("deprecation") // TODO can we throw a DebugException instead?
+ @Override
+ public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
+ {
+ return new MainMemoryBlock(this, startAddress, length);
+ }
+
+ @Override
+ public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException
+ {
+ return new MainMemoryBlockExtension(this, expression, context);
+ }
+
+ @Override
+ public IProcess getProcess()
+ {
+ return null;
+ }
+
+ @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();
+
+ // 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 });
+ }
+
+ private static void throwDebugException(String message) throws DebugException
+ {
+ throw new DebugException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Optional;
+
+import org.eclipse.core.resources.IFile;
+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.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.ui.statushandlers.StatusManager;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.MachineDefinition;
+import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+import net.mograsim.machine.mi.MicroInstructionMemory;
+import net.mograsim.machine.mi.MicroInstructionMemoryDefinition;
+import net.mograsim.machine.mi.MicroInstructionMemoryParser;
+import net.mograsim.machine.standard.memory.MainMemoryParser;
+import net.mograsim.plugin.MograsimActivator;
+import net.mograsim.plugin.nature.MachineContext;
+import net.mograsim.plugin.nature.MograsimNature;
+import net.mograsim.plugin.nature.ProjectMachineContext;
+
+public class MachineLaunchConfigType extends LaunchConfigurationDelegate
+{
+ public static final String PROJECT_ATTR = MograsimActivator.PLUGIN_ID + ".project";
+ public static final String MPM_FILE_ATTR = MograsimActivator.PLUGIN_ID + ".mpm";
+ public static final String INITIAL_RAM_FILE_ATTR = MograsimActivator.PLUGIN_ID + ".initialram";
+
+ private final IResourceChangeListener resChangedListener;
+
+ 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 boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException
+ {
+ String projName = configuration.getAttribute(PROJECT_ATTR, "");
+ if ("".equals(projName))
+ return showErrorAndReturnFalse("No project specified");
+
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);
+ if (!project.isAccessible())
+ return showErrorAndReturnFalse("Project not accessible");
+ if (!project.hasNature(MograsimNature.NATURE_ID))
+ return showErrorAndReturnFalse("Project is not a Mograsim project");
+
+ MachineContext machineContext = ProjectMachineContext.getMachineContextOf(project);
+ Optional<MachineDefinition> machDefOptional = machineContext.getMachineDefinition();
+ if (machDefOptional.isEmpty())
+ return showErrorAndReturnFalse("No machine definition set");
+
+ MachineDefinition machineDefinition = machDefOptional.orElseThrow();
+ MicroInstructionMemoryDefinition miMemDef = machineDefinition.getMicroInstructionMemoryDefinition();
+ MainMemoryDefinition mainMemDef = machineDefinition.getMainMemoryDefinition();
+
+ String mpmFileName = configuration.getAttribute(MPM_FILE_ATTR, "");
+ if ("".equals(mpmFileName))
+ return showErrorAndReturnFalse("No MPM file specified");
+
+ IFile mpmFile = project.getFile(mpmFileName);
+ if (mpmFile == null || !mpmFile.isAccessible())
+ return showErrorAndReturnFalse("MPM file not accessible");
+
+ try (InputStream mpmStream = mpmFile.getContents())
+ {
+ MicroInstructionMemoryParser.parseMemory(miMemDef, mpmStream);
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Unexpected IO exception reading MPM file", e));
+ }
+
+ String initialRAMFileName = configuration.getAttribute(INITIAL_RAM_FILE_ATTR, "");
+ if (!"".equals(initialRAMFileName))
+ {
+ IFile initialRAMFile = project.getFile(initialRAMFileName);
+ if (initialRAMFile == null || !initialRAMFile.isAccessible())
+ return showErrorAndReturnFalse("Initial RAM file not accessible");
+
+ try (InputStream initialRAMStream = initialRAMFile.getContents())
+ {
+ MainMemoryParser.parseMemory(mainMemDef, initialRAMStream);
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Unexpected IO exception reading initial RAM file", e));
+ }
+ }
+
+ return super.preLaunchCheck(configuration, mode, monitor);
+ }
+
+ private static boolean showErrorAndReturnFalse(String message)
+ {
+ StatusManager.getManager().handle(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, message, null), StatusManager.SHOW);
+ return false;
+ }
+
+ @Override
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException
+ {
+ String projName = configuration.getAttribute(PROJECT_ATTR, "");
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);
+
+ MachineContext machineContext = ProjectMachineContext.getMachineContextOf(project);
+ MachineDefinition machineDefinition = machineContext.getMachineDefinition().orElseThrow();
+ MicroInstructionMemoryDefinition miMemDef = machineDefinition.getMicroInstructionMemoryDefinition();
+ MainMemoryDefinition mainMemDef = machineDefinition.getMainMemoryDefinition();
+
+ IFile mpmFile = project.getFile(configuration.getAttribute(MPM_FILE_ATTR, ""));
+
+ MicroInstructionMemory mpm;
+ try (InputStream mpmStream = mpmFile.getContents())
+ {
+ mpm = MicroInstructionMemoryParser.parseMemory(miMemDef, mpmStream);
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Unexpected IO exception reading MPM file", e));
+ }
+
+ String initialRAMFileName = configuration.getAttribute(INITIAL_RAM_FILE_ATTR, "");
+ MainMemory mem;
+ if (!"".equals(initialRAMFileName))
+ {
+ IFile initialRAMFile = project.getFile(initialRAMFileName);
+ try (InputStream initialRAMStream = initialRAMFile.getContents())
+ {
+ mem = MainMemoryParser.parseMemory(mainMemDef, initialRAMStream);
+ }
+ catch (IOException e)
+ {
+ throw new CoreException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, "Unexpected IO exception reading initial RAM file", e));
+ }
+ } else
+ mem = null;
+
+ MachineDebugTarget debugTarget = new MachineDebugTarget(launch, machineDefinition);
+ debugTarget.suspend();
+ debugTarget.setExecutionSpeed(1);
+ Machine machine = debugTarget.getMachine();
+ machine.getMicroInstructionMemory().bind(mpm);
+ if (mem != null)
+ machine.getMainMemory().bind(mem);
+ machine.reset();
+ }
+
+ private void resourceChanged(IResourceChangeEvent event)
+ {
+ // TODO react to MPM changes
+ int type = event.getType();
+ String typeStr;
+ switch (type)
+ {
+ case IResourceChangeEvent.POST_BUILD:
+ typeStr = "POST_BUILD";
+ break;
+ case IResourceChangeEvent.POST_CHANGE:
+ typeStr = "POST_CHANGE";
+ break;
+ case IResourceChangeEvent.PRE_BUILD:
+ typeStr = "PRE_BUILD";
+ break;
+ case IResourceChangeEvent.PRE_CLOSE:
+ typeStr = "PRE_CLOSE";
+ break;
+ case IResourceChangeEvent.PRE_DELETE:
+ typeStr = "PRE_DELETE";
+ break;
+ case IResourceChangeEvent.PRE_REFRESH:
+ typeStr = "PRE_REFRESH";
+ break;
+ default:
+ typeStr = "<unknown: " + type + ">";
+ }
+ System.out.println(typeStr + ": " + event);
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.PixelConverter;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.window.Window;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+import net.mograsim.plugin.nature.MograsimNature;
+import net.mograsim.plugin.util.FileExtensionViewerFilter;
+import net.mograsim.plugin.util.ImageDescriptorWithMargins;
+import net.mograsim.plugin.util.ProjectViewerFilter;
+
+//a big part of this class is stolen from org.eclipse.jdt.debug.ui
+public class MainMachineLaunchConfigTab extends AbstractLaunchConfigurationTab
+{
+ private Text projSelText;
+ private Text mpmFileSelText;
+ private Text initialRAMFileSelText;
+
+ @Override
+ public void createControl(Composite parent)
+ {
+ parent.setLayout(new FillLayout());
+ Composite innerParent = new Composite(parent, SWT.NONE);
+ setControl(innerParent);
+
+ innerParent.setLayout(new GridLayout(3, false));
+
+ this.projSelText = addResourceSelector(innerParent, "&Project:", this::chooseMograsimProject);
+
+ this.mpmFileSelText = addResourceSelector(innerParent, "&MPM:", this::chooseMPMFile);
+
+ this.initialRAMFileSelText = addResourceSelector(innerParent, "Initial &RAM (optional):", this::chooseInitialRAMFile);
+ }
+
+ private Text addResourceSelector(Composite innerParent, String label, Supplier<String> chooser)
+ {
+ Label swtLabel = new Label(innerParent, SWT.NONE);
+ swtLabel.setText(label);
+ swtLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+
+ Text text = new Text(innerParent, SWT.BORDER);
+ text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ text.addModifyListener(e -> updateLaunchConfigurationDialog());
+
+ swtLabel.addListener(SWT.FocusIn, e -> text.setFocus());
+
+ Button browseButton = new Button(innerParent, SWT.PUSH);
+ GridData projSelButtonData = new GridData();
+ projSelButtonData.widthHint = calculateWidthHint(browseButton);
+ projSelButtonData.horizontalAlignment = SWT.FILL;
+ browseButton.setLayoutData(projSelButtonData);
+ browseButton.setText("&Browse...");
+ browseButton.addListener(SWT.Selection, e ->
+ {
+ String chosen = chooser.get();
+ if (chosen != null)
+ text.setText(chosen);
+ });
+ return text;
+ }
+
+ private static int calculateWidthHint(Control c)
+ {
+ int wHint = new PixelConverter(c).convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+ return Math.max(wHint, c.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
+ }
+
+ private String chooseMograsimProject()
+ {
+ WorkbenchLabelProvider renderer = new WorkbenchLabelProvider()
+ {
+ @Override
+ protected ImageDescriptor decorateImage(ImageDescriptor input, Object element)
+ {
+ return new ImageDescriptorWithMargins(input, new Point(20, 16));
+ }
+ };
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), renderer);
+ dialog.setTitle("Project Selection");
+ dialog.setMessage("Select a Mograsim project");
+ dialog.setElements(filterOpenMograsimProjects(ResourcesPlugin.getWorkspace().getRoot().getProjects()));
+ if (dialog.open() == Window.OK)
+ return ((IProject) dialog.getFirstResult()).getName();
+ return null;
+ }
+
+ private String chooseMPMFile()
+ {
+ ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+ dialog.setTitle("MPM Selection");
+ dialog.setMessage("Select a MPM file");
+ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ dialog.addFilter(new FileExtensionViewerFilter("mpm"));
+ dialog.addFilter(new ProjectViewerFilter(getSelectedProject()));
+
+ if (dialog.open() == Window.OK)
+ return ((IResource) dialog.getResult()[0]).getProjectRelativePath().toPortableString();
+ return null;
+ }
+
+ private String chooseInitialRAMFile()
+ {
+ ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), new WorkbenchLabelProvider(),
+ new WorkbenchContentProvider());
+ dialog.setTitle("Initial RAM Selection");
+ dialog.setMessage("Select a RAM file");
+ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+ dialog.addFilter(new FileExtensionViewerFilter("mem"));
+ dialog.addFilter(new ProjectViewerFilter(getSelectedProject()));
+
+ if (dialog.open() == Window.OK)
+ return ((IResource) dialog.getResult()[0]).getProjectRelativePath().toPortableString();
+ return null;
+ }
+
+ private IProject getSelectedProject()
+ {
+ String projName = projSelText.getText().trim();
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ if (workspace.validateName(projName, IResource.PROJECT).isOK())
+ return workspace.getRoot().getProject(projName);
+ return null;
+ }
+
+ private static IProject[] filterOpenMograsimProjects(IProject[] projects)
+ {
+ return Arrays.stream(projects).filter(p ->
+ {
+ try
+ {
+ return p.isAccessible() && p.hasNature(MograsimNature.NATURE_ID);
+ }
+ catch (CoreException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }).toArray(IProject[]::new);
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration)
+ {
+ // TODO don't let the user have to specify everything
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration configuration)
+ {
+ projSelText.setText(getStringAttribSafe(configuration, MachineLaunchConfigType.PROJECT_ATTR, ""));
+ mpmFileSelText.setText(getStringAttribSafe(configuration, MachineLaunchConfigType.MPM_FILE_ATTR, ""));
+ initialRAMFileSelText.setText(getStringAttribSafe(configuration, MachineLaunchConfigType.INITIAL_RAM_FILE_ATTR, ""));
+ }
+
+ private String getStringAttribSafe(ILaunchConfiguration configuration, String attrib, String defaultValue)
+ {
+ try
+ {
+ return configuration.getAttribute(attrib, defaultValue);
+ }
+ catch (CoreException e)
+ {
+ setErrorMessage(e.getStatus().getMessage());
+ }
+ return defaultValue;
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy configuration)
+ {
+ String projName = projSelText.getText().trim();
+ String mpmFileName = mpmFileSelText.getText().trim();
+ String initialRAMFileName = initialRAMFileSelText.getText().trim();
+
+ Set<IResource> associatedResources = new HashSet<>();
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ if (workspace.validateName(projName, IResource.PROJECT).isOK())
+ {
+ IProject project = workspace.getRoot().getProject(projName);
+ try
+ {
+ if (project != null && project.isAccessible() && project.hasNature(MograsimNature.NATURE_ID))
+ {
+ associatedResources.add(project);
+
+ IResource mpmFile = project.findMember(mpmFileName);
+ if (mpmFile != null && mpmFile.exists() && mpmFile.getType() == IResource.FILE)
+ associatedResources.add(mpmFile);
+
+ IResource ramFile = project.findMember(initialRAMFileName);
+ if (ramFile != null && ramFile.exists() && ramFile.getType() == IResource.FILE)
+ associatedResources.add(ramFile);
+ }
+ }
+ catch (CoreException e)
+ {
+ setErrorMessage(e.getStatus().getMessage());
+ }
+ }
+ configuration.setMappedResources(associatedResources.toArray(IResource[]::new));
+ configuration.setAttribute(MachineLaunchConfigType.PROJECT_ATTR, projName);
+ configuration.setAttribute(MachineLaunchConfigType.MPM_FILE_ATTR, mpmFileName);
+ configuration.setAttribute(MachineLaunchConfigType.INITIAL_RAM_FILE_ATTR, initialRAMFileName);
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig)
+ {
+ setErrorMessage(null);
+ setMessage(null);
+ String projName = projSelText.getText().trim();
+ if (projName.length() == 0)
+ return setErrorAndReturnFalse("No project specified");
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ IStatus status = workspace.validateName(projName, IResource.PROJECT);
+ if (!status.isOK())
+ return setErrorAndReturnFalse("Illegal project name: {0}: {1}", projName, status.getMessage());
+
+ IProject project = workspace.getRoot().getProject(projName);
+ if (!project.exists())
+ return setErrorAndReturnFalse("Project {0} does not exist", projName);
+ if (!project.isOpen())
+ return setErrorAndReturnFalse("Project {0} is closed", projName);
+ try
+ {
+ if (!project.hasNature(MograsimNature.NATURE_ID))
+ return setErrorAndReturnFalse("Project {0} is not a Mograsim project", projName);
+ }
+ catch (CoreException e)
+ {
+ return setErrorAndReturnFalse(e.getStatus().getMessage());
+ }
+
+ String mpmFileName = mpmFileSelText.getText().trim();
+ if (mpmFileName.length() == 0)
+ return setErrorAndReturnFalse("No MPM file specified");
+ IResource mpmResource = project.findMember(mpmFileName);
+ if (mpmResource == null || !mpmResource.exists())
+ return setErrorAndReturnFalse("MPM file {0} does not exist", mpmFileName);
+ if (mpmResource.getType() != IResource.FILE)
+ return setErrorAndReturnFalse("MPM file {0} is not a file", mpmFileName);
+
+ String initialRAMFileName = initialRAMFileSelText.getText().trim();
+ if (initialRAMFileName.length() > 0)
+ {
+ IResource initialRAMResource = project.findMember(initialRAMFileName);
+ if (initialRAMResource == null || !initialRAMResource.exists())
+ return setErrorAndReturnFalse("Initial RAM file {0} does not exist", initialRAMFileName);
+ if (initialRAMResource.getType() != IResource.FILE)
+ return setErrorAndReturnFalse("Initial RAM file {0} is not a file", initialRAMFileName);
+ }
+
+ return true;
+ }
+
+ private boolean setErrorAndReturnFalse(String message, String... params)
+ {
+ setErrorMessage(NLS.bind(message, params));
+ return false;
+ }
+
+ @Override
+ public String getName()
+ {
+ return "testlaunchconfigtabname";
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.PlatformObject;
+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.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+
+import net.mograsim.machine.MainMemory;
+import net.mograsim.plugin.MograsimActivator;
+
+@Deprecated
+public class MainMemoryBlock extends PlatformObject 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)
+ {
+ MainMemory mem = debugTarget.getMachine().getMainMemory();
+
+ if (length < 0)
+ throw new IllegalArgumentException("Negative size");
+ if (startAddress < 0)
+ throw new IllegalArgumentException("Negative start address");
+ if ((startAddress + length) / 2 > mem.size())
+ throw new IllegalArgumentException("End address higher than memory size");
+ if (length > Integer.MAX_VALUE)
+ throw new IllegalArgumentException("Memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\"");
+ if (startAddress % 2 != 0 || length % 2 != 0)
+ throw new IllegalArgumentException("Unaligned memory block");
+
+ this.debugTarget = debugTarget;
+ this.mem = mem;
+ this.startAddress = startAddress;
+ this.length = (int) length;
+
+ mem.registerCellModifiedListener(e ->
+ {
+ if (e >= startAddress / 2 && e < (startAddress + length) / 2)
+ fireContentChangeEvent();
+ });
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return debugTarget.getLaunch();
+ }
+
+ @Override
+ public long getStartAddress()
+ {
+ return startAddress;
+ }
+
+ @Override
+ public long getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public byte[] getBytes() throws DebugException
+ {
+ byte[] bs = new byte[length];
+ int i;
+ long j;
+ for (i = 0, j = startAddress / 2; i < length; i += 2, j++)
+ {
+ short word = mem.getCellAsBigInteger(j).shortValue();
+ bs[i + 0] = (byte) (word & 0xFF);
+ bs[i + 1] = (byte) (word >>> 8);
+ }
+ return bs;
+ }
+
+ @Override
+ public boolean supportsValueModification()
+ {
+ return true;
+ }
+
+ @Override
+ public void setValue(long offset, byte[] bytes) throws DebugException
+ {
+ if (offset % 2 != 0 || bytes.length % 2 != 0)
+ throw new IllegalArgumentException("Can't write unaligned to a memory block");
+ int i;
+ long j;
+ for (i = 0, j = (startAddress + offset) / 2; i < bytes.length; i += 2, j++)
+ {
+ short word = 0;
+ word |= bytes[i + 0] & 0xFF;
+ word |= bytes[i + 1] << 8;
+ mem.setCellAsBigInteger(j, BigInteger.valueOf(word));
+ }
+ }
+
+ /**
+ * Fires a terminate event for this debug element.
+ */
+ private void fireContentChangeEvent()
+ {
+ 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
--- /dev/null
+package net.mograsim.plugin.launch;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+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.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlockExtension;
+import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
+import org.eclipse.debug.core.model.MemoryByte;
+
+import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+import net.mograsim.machine.Memory.MemoryCellModifiedListener;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MainMemoryBlockExtension extends PlatformObject implements IMemoryBlockExtension
+{
+ // TODO do we want to make the memory accessible byte-wise?
+
+ private final String expression;
+ private final MachineDebugTarget debugTarget;
+ private final MainMemory mem;
+
+ private final MainMemoryDefinition memDef;
+ private final int cellWidthBits;
+ private final int cellWidthBytes;
+ private final BigInteger cellWidthBytesBI;
+ private final BigInteger minAddrWords;
+ private final BigInteger maxAddrWords;
+
+ private BigInteger baseAddrWords;
+ private BigInteger lengthWords;
+
+ private final Set<Object> clients;
+ private final MemoryCellModifiedListener memListener;
+ private final AtomicBoolean memListenerRegistered;
+
+ public MainMemoryBlockExtension(MachineDebugTarget debugTarget, String expression, @SuppressWarnings("unused") Object expressionContext)
+ throws DebugException
+ {
+ this.expression = expression;
+ this.debugTarget = debugTarget;
+ this.mem = debugTarget.getMachine().getMainMemory();
+
+ this.memDef = mem.getDefinition();
+ this.cellWidthBits = memDef.getCellWidth();
+ this.cellWidthBytes = (cellWidthBits + 7) / 8;
+ this.cellWidthBytesBI = BigInteger.valueOf(cellWidthBytes);
+ this.minAddrWords = BigInteger.valueOf(memDef.getMinimalAddress());
+ this.maxAddrWords = BigInteger.valueOf(memDef.getMaximalAddress());
+
+ // TODO parse expression better
+ this.baseAddrWords = new BigInteger(expression, 16);
+ this.lengthWords = BigInteger.ONE;
+
+ if (baseAddrWords.compareTo(minAddrWords) < 0 || baseAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Base address out of range");
+ if (baseAddrWords.add(lengthWords).compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ this.clients = new HashSet<>();
+ // don't check whether the address is in range, because this memory block could be read outside its "range"
+ this.memListener = a -> fireContentChangeEvent();
+ this.memListenerRegistered = new AtomicBoolean();
+ }
+
+ @Override
+ public long getStartAddress()
+ {
+ return baseAddrWords.multiply(cellWidthBytesBI).longValueExact();
+ }
+
+ @Override
+ public long getLength()
+ {
+ return lengthWords.multiply(cellWidthBytesBI).longValueExact();
+ }
+
+ @Override
+ public byte[] getBytes() throws DebugException
+ {
+ BigInteger endAddrWords = baseAddrWords.add(lengthWords);
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+ int lengthBytes = lengthWords.multiply(cellWidthBytesBI).intValueExact();
+
+ byte[] bytes = new byte[lengthBytes];
+ int i;
+ long j;
+ for (i = 0, j = baseAddrWords.longValue(); i < lengthBytes; i += cellWidthBytes, j++)
+ {
+ BigInteger word = mem.getCellAsBigInteger(j);
+ System.arraycopy(word.toByteArray(), 0, bytes, i, cellWidthBytes);
+ }
+ return bytes;
+ }
+
+ @Override
+ public boolean supportsValueModification()
+ {
+ return true;
+ }
+
+ @Override
+ public void setValue(long offset, byte[] bytes) throws DebugException
+ {
+ if (offset % cellWidthBytes != 0 || bytes.length % cellWidthBytes != 0)
+ throwDebugException("Requested unaligned memory write");
+ BigInteger startAddrWords = baseAddrWords.add(BigInteger.valueOf(offset / cellWidthBytes));
+ if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Start address out of range");
+
+ BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ int i;
+ long j;
+ for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
+ {
+ BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
+ mem.setCellAsBigInteger(j, word);
+ }
+ }
+
+ @Override
+ public String getModelIdentifier()
+ {
+ return MograsimActivator.PLUGIN_ID;
+ }
+
+ @Override
+ public IDebugTarget getDebugTarget()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public ILaunch getLaunch()
+ {
+ return debugTarget.getLaunch();
+ }
+
+ @Override
+ public String getExpression()
+ {
+ return expression;
+ }
+
+ @Override
+ public BigInteger getBigBaseAddress() throws DebugException
+ {
+ return baseAddrWords;
+ }
+
+ @Override
+ public BigInteger getMemoryBlockStartAddress() throws DebugException
+ {
+ return minAddrWords;
+ }
+
+ @Override
+ public BigInteger getMemoryBlockEndAddress() throws DebugException
+ {
+ return maxAddrWords;
+ }
+
+ @Override
+ public BigInteger getBigLength() throws DebugException
+ {
+ return maxAddrWords.subtract(minAddrWords);
+ }
+
+ @Override
+ public int getAddressSize() throws DebugException
+ {
+ return Long.BYTES;
+ }
+
+ @Override
+ public boolean supportBaseAddressModification() throws DebugException
+ {
+ return true;
+ }
+
+ @Override
+ public boolean supportsChangeManagement()
+ {
+ return false;
+ }
+
+ @Override
+ public void setBaseAddress(BigInteger address) throws DebugException
+ {
+ if (address.compareTo(minAddrWords) < 0 || address.compareTo(maxAddrWords) > 0)
+ throwDebugException("Address out of range");
+ this.baseAddrWords = address;
+ }
+
+ @Override
+ public MemoryByte[] getBytesFromOffset(BigInteger unitOffset, long addressableUnits) throws DebugException
+ {
+ return getBytesFromAddress(getBigBaseAddress().add(unitOffset), addressableUnits);
+ }
+
+ @Override
+ public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException
+ {
+ if (units < 0)
+ throwDebugException("Requested negative amount of unites");
+ int lengthBytes = BigInteger.valueOf(units).multiply(cellWidthBytesBI).intValueExact();
+
+ MemoryByte[] bytes = new MemoryByte[lengthBytes];
+ int i;
+ BigInteger j;
+ for (i = 0, j = address; i < lengthBytes; i += cellWidthBytes, j = j.add(BigInteger.ONE))
+ {
+ if (j.compareTo(minAddrWords) >= 0 && j.compareTo(maxAddrWords) <= 0)
+ {
+ BigInteger word = mem.getCellAsBigInteger(j.longValue());
+ byte[] wordBytes = word.toByteArray();
+ int l = wordBytes[0] == 0 ? 1 : 0;
+ int k;
+ for (k = 0; k < cellWidthBytes - wordBytes.length + l; k++)
+ bytes[i + k] = new MemoryByte();
+ for (; k < cellWidthBytes; k++, l++)
+ bytes[i + k] = new MemoryByte(wordBytes[l]);
+ } else
+ for (int k = 0; k < cellWidthBytes; k++)
+ bytes[i + k] = new MemoryByte((byte) 0, (byte) 0);
+ }
+ return bytes;
+ }
+
+ @Override
+ public void setValue(BigInteger offset, byte[] bytes) throws DebugException
+ {
+ if (bytes.length % cellWidthBytes != 0)
+ throwDebugException("Requested unaligned memory write");
+ BigInteger startAddrWords = baseAddrWords.add(offset);
+ if (startAddrWords.compareTo(minAddrWords) < 0 || startAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("Start address out of range");
+ BigInteger endAddrWords = startAddrWords.add(BigInteger.valueOf(bytes.length / cellWidthBytes));
+ if (endAddrWords.compareTo(maxAddrWords) > 0)
+ throwDebugException("End address out of range");
+
+ unregisterMemoryListener();
+
+ int i;
+ long j;
+ for (i = 0, j = startAddrWords.longValue(); i < bytes.length; i += cellWidthBytes, j++)
+ {
+ BigInteger word = new BigInteger(bytes, i, cellWidthBytes);
+ mem.setCellAsBigInteger(j, word);
+ }
+
+ if (!clients.isEmpty())
+ registerMemoryListener();
+ fireContentChangeEvent();
+ }
+
+ @Override
+ public void connect(Object client)
+ {
+ registerMemoryListener();
+ clients.add(client);
+ }
+
+ @Override
+ public void disconnect(Object client)
+ {
+ clients.remove(client);
+
+ if (clients.isEmpty())
+ unregisterMemoryListener();
+ }
+
+ @Override
+ public Object[] getConnections()
+ {
+
+ Set<Object> clientsLocal = clients;
+ return clientsLocal == null ? new Object[0] : clientsLocal.toArray();
+ }
+
+ private void registerMemoryListener()
+ {
+ if (!memListenerRegistered.getAndSet(true))
+ mem.registerCellModifiedListener(memListener);
+ }
+
+ private void unregisterMemoryListener()
+ {
+ if (memListenerRegistered.getAndSet(false))
+ mem.deregisterCellModifiedListener(memListener);
+ }
+
+ @Override
+ public void dispose() throws DebugException
+ {
+ clients.clear();
+ unregisterMemoryListener();
+ }
+
+ @Override
+ public IMemoryBlockRetrievalExtension getMemoryBlockRetrieval()
+ {
+ return debugTarget;
+ }
+
+ @Override
+ public int getAddressableSize() throws DebugException
+ {
+ return cellWidthBytes;
+ }
+
+ /**
+ * Fires a terminate event for this debug element.
+ */
+ private void fireContentChangeEvent()
+ {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT));
+ }
+
+ /**
+ * Fires a debug event.
+ *
+ * @param event debug event to fire
+ */
+ private static void fireEvent(DebugEvent event)
+ {
+ DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
+ }
+
+ private static void throwDebugException(String message) throws DebugException
+ {
+ throw new DebugException(
+ new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.util;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class FileExtensionViewerFilter extends ViewerFilter
+{
+ private final String fileext;
+
+ public FileExtensionViewerFilter(String fileext)
+ {
+ this.fileext = fileext;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ IResource elementResource = (IResource) element;
+ switch (elementResource.getType())
+ {
+ case IResource.FILE:
+ return elementResource.getProjectRelativePath().getFileExtension().equals(fileext);
+ case IResource.FOLDER:
+ return true;
+ default:
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.util;
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Point;
+
+public class ImageDescriptorWithMargins extends CompositeImageDescriptor
+{
+ private final ImageDescriptor input;
+ private final Point size;
+ private final int ox, oy;
+
+ public ImageDescriptorWithMargins(ImageDescriptor input, Point size)
+ {
+ this(input, 0, 0, size);
+ }
+
+ public ImageDescriptorWithMargins(ImageDescriptor input, int offX, int offY, Point size)
+ {
+ this.input = input;
+ this.size = size;
+ this.ox = offX;
+ this.oy = offY;
+ }
+
+ @Override
+ protected Point getSize()
+ {
+ return size;
+ }
+
+ @Override
+ protected void drawCompositeImage(int width, int height)
+ {
+ drawImage(input::getImageData, ox, oy);
+ }
+}
\ No newline at end of file
--- /dev/null
+package net.mograsim.plugin.util;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class ProjectViewerFilter extends ViewerFilter
+{
+ private final IProject project;
+
+ public ProjectViewerFilter(IProject project)
+ {
+ this.project = project;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element)
+ {
+ if (((IResource) element).getType() == IResource.PROJECT)
+ return element == project;
+ return true;
+ }
+}
\ No newline at end of file