de.codesourcery.jasm16.emulator.EmulationOptions.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.jasm16.emulator.EmulationOptions.java

Source

/**
 * 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.io.File;
import java.util.List;
import java.util.NoSuchElementException;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import de.codesourcery.jasm16.emulator.IEmulator.EmulationSpeed;
import de.codesourcery.jasm16.emulator.ILogger.LogLevel;
import de.codesourcery.jasm16.emulator.devices.IDevice;
import de.codesourcery.jasm16.emulator.devices.impl.DefaultClock;
import de.codesourcery.jasm16.emulator.devices.impl.DefaultFloppyDrive;
import de.codesourcery.jasm16.emulator.devices.impl.DefaultKeyboard;
import de.codesourcery.jasm16.emulator.devices.impl.DefaultScreen;
import de.codesourcery.jasm16.emulator.devices.impl.DefaultVectorDisplay;
import de.codesourcery.jasm16.emulator.devices.impl.FileBasedFloppyDisk;

/**
 * Emulation options.
 *
 * @author tobias.gierke@code-sourcery.de
 */
public final class EmulationOptions {

    private static final EmulationSpeed DEFAULT_EMULATION_SPEED = EmulationSpeed.REAL_SPEED;

    /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     * Adjust the following locations when
     * adding/removing configuration options:
     * 
     * - COPY constructor !!
     * - loadEmulationOptions()
     * - saveEmulationOptions()
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     */
    private boolean enableDebugOutput = false;
    private boolean memoryProtectionEnabled = false;
    private boolean ignoreAccessToUnknownDevices = false;
    private boolean useLegacyKeyboardBuffer = false;
    private boolean mapVideoRamUponAddDevice = true;
    private boolean mapFontRamUponAddDevice = false;
    private boolean runFloppyAtFullSpeed = false;
    private EmulationSpeed emulationSpeed = DEFAULT_EMULATION_SPEED;
    private boolean crashOnStoreWithImmediate = true;

    private InsertedDisk insertedDisk;

    private boolean newEmulatorInstanceRequired = false;

    public static final class InsertedDisk {
        private final File file;
        private final boolean writeProtected;

        public InsertedDisk(File file, boolean writeProtected) {
            this.file = file;
            this.writeProtected = writeProtected;
        }

        public File getFile() {
            return file;
        }

        public boolean isWriteProtected() {
            return writeProtected;
        }
    }

    public EmulationOptions() {
    }

    public EmulationSpeed getEmulationSpeed() {
        return emulationSpeed;
    }

    /**
     * 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() {
        return crashOnStoreWithImmediate;
    }

    /**
     * Sets whether the emulation should when an 
     * instruction like <code>SET 0x1000 ,A</code> is encountered.
     * 
     * @param doCrashOnImmediate
     */
    public void setCrashOnStoreWithImmediate(boolean doCrashOnImmediate) {
        this.crashOnStoreWithImmediate = doCrashOnImmediate;
    }

    public void setEmulationSpeed(EmulationSpeed emulationSpeed) {
        if (emulationSpeed == null) {
            throw new IllegalArgumentException("emulationSpeed must not be null");
        }
        this.emulationSpeed = emulationSpeed;
    }

    public void setEnableDebugOutput(boolean enableDebugOutput) {
        this.enableDebugOutput = enableDebugOutput;
    }

    public boolean isEnableDebugOutput() {
        return enableDebugOutput;
    }

    public EmulationOptions(EmulationOptions other) {
        if (other == null) {
            throw new IllegalArgumentException("options must not be null");
        }
        this.enableDebugOutput = other.enableDebugOutput;
        this.memoryProtectionEnabled = other.memoryProtectionEnabled;
        this.ignoreAccessToUnknownDevices = other.ignoreAccessToUnknownDevices;
        this.useLegacyKeyboardBuffer = other.useLegacyKeyboardBuffer;
        this.mapFontRamUponAddDevice = other.mapFontRamUponAddDevice;
        this.mapVideoRamUponAddDevice = other.mapVideoRamUponAddDevice;
        this.runFloppyAtFullSpeed = other.runFloppyAtFullSpeed;
        this.newEmulatorInstanceRequired = other.newEmulatorInstanceRequired;
        this.insertedDisk = other.insertedDisk;
        this.emulationSpeed = other.emulationSpeed;
        this.crashOnStoreWithImmediate = other.crashOnStoreWithImmediate;
    }

    public InsertedDisk getInsertedDisk() {
        return insertedDisk;
    }

    public void setInsertedDisk(InsertedDisk disk) {
        this.insertedDisk = disk;
    }

    public void setRunFloppyAtFullSpeed(boolean runFloppyAtFullSpeed) {
        this.runFloppyAtFullSpeed = runFloppyAtFullSpeed;
    }

    public boolean isRunFloppyAtFullSpeed() {
        return runFloppyAtFullSpeed;
    }

