/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.codesourcery.jasm16.emulator;
import java.util.List;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.emulator.devices.DeviceDescriptor;
import de.codesourcery.jasm16.emulator.devices.IDevice;
import de.codesourcery.jasm16.emulator.devices.IInterrupt;
import de.codesourcery.jasm16.emulator.exceptions.DeviceErrorException;
import de.codesourcery.jasm16.emulator.exceptions.MemoryProtectionFaultException;
import de.codesourcery.jasm16.emulator.memory.IMemoryRegion;
import de.codesourcery.jasm16.emulator.memory.IReadOnlyMemory;
import de.codesourcery.jasm16.emulator.memory.MainMemory;
/**
* DCPU-16 emulator abstraction.
*
* @author tobias.gierke@code-sourcery.de
*/
public interface IEmulator
{
public enum EmulationSpeed
{
/**
* Run the emulation as fast as possible.
*/
MAX_SPEED,
/**
* Try to run emulation at the DCPU-16s native speed (~100 kHz).
*/
REAL_SPEED;
}
public void addBreakpoint(Breakpoint bp);
/**
* Adds a device to this emulator.
*
* <p> This method will invoke {@link IDevice#afterAddDevice(IEmulator)} after
* the device has been registered with the emulator. If this call fails,
* the device will be removed from the emulator again before propagating
* the exception.</p>.
*
* @param device
* @return the hardware slot number where the device has been added
* @throws DeviceErrorException if the {@link IDevice#afterAddDevice(IEmulator)} call failed
*/
public int addDevice(IDevice device) throws DeviceErrorException;
/**
* Invokes a callback , passing mutable instances of this emulator's CPU and memory.
*
* <p>The executing callback has exclusive access to the emulator's CPU and memory ,
* meaning all updates are atomic and the CPU and memory state cannot be changed concurrently
* by other threads.</p>
*
* @param invoker
* @return
*/
public <T> T doWithEmulator(IEmulatorInvoker<T> invoker);
/**
* Returns a list of devices with matching hardware IDs , versions and manufacturer IDs.
*
* @param desc
* @return
* @see DeviceDescriptor#matches(DeviceDescriptor)
*/
public List<IDevice> getDevicesByDescriptor(DeviceDescriptor desc);
/**
* Adds a new device, replacing any existing device with the same
* device descriptor.
*
* <p>Devices are replaced if their hardware ID , manufacturer ID and version match.</p>
*
* @param device
* @return
* @throws DeviceErrorException
* @throws IllegalStateException if more than one device with matching device descriptor are already registered,
*/
public int addOrReplaceDevice(IDevice device) throws DeviceErrorException;
public void addEmulationListener(IEmulationListener listener);
public void breakpointChanged(Breakpoint breakpoint);
public void calibrate();
/**
* Enable/disable instruction memory write protection.
*
* <p>When enabled, the emulator will keep track of all memory
* regions that contain executable code and will stop
* emulation with {@link MemoryProtectionFaultException}
* whenever the program tries to write to one of these locations</p>.
*
* <p>It's obviously not a good idea to enable this feature when running self-modifying code.</p>
*
* @param enabled
* @see MemoryProtectionFaultException
* @see #isMemoryProtectionEnabled()
*/
public void setMemoryProtectionEnabled(boolean enabled);
/**
* Check whether the emulation is currently running with memory-protection enabled.
*
* @return
* @see #setMemoryProtectionEnabled(boolean)
*/
public boolean isMemoryProtectionEnabled();
/**
* Returns whether the emulation should stop when an
* instruction like <code>SET 0x1000 ,A</code> is encountered.
*
* @return <code>true</code> if emulation should stop, <code>false</code> if emulation
* should silently continue
*/
public boolean isCrashOnStoreWithImmediate();
/**
* Sets whether the emulation should when an
* instruction like <code>SET 0x1000 ,A</code> is encountered.
*
* @param doCrashOnImmediate
*/
public void setCrashOnStoreWithImmediate(boolean doCrashOnImmediate);
/**
* Disposes this emulator.
*
* <p>This method terminates the emulator and releases all associated allocated resources. After this method returns,
* the emulator is no longer in a usable state.</p>
* <p>Calling this method more than once does no harm.</p>
*/
public void dispose();
public void deleteBreakpoint(Breakpoint bp);
public void executeOneInstruction();
/**
* Check it's ok to invoke {@link #stepReturn()} because
* the PC is currently pointing at a JSR instruction.
*
* @return
*/
public boolean canStepReturn();
/**
* Executes a subroutine and stop at the next instruction after returning.
*
* <p>Use {@link #canStepReturn()} to check whether the PC is currently pointing
* at a JSR instruction.</p>
*
* @throws IllegalStateException if the PC is not currently at a JSR instruction
* @see #canStepReturn()
*/
public void stepReturn() throws IllegalStateException;
public Breakpoint getBreakPoint(Address address);
public List<Breakpoint> getBreakPoints();
/**
* Returns a read-only instance of this emulator's CPU.
*
* <p>If you want to change the CPU's state, you need to use {@link #doWithEmulator(IEmulatorInvoker)}</p>.
*
* @return
*/
public IReadOnlyCPU getCPU();
/**
* Returns a textual description of the last
* emulation error.
*
* <p>Note that this error message gets cleared every time
* {@link #start()} , {@link #stop()} ,{@link #reset(boolean)} or {@link #loadMemory(Address, byte[])}
* are called.
* </p>
* @return error message or <code>null</code>
*/
public Throwable getLastEmulationError();
public void setEmulationSpeed(EmulationSpeed speed);
public EmulationSpeed getEmulationSpeed();
public List<IDevice> getDevices();
/**
* Returns a read-only instance of this emulator's memory.
*
* <p>If you want to write to memory, you need to use {@link #doWithEmulator(IEmulatorInvoker)}</p>.
*
* @return
*/
public IReadOnlyMemory getMemory();
/**
* Clears the memory and populates it with data from a byte array.
*
* <p>This method will {@link #stop()} the emulator before updating the memory.</p>
*
* <p>Note that since this method calls {@link #stop()} it
* will also delete all one-shot breakpoints.</p>
*
* @param startingOffset
* @param data
*/
public void loadMemory(Address startingOffset, byte[] data);
/**
* Maps main memory to a specific region.
*
* @param region
* @see #unmapRegion(IMemoryRegion)
* @see MainMemory#mapRegion(IMemoryRegion)
*/
public void mapRegion(IMemoryRegion region);
/**
* Remove a registered device.
*
* <p>If the device is not registered , does nothing.</p>
*
* @param device
* @throws DeviceErrorException if the {@link IDevice#beforeRemoveDevice(IEmulator)} call failed
*/
public void removeDevice(IDevice device) throws DeviceErrorException;
public void removeEmulationListener(IEmulationListener listener);
/**
* Removes all registered emulation listeners <b>except</b> listeners
* that belong to hardware devices.
*
* @see IEmulationListener#belongsToHardwareDevice()
*/
public void removeAllEmulationListeners();
// emulator control
/**
* Reset the emulator,optionally clearing the memory as well.
*
* <p>This method stops the emulator and resets CPU,all devices and (optionally) the memory. Note that calling this method will also delete all one-shot breakpoints.</p>
*
* @param clearMemory whether the memory should be cleared
*/
public void reset(boolean clearMemory);
public void skipCurrentInstruction();
public void start();
/**
* Stop the emulation.
*
* <p>Note that calling this method will also delete all one-shot breakpoints.</p>
* @return <code>true</code> if the emulation was currently running and has been stopped.
*/
public boolean stop();
/**
* Triggers an interrupt.
*
* <p>If the interrupt queue is empty, the interrupt will be handled
* <b>after</b> the next regular instruction execution.If the interrupt queue
* is not yet full ( less than 256 interrupts queued), the interrupt
* will be added to the queue. If interrupts are currently disabled,
* this method will return <code>false</code> and do nothing.</p>
* @param interrupt
* @return <code>false</code> if interrupts are disabled (IA is set to 0),
* otherwise <code>true</code>
*/
public boolean triggerInterrupt(IInterrupt interrupt);
/**
* Replaces a mapped memory region with plain (unmapped) main-memory.
*
* @param region
* @see MainMemory#unmapRegion(IMemoryRegion)
*/
public void unmapRegion(IMemoryRegion region);
public boolean isCalibrated();
public void setOutput(ILogger logger);
public ILogger getOutput();
public void setIgnoreAccessToUnknownDevices(boolean yesNo);
/**
* Removes all breakpoints from this emulator and invokes
* {@link IEmulationListener#allBreakpointsDeleted(IEmulator)} afterwards.
*/
public void deleteAllBreakpoints();
}