    public boolean isMemoryProtectionEnabled() {
        return memoryProtectionEnabled;
    }

    public void setMemoryProtectionEnabled(boolean memoryProtectionEnabled) {
        this.memoryProtectionEnabled = memoryProtectionEnabled;
    }

    public boolean isIgnoreAccessToUnknownDevices() {
        return ignoreAccessToUnknownDevices;
    }

    public void setIgnoreAccessToUnknownDevices(boolean ignoreAccessToUnknownDevices) {
        this.ignoreAccessToUnknownDevices = ignoreAccessToUnknownDevices;
    }

    public boolean isUseLegacyKeyboardBuffer() {
        return useLegacyKeyboardBuffer;
    }

    public void setUseLegacyKeyboardBuffer(boolean useLegacyKeyboardBuffer) {
        newEmulatorInstanceRequired |= this.useLegacyKeyboardBuffer != useLegacyKeyboardBuffer;
        this.useLegacyKeyboardBuffer = useLegacyKeyboardBuffer;
    }

    public boolean isMapVideoRamUponAddDevice() {
        return mapVideoRamUponAddDevice;
    }

    public void setMapVideoRamUponAddDevice(boolean mapVideoRamUponAddDevice) {
        newEmulatorInstanceRequired |= this.mapVideoRamUponAddDevice != mapVideoRamUponAddDevice;
        this.mapVideoRamUponAddDevice = mapVideoRamUponAddDevice;
    }

    public boolean isMapFontRamUponAddDevice() {
        return mapFontRamUponAddDevice;
    }

    public void setMapFontRamUponAddDevice(boolean mapFontRamUponAddDevice) {
        this.newEmulatorInstanceRequired |= this.mapFontRamUponAddDevice != mapFontRamUponAddDevice;
        this.mapFontRamUponAddDevice = mapFontRamUponAddDevice;
    }

    public boolean isNewEmulatorInstanceRequired() {
        return newEmulatorInstanceRequired;
    }

    public void apply(IEmulator emulator) {
        final ILogger outLogger = new PrintStreamLogger(System.out);

        if (enableDebugOutput) {
            outLogger.setLogLevel(LogLevel.DEBUG);
        }
        emulator.setOutput(outLogger);
        emulator.setMemoryProtectionEnabled(memoryProtectionEnabled);
        emulator.setIgnoreAccessToUnknownDevices(ignoreAccessToUnknownDevices);
        emulator.setEmulationSpeed(emulationSpeed);
        emulator.setCrashOnStoreWithImmediate(crashOnStoreWithImmediate);

        try {
            final DefaultFloppyDrive drive = getFloppyDrive(emulator);
            drive.setRunAtMaxSpeed(runFloppyAtFullSpeed);
            insertDisk(drive);
        } catch (NoSuchElementException e) {
            // ok , no floppy attached
        }
    }

    public void saveEmulationOptions(Element element, Document document) {

        if (isEnableDebugOutput()) {
            element.setAttribute("debug", "true");
        }
        if (isIgnoreAccessToUnknownDevices()) {
            element.setAttribute("ignoreAccessToUnknownDevices", "true");
        }
        if (isMapFontRamUponAddDevice()) {
            element.setAttribute("mapFontRamUponAddDevice", "true");
        }
        if (isMapVideoRamUponAddDevice()) {
            element.setAttribute("mapVideoRamUponAddDevice", "true");
        }
        if (isMemoryProtectionEnabled()) {
            element.setAttribute("memoryProtectionEnabled", "true");
        }
        if (isUseLegacyKeyboardBuffer()) {
            element.setAttribute("useLegacyKeyboardBuffer", "true");
        }
        if (isRunFloppyAtFullSpeed()) {
            element.setAttribute("runFloppyAtFullSpeed", "true");
        }
        if (isCrashOnStoreWithImmediate()) {
            element.setAttribute("crashOnStoreWithImmediate", "true");
        }

        element.setAttribute("emulationSpeed", emulationSpeedToString(this.emulationSpeed));

        if (getInsertedDisk() != null) {
            final Element disks = document.createElement("disks");
            element.appendChild(disks);

            final Element disk = document.createElement("disk");
            disks.appendChild(disk);

            disk.setAttribute("writeProtected", getInsertedDisk().isWriteProtected() ? "true" : "false");
            disk.setAttribute("file", getInsertedDisk().getFile().getAbsolutePath());
        }
    }

    public static EmulationOptions loadEmulationOptions(Element element) {
        final EmulationOptions result = new EmulationOptions();
        result.setEnableDebugOutput(isSet(element, "debug"));
        result.setIgnoreAccessToUnknownDevices(isSet(element, "ignoreAccessToUnknownDevices"));
        result.setMapFontRamUponAddDevice(isSet(element, "mapFontRamUponAddDevice"));
        result.setMapVideoRamUponAddDevice(isSet(element, "mapVideoRamUponAddDevice"));
        result.setMemoryProtectionEnabled(isSet(element, "memoryProtectionEnabled"));
        result.setUseLegacyKeyboardBuffer(isSet(element, "useLegacyKeyboardBuffer"));
        result.setRunFloppyAtFullSpeed(isSet(element, "runFloppyAtFullSpeed"));
        result.setEmulationSpeed(emulationSpeedFromString(element.getAttribute("emulationSpeed")));
        result.setCrashOnStoreWithImmediate(isSet(element, "crashOnStoreWithImmediate"));

        Element disks = getChildElement(element, "disks");
        if (disks != null) {
            Element disk = getChildElement(element, "disk");
            if (disk != null) {
                final boolean writeProtected = isSet(disk, "writeProtected");
                final File file = new File(disk.getAttribute("file"));
                result.setInsertedDisk(new InsertedDisk(file, writeProtected));
            }
        }
        return result;
    }

    private static String emulationSpeedToString(EmulationSpeed speed) {
        switch (speed) {
        case MAX_SPEED:
            return "max";
        case REAL_SPEED:
            return "real";
        default:
            throw new RuntimeException("Unhandled speed: " + speed);
        }
    }

    private static EmulationSpeed emulationSpeedFromString(String s) {
        if (StringUtils.isBlank(s)) {
            return DEFAULT_EMULATION_SPEED;
        }

        switch (s) {
        case "max":
            return EmulationSpeed.MAX_SPEED;
        case "real":
            return EmulationSpeed.REAL_SPEED;
        default:
            throw new RuntimeException("Unhandled speed: '" + s + "'");
        }
    }

    private static Element getChildElement(Element parent, String tagName) {
        final NodeList nodeList = parent.getElementsByTagName(tagName);
        if (nodeList.getLength() == 1) {
            return (Element) nodeList.item(0);
        }
        if (nodeList.getLength() > 1) {
            throw new RuntimeException("Parse error, more than one <disks/> node in file?");
        }
        return null;
    }

    private static boolean isSet(Element element, String attribute) {
        final String value = element.getAttribute(attribute);
        return "true".equals(value);
    }

    public Emulator createEmulator() {
        final Emulator result = new Emulator();

        result.addDevice(new DefaultClock());
        result.addDevice(new DefaultKeyboard(useLegacyKeyboardBuffer));
        result.addDevice(new DefaultScreen(mapVideoRamUponAddDevice, mapFontRamUponAddDevice));
        result.addDevice(new DefaultFloppyDrive(runFloppyAtFullSpeed));
        result.addDevice(new DefaultVectorDisplay());

        apply(result);

        newEmulatorInstanceRequired = false;
        return result;
    }

    private void insertDisk(DefaultFloppyDrive diskDrive) {
        final InsertedDisk disk = getInsertedDisk();

        if (disk != null) {
            diskDrive.setDisk(new FileBasedFloppyDisk(disk.getFile(), disk.isWriteProtected()));
        } else {
            diskDrive.eject();
        }
    }

    public DefaultFloppyDrive getFloppyDrive(IEmulator emulator) {
        List<IDevice> result = emulator.getDevicesByDescriptor(DefaultFloppyDrive.DESC);
        if (result.isEmpty()) {
            throw new NoSuchElementException("Internal error, found no floppy drive?");
        }
        if (result.size() > 1) {
            throw new RuntimeException("Internal error, found more than one floppy drive?");
        }
        return (DefaultFloppyDrive) result.get(0);
    }

    public DefaultScreen getScreen(IEmulator emulator) {
        List<IDevice> result = emulator.getDevicesByDescriptor(DefaultScreen.DESC);
        if (result.isEmpty()) {
            throw new NoSuchElementException("Internal error, found no default screen?");
        }
        if (result.size() > 1) {
            throw new RuntimeException("Internal error, found more than one default screen?");
        }
        return (DefaultScreen) result.get(0);
    }

    public DefaultVectorDisplay getVectorDisplay(IEmulator emulator) {
        List<IDevice> result = emulator.getDevicesByDescriptor(DefaultVectorDisplay.DEVICE_DESCRIPTOR);
        if (result.isEmpty()) {
            throw new NoSuchElementException("Internal error, found no default vector display ?");
        }
        if (result.size() > 1) {
            throw new RuntimeException("Internal error, found more than one vector display ?");
        }
        return (DefaultVectorDisplay) result.get(0);
    }

    public DefaultKeyboard getKeyboard(IEmulator emulator) {
        List<IDevice> result = emulator.getDevicesByDescriptor(DefaultKeyboard.DESC);
        if (result.isEmpty()) {
            throw new NoSuchElementException("Internal error, found no default keyboard ?");
        }
        if (result.size() > 1) {
            throw new RuntimeException("Internal error, found more than one default keyboard ?");
        }
        return (DefaultKeyboard) result.get(0);
    }
}