diff --git a/com/tado/mspsim/core/ADC12Plus.java b/com/tado/mspsim/core/ADC12Plus.java new file mode 100644 index 0000000..c34800d --- /dev/null +++ b/com/tado/mspsim/core/ADC12Plus.java @@ -0,0 +1,415 @@ +/** + * Copyright (c) 2007, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of MSPSim. + * + * $Id$ + * + * ----------------------------------------------------------------- + * + * ADC12 + * + * Each time a sample is converted the ADC12 system will check for EOS flag + * and if not set it just continues with the next conversion (x + 1). + * If EOS next conversion is startMem. + * Interrupt is triggered when the IE flag are set! + * + * + * Author : Joakim Eriksson + * Created : Sun Oct 21 22:00:00 2007 + * Updated : $Date$ + * $Revision$ + */ + +package com.tado.mspsim.core; + +import java.util.Arrays; + +import se.sics.mspsim.core.ADCInput; +import se.sics.mspsim.core.IOUnit; +import se.sics.mspsim.core.MSP430Core; +import se.sics.mspsim.core.TimeEvent; +import se.sics.mspsim.core.EmulationLogger.WarningType; + +/** + * ADC12 Plus IO peripheral + * + * This module extends the functionality of the ADC12 by adding one + * configuration register which controls the power consumption of this + * peripheral. Additionally, implements several resolutions (8 to 12-bit) + * + * The rest of functionalities are the same as the standard ADC12 + * + * @author Víctor Ariño + */ +public class ADC12Plus extends IOUnit { + + /** + * Address and size for IO configuration + */ + public static int OFFSET = 0x0700; + public static int SIZE = 0x3e; + + public static final int ADC12CTL0 = 0x00;// Reset with POR + public static final int ADC12CTL1 = 0x02;// Reset with POR + public static final int ADC12CTL2 = 0x04;// Reset with POR XXX + + public static final int ADC12IFG = 0x0a; // Reset with POR + public static final int ADC12IE = 0x0c; // Reset with POR + public static final int ADC12IV = 0x0e; // Reset with POR + + public static final int ADC12MEM0 = 0x20; // Unchanged + public static final int ADC12MEM1 = 0x22; // Unchanged + public static final int ADC12MEM2 = 0x24; // Unchanged + public static final int ADC12MEM3 = 0x26; // Unchanged + public static final int ADC12MEM4 = 0x28; // Unchanged + public static final int ADC12MEM5 = 0x2A; // Unchanged + public static final int ADC12MEM6 = 0x2C; // Unchanged + public static final int ADC12MEM7 = 0x2E; // Unchanged + public static final int ADC12MEM8 = 0x30; // Unchanged + public static final int ADC12MEM9 = 0x32; // Unchanged + public static final int ADC12MEM10 = 0x34; // Unchanged + public static final int ADC12MEM11 = 0x36; // Unchanged + public static final int ADC12MEM12 = 0x38; // Unchanged + public static final int ADC12MEM13 = 0x3A; // Unchanged + public static final int ADC12MEM14 = 0x3C; // Unchanged + public static final int ADC12MEM15 = 0x3E; // Unchanged + + public static final int ADC12MCTL0 = 0x0010; // Reset with POR + public static final int ADC12MCTL1 = 0x0011; // Reset with POR + public static final int ADC12MCTL2 = 0x0012; // Reset with POR + public static final int ADC12MCTL3 = 0x0013; // Reset with POR + public static final int ADC12MCTL4 = 0x0014; // Reset with POR + public static final int ADC12MCTL5 = 0x0015; // Reset with POR + public static final int ADC12MCTL6 = 0x0016; // Reset with POR + public static final int ADC12MCTL7 = 0x0017; // Reset with POR + public static final int ADC12MCTL8 = 0x0018; // Reset with POR + public static final int ADC12MCTL9 = 0x0019; // Reset with POR + public static final int ADC12MCTL10 = 0x001A; // Reset with POR + public static final int ADC12MCTL11 = 0x001B; // Reset with POR + public static final int ADC12MCTL12 = 0x001C; // Reset with POR + public static final int ADC12MCTL13 = 0x001D; // Reset with POR + public static final int ADC12MCTL14 = 0x001E; // Reset with POR + public static final int ADC12MCTL15 = 0x001F; // Reset with POR + + public static final int[] SHTBITS = new int[] { 4, 8, 16, 32, 64, 96, 128, + 192, 256, 384, 512, 768, 1024, 1024, 1024, 1024 }; + + public static final int BUSY_MASK = 0x01; + public static final int EOS_MASK = 0x80; + + public static final int CONSEQ_SINGLE = 0x00; + public static final int CONSEQ_SEQUENCE = 0x01; + public static final int CONSEQ_REPEAT_SINGLE = 0x02; + public static final int CONSEQ_REPEAT_SEQUENCE = 0x03; + public static final int CONSEQ_SEQUENCE_MASK = 0x01; + + private int adc12ctl0 = 0; + private int adc12ctl1 = 0; + private int adc12ctl2 = 0; + private int[] adc12mctl = new int[16]; + private int[] adc12mem = new int[16]; + private int adc12Pos = 0; + + private int shTime0 = 4; + private int shTime1 = 4; + private boolean adc12On = false; + private boolean enableConversion; + private boolean startConversion; + private boolean isConverting; + + private int shSource = 0; + private int startMem = 0; + private int adcDiv = 1; + + private ADCInput adcInput[] = new ADCInput[16]; + + private int conSeq; + private int adc12ie; + private int adc12ifg; + private int adc12iv; + + private int adcSSel; + private int adc12Vector = 0x38; + + private TimeEvent adcTrigger = new TimeEvent(0) { + public void execute(long t) { + // System.out.println(getName() + " **** executing update timers at " + + // t + " cycles=" + cpu.cycles); + convert(); + } + }; + + /* These are CTL2 variables */ + private int bitsResolution = 12; + private boolean formatSigned = false; + private int clockPredivider = 1; + + /* Reference voltage 2.5V or 1.5V */ + private boolean ref25V = false; + + public ADC12Plus(MSP430Core cpu, int offset, int intVector) { + super("ADC12Plus", cpu, cpu.memory, offset); + adc12Vector = intVector; + } + + public void reset(int type) { + enableConversion = false; + startConversion = false; + isConverting = false; + adc12ctl0 = 0; + adc12ctl1 = 0; + adc12ctl2 = 0; + shTime0 = shTime1 = 4; + adc12On = false; + shSource = 0; + startMem = adc12Pos = 0; + adcDiv = 1; + + conSeq = 0; + adc12ie = 0; + adc12ifg = 0; + adc12iv = 0; + adcSSel = 0; + + Arrays.fill(adc12mctl, 0); + + clockPredivider = 1; + formatSigned = false; + bitsResolution = 12; + ref25V = false; + } + + public void setADCInput(int adindex, ADCInput input) { + adcInput[adindex] = input; + } + + /** + * Get the maximum input voltage set by the configuration of the registers + * (in mV) + * + * @return + */ + public int getMaxInVoltage() { + if (ref25V) { + return 2500; + } else { + return 1500; + } + } + + /** + * Get the minimum in voltage set by the registers (in mV) + * + * @return + */ + public int getMinInVoltage() { + // TODO + return 0; + } + + // write a value to the IO unit + public void write(int address, int value, boolean word, long cycles) { + address -= offset; + switch (address) { + case ADC12CTL0: + if (enableConversion) { + // Ongoing conversion: only some parts may be changed + adc12ctl0 = (adc12ctl0 & 0xfff0) + (value & 0xf); + } else { + adc12ctl0 = value; + shTime0 = SHTBITS[(value >> 8) & 0x0f]; + shTime1 = SHTBITS[(value >> 12) & 0x0f]; + adc12On = (value & 0x10) > 0; + } + enableConversion = (value & 0x02) > 0; + startConversion = (value & 0x01) > 0; + ref25V = (value & 0x20) > 0; + + if (DEBUG) + log("Set SHTime0: " + shTime0 + " SHTime1: " + shTime1 + " ENC:" + + enableConversion + " Start: " + startConversion + + " ADC12ON: " + adc12On); + if (adc12On && enableConversion && startConversion && !isConverting) { + // Set the start time to be now! + isConverting = true; + adc12Pos = startMem; + int delay = clockPredivider * adcDiv + * ((adc12Pos < 8 ? shTime0 : shTime1) + 13); + cpu.scheduleTimeEvent(adcTrigger, cpu.getTime() + delay); + } + break; + case ADC12CTL1: + if (enableConversion) { + // Ongoing conversion: only some parts may be changed + adc12ctl1 = (adc12ctl1 & 0xfff8) + (value & 0x6); + } else { + adc12ctl1 = value & 0xfffe; + startMem = (value >> 12) & 0xf; + shSource = (value >> 10) & 0x3; + adcDiv = ((value >> 5) & 0x7) + 1; + adcSSel = (value >> 3) & 0x03; + } + conSeq = (value >> 1) & 0x03; + if (DEBUG) + log("Set startMem: " + startMem + " SHSource: " + shSource + + " ConSeq-mode:" + conSeq + " Div: " + adcDiv + " ADCSSEL: " + + adcSSel); + break; + + case ADC12CTL2: /* Low Power Specs */ + if (enableConversion) { + /* + * Clock pre divider can't be modified when conversion is already + * enabled + */ + value &= 0xfeff; + value |= (adc12ctl2 & 0x100); + } + clockPredivider = ((value & 0x100) > 0) ? 4 : 1; + /* bit resolution 8, 10, 12 */ + int tmp = (value & 0x30) >> 4; + tmp = (tmp <= 2) ? tmp * 2 : 4; + bitsResolution = tmp + 8; + formatSigned = ((value & 0x08) > 0); + if (formatSigned) { + logw(WarningType.EMULATION_ERROR, "signed format not implemented"); + } + adc12ctl2 = value; + break; + case ADC12IE: + adc12ie = value; + break; + case ADC12IFG: + adc12ifg = value; + break; + default: + if (address >= ADC12MCTL0 && address <= ADC12MCTL15) { + if (enableConversion) { + /* Ongoing conversion: not possible to modify */ + } else { + adc12mctl[address - ADC12MCTL0] = value & 0xff; + if (DEBUG) + log("ADC12MCTL" + (address - ADC12MCTL0) + " source = " + + (value & 0xf) + + (((value & EOS_MASK) != 0) ? " EOS bit set" : "")); + } + } + } + } + + // read a value from the IO unit + public int read(int address, boolean word, long cycles) { + address -= offset; + switch (address) { + case ADC12CTL0: + return adc12ctl0; + case ADC12CTL1: + return isConverting ? (adc12ctl1 | BUSY_MASK) : adc12ctl1; + case ADC12IE: + return adc12ie; + case ADC12IFG: + return adc12ifg; + default: + if (address >= ADC12MCTL0 && address <= ADC12MCTL15) { + return adc12mctl[address - ADC12MCTL0]; + } else if (address >= ADC12MEM0 && address <= ADC12MEM15) { + int reg = (address - ADC12MEM0) / 2; + // Clear ifg! + adc12ifg &= ~(1 << reg); + // System.out.println("Read ADCMEM" + (reg / 2)); + if (adc12iv == reg * 2 + 6) { + cpu.flagInterrupt(adc12Vector, this, false); + adc12iv = 0; + // System.out.println("** de-Trigger ADC12 IRQ for ADCMEM" + + // adc12Pos); + } + return adc12mem[reg]; + } + } + return 0; + } + + int smp = 0; + + private void convert() { + // If off then just return... + if (!adc12On) { + isConverting = false; + return; + } + boolean runAgain = enableConversion && conSeq != CONSEQ_SINGLE; + // Some noise... + ADCInput input = adcInput[adc12mctl[adc12Pos] & 0xf]; + int reading = input != null ? input.nextData() : 2048 + 100 - smp & 255; + /* Adapt resolution to 8, 10, 12 bits */ + float percent = (reading - getMinInVoltage()) * 1f + / ((getMaxInVoltage() - getMinInVoltage()) * 1f); + reading = (int) (((1L << bitsResolution) - 1) * percent); + adc12mem[adc12Pos] = reading; + smp += 7; + adc12ifg |= (1 << adc12Pos); + if ((adc12ie & (1 << adc12Pos)) > 0) { + // This should check if there already is an higher iv! + adc12iv = adc12Pos * 2 + 6; + // System.out.println("** Trigger ADC12 IRQ for ADCMEM" + adc12Pos); + cpu.flagInterrupt(adc12Vector, this, true); + } + if ((conSeq & CONSEQ_SEQUENCE_MASK) != 0) { + // Increase + if ((adc12mctl[adc12Pos] & EOS_MASK) == EOS_MASK) { + adc12Pos = startMem; + if (conSeq == CONSEQ_SEQUENCE) { + // Single sequence only + runAgain = false; + } + } else { + adc12Pos = (adc12Pos + 1) & 0x0f; + } + } + if (!runAgain) { + isConverting = false; + } else { + int delay = clockPredivider * adcDiv + * ((adc12Pos < 8 ? shTime0 : shTime1) + 13); + cpu.scheduleTimeEvent(adcTrigger, adcTrigger.getTime() + delay); + } + } + + public void interruptServiced(int vector) { + } + + /** + * Get the reference voltage if it is 1.5 volts or 2.5V + * + * @return + */ + public boolean isRef25V() { + return ref25V; + } + +} diff --git a/com/tado/mspsim/core/AES128.java b/com/tado/mspsim/core/AES128.java new file mode 100644 index 0000000..392c9ab --- /dev/null +++ b/com/tado/mspsim/core/AES128.java @@ -0,0 +1,423 @@ +package com.tado.mspsim.core; + +import java.nio.ByteBuffer; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +import se.sics.mspsim.core.IOUnit; +import se.sics.mspsim.core.MSP430Core; +import se.sics.mspsim.core.EmulationLogger.WarningType; + +/** + * AES128 msp430 peripheral emulation + * + * TODO: + * advanced cipher modes + * longer key support + * interrupts + * timing concerns + * first round key operations + * + * @author Víctor Ariño + */ +public class AES128 extends IOUnit { + + /** + * Address and size for IO configuration + */ + public static int OFFSET = 0x09C0; + public static int SIZE = 12; + + /** + * Enable/Disable debug output of the module + */ + private static boolean DEBUG = false; + + /** + * Register definition as in the documentation Offset from 0xff80 + */ + public static final int AES_VECTOR = 0x005A; + + /* Main registers */ + public static final int AESACTL0 = 0x00; + public static final int AESACTL1 = 0x02; + public static final int AESASTAT = 0x04; + public static final int AESAKEY = 0x06; + public static final int AESADIN = 0x08; + public static final int AESADOUT = 0x0a; + + /* AESACTL0 Control Bits */ + public static final int AESSWRST = 0x0080; + public static final int AESRDYIFG = 0x0100; + public static final int AESERRFG = 0x0800; + public static final int AESRDYIE = 0x1000; + + public static final int AESOP_0 = 0x0000; + public static final int AESOP_1 = 0x0001; + public static final int AESOP_2 = 0x0002; + public static final int AESOP_3 = 0x0003; + + /* AESASTAT Control Bits */ + public static final int AESBUSY = 0x0001; + public static final int AESKEYWR = 0x0002; + public static final int AESDINWR = 0x0004; + public static final int AESDOUTRD = 0x0008; + public static final int AESCMEN = 0x8000; + + /* Cipher modes */ + private static final int MODE_ECB = 0; + private static final int MODE_CBC = 1; + private static final int MODE_OFB = 2; + private static final int MODE_CFB = 3; + + /* Key lengths */ + private static final int KEY_128 = 0; + private static final int KEY_192 = 1; + private static final int KEY_256 = 2; + + /** + * Emulate peripheral using the default offset + * + * @param cpu + */ + public AES128(MSP430Core cpu) { + this(cpu, OFFSET); + } + + /** + * Emulate peripheral using custom offset + * + * @param cpu + * @param offset + */ + public AES128(MSP430Core cpu, int offset) { + super("CRC", cpu, cpu.memory, offset); + } + + /** + * Clear everything when reset. The cleared fields are specified in the + * manual + */ + public void reset(int type) { + /* + * AES software reset. Immediately resets the complete AES accelerator + * module even when busy except for the AESRDYIE, the AESKLx and the + * AESOPx bits. It also clears the (internal) state memory. + */ + key.clear(); + inData.clear(); + outData.clear(); + isBusy = false; + advancedCipherMode = false; + interruptEnable = false; + errorFlag = false; + resetFlag = false; + cipherMode = MODE_ECB; + cipherBlockCounter = 0; + } + + /** + * Variable holders for the different registers needed by this peripheral + */ + private ByteBuffer key = ByteBuffer.allocate(16); + private ByteBuffer inData = ByteBuffer.allocate(16); + private ByteBuffer outData = ByteBuffer.allocate(16); + + /** + * Syntax sugar + */ + private boolean isBusy = false; + + /** + * Nice names for buffer operations :-) + */ + private int bytesReadOut() { + return outData.position(); + } + + private int bytesWrittenIn() { + return inData.position(); + } + + private int bytesKeyWritten() { + return key.position(); + } + + private boolean allBytesReadOut() { + return !outData.hasRemaining(); + } + + private boolean isKeyReady() { + return !key.hasRemaining(); + } + + private boolean isDataReady() { + return !inData.hasRemaining(); + } + + private int getStatReg() { + int stat = 0; + stat |= (bytesReadOut() & 0x0f) << 12; + stat |= (bytesWrittenIn() & 0x0f) << 8; + stat |= (bytesKeyWritten() & 0x0f) << 4; + stat |= (allBytesReadOut() ? 1 : 0) << 3; + stat |= ((isDataReady() ? 1 : 0) & 0x01) << 2; + stat |= ((isKeyReady() ? 1 : 0) & 0x01) << 1; + stat |= ((isBusy ? 1 : 0) & 0x01); + return stat & 0xffff; + } + + /* AESACTL0 register */ + private boolean advancedCipherMode = false; + private boolean interruptEnable = false; + private boolean errorFlag = false; + private boolean readyInterruptFlag = false; + private boolean resetFlag = false; + private int cipherMode = MODE_ECB; + private int keyLength = KEY_128; + private int operation = AESOP_0; + private int cipherBlockCounter = 0; + + /** + * CTL0 Register built upon variables + * + * @return uint16_t register + */ + private int getCTL0Reg() { + int ctl0 = 0; + ctl0 |= ((advancedCipherMode ? 1 : 0) & 0x01) << 15; + ctl0 |= ((interruptEnable ? 1 : 0) & 0x01) << 12; + ctl0 |= ((errorFlag ? 1 : 0) & 0x01) << 11; + ctl0 |= ((readyInterruptFlag ? 1 : 0) & 0x01) << 8; + ctl0 |= ((resetFlag ? 1 : 0) & 0x01) << 7; + ctl0 |= (cipherMode & 0x03) << 5; + ctl0 |= (keyLength & 0x03) << 2; + ctl0 |= (operation & 0x03); + return ctl0 & 0xffff; + } + + private int getCTL1Reg() { + int ctl1 = cipherBlockCounter; + return ctl1 & 0x000f; + } + + /** + * Java implementation of the AES encryption algorithm + * + * This method encrypts whatever is in inData using key and sets it into + * outData + */ + private void aesEncrypt() { + log("encrypt"); + Cipher cipher; + SecretKeySpec spec = new SecretKeySpec(key.array(), "AES"); + try { + cipher = Cipher.getInstance("AES/ECB/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, spec); + byte[] bytes = cipher.doFinal(inData.array()); + outData.clear(); + outData.put(bytes); + outData.rewind(); + } catch (Exception e) { + log(e.getStackTrace().toString()); + } + } + + /** + * Java implementation of the AES decryption algorithm + * + * This method decrypts whatever is in inData using key and sets it into + * outData + */ + private void aesDecrypt() { + log("decrypt"); + Cipher cipher; + SecretKeySpec spec = new SecretKeySpec(key.array(), "AES"); + try { + cipher = Cipher.getInstance("AES/ECB/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, spec); + byte[] bytes = cipher.doFinal(inData.array()); + outData.clear(); + outData.put(bytes); + outData.rewind(); + } catch (Exception e) { + log(e.getStackTrace().toString()); + } + } + + /* + * The inherited log function is not working for whatever reason. A quick + * redefinition helps a lot while developing the module + */ + @Override + protected void log(String msg) { + if (DEBUG) { + System.out.println(msg); + } + } + + /** + * Log using printf format + * + * @param format + * @param arguments + */ + protected void log(final String format, final Object... arguments) { + if (DEBUG) { + System.out.printf(format, arguments); + } + } + + /** + * The registers are written + */ + public void write(int address, int value, boolean word, long cycles) { + log("write @ %x <-- %x (word=%b)\n", address, value, word); + int lo = (value) & 0xff; // low byte + int hi = (value >> 8) & 0xff; // high byte + + switch (address - offset) { + case AESACTL0: + if ((value & AESSWRST) == AESSWRST) { + reset(0); + } + + if (!(advancedCipherMode && cipherBlockCounter > 0)) { + advancedCipherMode = ((value & AESCMEN) == AESCMEN); + } + + interruptEnable = ((value & AESRDYIE) == AESRDYIE); + errorFlag = ((value & AESERRFG) == AESERRFG); + readyInterruptFlag = ((value & AESRDYIFG) == AESRDYIFG); + + if (!(advancedCipherMode && cipherBlockCounter > 0) + && !advancedCipherMode) { + cipherMode = ((value & 0x60) >> 5); + } + + keyLength = ((value & 0xC) >> 2); + + if (!(advancedCipherMode && cipherBlockCounter > 0)) { + operation = value & 0x03; + } + break; + + case AESACTL1: + /* Only lower byte allowed */ + if (!(advancedCipherMode && cipherBlockCounter > 0)) { + value &= 0x000f; + cipherBlockCounter = value; + } + + break; + + case AESASTAT: + /* Only two fields can be written in this register */ + value &= (AESDINWR | AESKEYWR); + + if ((value & AESKEYWR) == 0) { + /* + * This flag can only be cleared if the advanceModeSupport is not + * enabled (ref. User Guide) + */ + if (!advancedCipherMode) { + value |= AESKEYWR; + } else { + key.rewind(); + } + } else { + isBusy = true; + key.position(key.limit()); + switch (operation) { + case AESOP_0: // encrypt + aesEncrypt(); + break; + case AESOP_1: // decrypt + aesDecrypt(); + break; + case AESOP_2: // gen 1st round key + logw(WarningType.ILLEGAL_IO_WRITE, "to implement"); + break; + case AESOP_3: // decrypt 1st round key + logw(WarningType.ILLEGAL_IO_WRITE, "to implement"); + break; + } + isBusy = false; + readyInterruptFlag = true; + } + + if ((value & AESDINWR) == 0) { + if (!advancedCipherMode) { + value |= AESKEYWR; + } else { + inData.rewind(); + } + } else { + inData.position(inData.limit()); + } + + break; + + case AESAKEY: + if (key.hasRemaining()) { + /* Clear the ready interrupt flag */ + readyInterruptFlag = false; + key.put((byte) lo); + if (word && key.hasRemaining()) { + key.put((byte) hi); + } + } + break; + + case AESADIN: + if (inData.hasRemaining()) { + /* Clear the ready interrupt flag */ + readyInterruptFlag = false; + inData.put((byte) lo); + if (word && inData.hasRemaining()) { + inData.put((byte) hi); + } + } + break; + } + + log("ctl0: %04x\nstat: %04x\n", getCTL0Reg(), getStatReg()); + } + + /** + * Registers are read + */ + public int read(int address, boolean word, long cycles) { + log("read %x (word?%b)\n", address, word); + switch (address - offset) { + case AESACTL0: + return getCTL0Reg(); + case AESACTL1: + if (advancedCipherMode) { + return cipherBlockCounter; + } + case AESASTAT: + return getStatReg(); + case AESADOUT: + if (isDataReady()) { + /* Clear the ready interrupt flag */ + readyInterruptFlag = false; + if (outData.hasRemaining()) { + int temp = outData.get() & 0xff; + if (word && outData.hasRemaining()) { + temp |= (outData.get() << 8) & 0xff00; + } + return temp & 0xffff; + } + } + } + return 0; + } + + public void interruptServiced(int vector) { + if (vector == AES_VECTOR) { + readyInterruptFlag = false; + } + } +} diff --git a/com/tado/mspsim/core/CRC16.java b/com/tado/mspsim/core/CRC16.java new file mode 100644 index 0000000..62a8d76 --- /dev/null +++ b/com/tado/mspsim/core/CRC16.java @@ -0,0 +1,221 @@ +package com.tado.mspsim.core; + +import se.sics.mspsim.core.IOUnit; +import se.sics.mspsim.core.Loggable; +import se.sics.mspsim.core.MSP430Core; + +/** + * CRC16 module for the MSP430 + * + * @author Victor Ariño + */ +public class CRC16 extends IOUnit { + + /** + * Address and size for IO configuration + */ + public static final int OFFSET = 0x0150; + public static final int SIZE = 8; + + /** + * Register offsets definition as in the documentation. + */ + public static final int CRCDI = 0; + public static final int CRCDIRB = 0x2; + public static final int CRCINIRES = 0x4; + public static final int CRCRESR = 0x6; + + /** + * Initial seed as recommended by CCITT + */ + private static final int CCITTSeed = 0xFFFF; + + /** + * CRC16 peripheral for the MSP430 using the default memory offset + * + * @param cpu + */ + public CRC16(MSP430Core cpu) { + this(cpu, OFFSET); + } + + /** + * CRC16 peripheral for the MSP430 + * + * @param cpu + * CPU core + * @param offset + * Address offset, by default is 0x0150 + */ + public CRC16(MSP430Core cpu, int offset) { + super("CRC16", cpu, cpu.memory, offset); + setLogLevel(Loggable.DEBUG); + } + + /** + * CRC16 CCITT Java implementation + * + * @author Víctor Ariño + */ + private class CRC16Java { + private int crc = CCITTSeed; + private int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) + + /** + * Adds one byte to the CRC computation + * + * @param b + * byte to add + */ + protected void process(byte b) { + for (int i = 0; i < 8; i++) { + boolean bit = ((b >> (7 - i) & 1) == 1); + boolean c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) + crc ^= polynomial; + } + } + + /** + * Add one byte in reverse way + * + * @param b + * byte to add + */ + protected void processRb(byte b) { + process(reflectByte(b)); + } + + /** + * Set the seed of the CRC + * + * @param seed + */ + protected void reset(int seed) { + crc = seed; + } + + /** + * Get computed CRC + * + * @return + */ + protected int getCRC() { + return crc; + } + + /** + * Reflect a byte + * + * @author Valentin Sawadski + * @param b + * byte to reflect + * @return reversed byte + */ + private byte reflectByte(byte b) { + byte ret = 0; + for (int i = 0; i < 8; i++) { + if ((b & (1 << i)) == (1 << i)) { + ret += (1 << (7 - i)); + } + } + return ret; + } + + /** + * Reflect all bytes of the CRC + * + * @author Valentin Sawadski + * @param crc + * @return crc reversed + */ + private int reflectCrcBytewise(int crc) { + int lowByte = reflectByte((byte) (crc & 0xFF)) & 0xFF; + int highByte = reflectByte((byte) ((crc & 0xFF00) >> 8)) & 0xFF; + return ((highByte << 8) + lowByte) & 0xFFFF; + } + + /** + * Swap two bytes + * + * @author Valentin Sawadski + * @param crc + * @return swapped bytes + */ + private int swapBytes(int crc) { + int lowByte = crc & 0xFF; + int highByte = crc & 0xFF00; + return ((lowByte << 8) + (highByte >> 8)) & 0xFFFF; + } + + /** + * Get the CRC reversed + * + * @return reversed crc + */ + protected int getCrcRb() { + return swapBytes(reflectCrcBytewise(crc)); + } + } + + private CRC16Java crc = new CRC16Java(); + + /** + * Clear everything when reset + */ + public void reset(int type) { + crc.reset(CCITTSeed); + } + + /** + * The registers are written + */ + public void write(int address, int value, boolean word, long cycles) { + /* + * The offset variable is used in case the peripheral changes the base + * address in some microcontroller model + */ + switch (address - offset) { + case CRC16.CRCDI: + if (word) { + int hi = (value >> 8) & 0x00ff; + int lo = (value) & 0x00ff; + crc.processRb((byte) lo); + crc.processRb((byte) hi); + } else { + crc.processRb((byte) value); + } + break; + case CRC16.CRCDIRB: + if (word) { + int hi = (value >> 8) & 0x00ff; + int lo = (value) & 0x00ff; + crc.process((byte) hi); + crc.process((byte) lo); + } else { + crc.process((byte) value); + } + break; + case CRC16.CRCINIRES: + crc.reset(value & 0xffff); + break; + } + } + + /** + * Registers are read + */ + public int read(int address, boolean word, long cycles) { + switch (address - offset) { + case CRC16.CRCINIRES: + return crc.getCRC(); + case CRC16.CRCRESR: + return crc.getCrcRb(); + } + return 0; + } + + public void interruptServiced(int vector) { + } +} diff --git a/com/tado/mspsim/core/RTC.java b/com/tado/mspsim/core/RTC.java new file mode 100644 index 0000000..ce272d2 --- /dev/null +++ b/com/tado/mspsim/core/RTC.java @@ -0,0 +1,690 @@ +package com.tado.mspsim.core; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +import se.sics.mspsim.core.IOUnit; +import se.sics.mspsim.core.MSP430Core; +import se.sics.mspsim.core.TimeEvent; +import se.sics.mspsim.core.EmulationLogger.WarningType; + +/** + * RTC module for the MSP430 + * + * TODO: alarm mode not implemented + * + * @author Víctor Ariño + */ +public class RTC extends IOUnit { + + /** + * Address and size for IO configuration + */ + public static final int OFFSET = 0x04A0; + public static final int SIZE = 0x0001A; + + /** + * Interrupt vector + */ + public static final int RTC_VECTOR = 0x005C; + + /* Peripheral registers */ + private static final int RTCCTL01 = 0x0000; + private static final int RTCCTL23 = 0x0002; + private static final int RTCPS0CTL = 0x0008; + private static final int RTCPS1CTL = 0x000A; + private static final int RTCPS = 0x000C; + private static final int RTCIV = 0x000E; + private static final int RTCTIM0 = 0x0010; + private static final int RTCTIM1 = 0x0012; + private static final int RTCDATE = 0x0014; + private static final int RTCYEAR = 0x0016; + private static final int RTCAMINHR = 0x0018; + private static final int RTCADOWDAY = 0x001A; + + /* RTCCTL01 Control Bits */ + private static final int RTCBCD = 0x8000; + private static final int RTCHOLD = 0x4000; + private static final int RTCMODE = 0x2000; + private static final int RTCRDY = 0x1000; + private static final int RTCTEVIE = 0x0040; + private static final int RTCAIE = 0x0020; + private static final int RTCRDYIE = 0x0010; + private static final int RTCTEVIFG = 0x0004; + private static final int RTCAIFG = 0x0002; + private static final int RTCRDYIFG = 0x0001; + + private static final int RTCSSEL__ACLK = 0x0000; + private static final int RTCSSEL__SMCLK = 0x0400; + private static final int RTCSSEL__RT1PS = 0x0800; + + private static final int RTCOFIE = (1 << 7); + private static final int RTCOFIFG = (1 << 3); + private static final int RTCEVIFG = (1 << 2); + + /* Possible types of RTC */ + public static enum RtcType { + TYPE_A, TYPE_D, + } + + private RtcType type = RtcType.TYPE_A; + private int rtcIntVector = RTC_VECTOR; + + /** + * RTC peripheral for the MSP430 + * + * @param cpu + * CPU core + */ + public RTC(MSP430Core cpu, int offset, RtcType r, int intVector) { + super("RTC", cpu, cpu.memory, offset); + type = r; + rtcIntVector = intVector; + DEBUG = true; + cal.setLenient(true); + } + + /** + * Clear everything when reset + */ + public void reset(int type) { + oscFaultInterruptEnable = false; + eventInterruptEnable = false; + alarmInterruptEnable = false; + readyInterruptEnable = false; + oscFaultInterruptFlag = false; + eventInterruptFlag = false; + alarmInterruptFlag = false; + readyInterruptFlag = false; + } + + /* RTCCTL0 */ + private boolean oscFaultInterruptEnable = false; + private boolean eventInterruptEnable = false; + private boolean alarmInterruptEnable = false; + private boolean readyInterruptEnable = false; + private boolean oscFaultInterruptFlag = false; + private boolean eventInterruptFlag = false; + private boolean alarmInterruptFlag = false; + private boolean readyInterruptFlag = false; + + /** + * Get the CTLO (byte) register + * + * @return + */ + private short getCTL0Reg() { + short ctl0 = 0; + ctl0 |= (oscFaultInterruptEnable ? 1 : 0) << 7; + ctl0 |= (eventInterruptEnable ? 1 : 0) << 6; + ctl0 |= (alarmInterruptEnable ? 1 : 0) << 5; + ctl0 |= (readyInterruptEnable ? 1 : 0) << 4; + ctl0 |= (oscFaultInterruptFlag ? 1 : 0) << 3; + ctl0 |= (eventInterruptFlag ? 1 : 0) << 2; + ctl0 |= (alarmInterruptFlag ? 1 : 0) << 1; + ctl0 |= (readyInterruptFlag ? 1 : 0) << 0; + return ctl0; + } + + /** + * Get the CTL1 (byte) register + * + * @return + */ + private short getCTL1Reg() { + short ctl1 = 0; + ctl1 |= (formatBCD ? 1 : 0) << 7; + ctl1 |= (rtcHold ? 1 : 0) << 6; + ctl1 |= (modeCalendar ? 1 : 0) << 5; + ctl1 |= (rtcReady ? 1 : 0) << 4; + ctl1 |= (clockSource & 0x3) << 2; + ctl1 |= (rtcEvent & 0x3); + return ctl1; + } + + /** + * Get the CTL01 (word) register + * + * @return + */ + private int getCTL01Reg() { + return getCTL1Reg() << 8 | getCTL0Reg(); + } + + /* RTCCTL1 */ + private boolean formatBCD = false; + private boolean rtcHold = true; + private boolean modeCalendar = true; + private boolean rtcReady = true; + private int clockSource = 0; + private int rtcEvent = 0; + + /** + * Handler of the 4 (byte) counter registers, for simplicity they can be + * collected in a single 32-bit variable. + */ + private long rtcCount = 0; + + /** + * Period to increment the counters + */ + private double period = osc32KHzMs; + private static final float osc32KHzMs = 0.031f; + + private int preScaler0Src = 0; + private int preScaler0Div = 0; + private boolean preScaler0Hold = false; + + private int preScaler1Src = 0; + private int preScaler1Div = 0; + private boolean preScaler1Hold = false; + + private static final int RT0SSEL = 0x4000; + private static final int RT0PSHOLD = 0x0100; + private static final int RT0PSIE = 0x0002; + private static final int RT0PSIFG = 0x0001; + private static final int RT0IP = 0x001C; + + private static final int RT1SSEL = 0xC000; + private static final int RT1PSHOLD = 0x0100; + private static final int RT1PSIE = 0x0002; + private static final int RT1PSIFG = 0x0001; + private static final int RT1IP = 0x001C; + + private int getPS0CTL() { + int ctl0 = 0; + ctl0 |= (preScaler0Src & 0x01) << 14; + ctl0 |= (preScaler0Div & 0x03) << 11; + ctl0 |= (preScaler0Hold ? 1 : 0) << 8; + return ctl0; + } + + private int getPS1CTL() { + int ctl1 = 0; + ctl1 |= (preScaler1Src & 0x03) << 14; + ctl1 |= (preScaler1Div & 0x03) << 11; + ctl1 |= (preScaler1Hold ? 1 : 0) << 8; + return ctl1; + } + + /** + * In calendar mode, use a gregorian calendar + */ + private GregorianCalendar cal = new GregorianCalendar(0, 1, 1, 0, 0, 0); + + /** + * Timer to generate the interrupts and handle the calendar + */ + private TimeEvent rtcTimer = new TimeEvent(0) { + + public void execute(long t) { + if (!rtcHold) { + updateCounters(); + reSchedule(); + } + if (getIV() > 0) { + + cpu.flagInterrupt(rtcIntVector, RTC.this, true); + } + } + + /** + * Re-Schedule the timer event + */ + private void reSchedule() { + // cpu.scheduleTimeEventMillis(this, period); + rtcInit(); + } + + /** + * Update the counters or the calendar, depending on the mode + */ + private void updateCounters() { + if (modeCalendar) { + /* In calendar mode it is updated every second */ + cal.add(Calendar.SECOND, 1); + + /* Track the changes */ + int sec = cal.get(Calendar.SECOND); + int min = cal.get(Calendar.MINUTE); + int hour = cal.get(Calendar.HOUR_OF_DAY); + + if (sec == 0) { + if (rtcEvent == 0) { // minute + generateInterrupt(); + } else if (rtcEvent == 1 && min == 0) { // hour + generateInterrupt(); + } else if (rtcEvent == 2 && hour == 0 && min == 0) { // day + generateInterrupt(); + } else if (rtcEvent == 3 && hour == 12 && min == 0) { // day + generateInterrupt(); + } + } + + /* + * Generate interrupts when time successfully increased and there's + * no other interrupt + */ + if (readyInterruptEnable && modeCalendar) { + readyInterruptFlag = true; + } + + } else { + rtcCount += 1; + long overflow = ((1L << ((rtcEvent + 1) * 8))); + overflow -= 1; + if ((rtcCount & overflow) == 0) { + generateInterrupt(); + } + if (rtcCount >= (1L << 32)) { + rtcCount = 0; + } + } + } + + /** + * Trigger a microprocessor interrupt + */ + private void generateInterrupt() { + eventInterruptFlag = true; + } + + }; + + private double getPreScalerFreq() { + double freqSrc = 0; + if (preScaler1Src > 1) { + /* From preScaler 0 */ + if (preScaler0Src == 0) { + freqSrc = cpu.getAclkFrq(); + } else { + freqSrc = cpu.getSmclkFrq(); + } + int div = (0x2 << preScaler0Div); + freqSrc /= div; + } else if (preScaler1Src == 0) { + freqSrc = cpu.getAclkFrq(); + } else { + freqSrc = cpu.getSmclkFrq(); + } + int div = (0x2 << preScaler1Div); + freqSrc /= div; + return freqSrc; + } + + /** + * Initialize the RTC clock. This is called when the hold bit is released + */ + private void rtcInit() { + double freqSrc = 1f; + if (modeCalendar) { + /* In this mode we can set the counter to 1 second */ + freqSrc = 1f; // 1Hz = ~1s + } else { + if (type == RtcType.TYPE_A) { + /* For the RTC_A the sources are aclk, smclk, rt1ps */ + if (clockSource == 0) { + freqSrc = cpu.getAclkFrq(); + } else if (clockSource == 1) { + freqSrc = cpu.getSmclkFrq(); + } else { + freqSrc = getPreScalerFreq(); + } + } else { + /* For the RTC_D the sources are 32-khz, rt1ps */ + if (clockSource <= 1) { + freqSrc = 32000; + } else { + freqSrc = getPreScalerFreq(); + } + } + } + period = 1000f / freqSrc; + cpu.scheduleTimeEventMillis(rtcTimer, period); + } + + /** + * Reset the calendar to 01.01.0000 + */ + private void resetCalendar() { + cal = new GregorianCalendar(0, 1, 1, 0, 0, 0); + } + + /** + * Get the interrupt vector + * + * @return + */ + private int getIV() { + int iv = 0; + iv |= (readyInterruptFlag ? 1 : 0) << 1; + iv |= (eventInterruptFlag ? 1 : 0) << 2; + iv |= (alarmInterruptFlag ? 1 : 0) << 3; + return iv; + } + + /** + * Given a calendar field, get the BCD or hex representation + * + * @param calField + * Calendar.XXXX field + * @return the formated field + */ + private int formatField(int calField) { + int f = cal.get(calField); + /* Day of week is 0-6 in uC and 1-7 in Java */ + if (calField == Calendar.DAY_OF_WEEK) { + f -= 1; + } + int res = 0; + int base = 0; + if (formatBCD) { + base = 10; + } else { + base = 16; + } + for (int i = 0; f > 0; i++) { + int v = f % base; + f = f / base; + res |= (v << i * 4); + } + return res; + } + + /** + * Parses a calendar input + * + * This processes the registers written in BCD or hex format + * + * @param calField + * the calendar field to parse + * @param value + * the input value + */ + private void parseCalReg(int calField, int value) { + int res = 0; + int factor = 1; + int base = 0; + if (formatBCD) { + base = 10; + } else { + base = 16; + } + + /* Day of week is 0-6 in uC and 1-7 in Java */ + if (calField == Calendar.DAY_OF_WEEK) { + value += 1; + } + + while (value > 0) { + int v = value & 0x0f; + res += (v * factor); + value >>= 4; + factor *= base; + } + cal.set(calField, res); + cal.get(calField); // Until get is not done the fields are not set + } + + /** + * Clear the highest priority interrupt flag + */ + private void clearHighestInterrupt() { + if (readyInterruptFlag) { + readyInterruptFlag = false; + } else if (eventInterruptFlag) { + eventInterruptFlag = false; + } else if (alarmInterruptFlag) { + alarmInterruptFlag = false; + } + + if (getIV() > 0) { + cpu.flagInterrupt(rtcIntVector, this, true); + } + } + + /** + * The registers are written + */ + public void write(int address, int value, boolean word, long cycles) { + /* + * XXX: this assumes always word access + */ + if (!word) { + logw(WarningType.MISALIGNED_WRITE, "byte access not implemented"); + } + + int lo = (value) & 0xff; // low byte + int hi = (value >> 8) & 0xff; // high byte + + switch (address - offset) { + case RTCCTL01: + oscFaultInterruptEnable = ((value & RTCOFIE) == RTCOFIE); + eventInterruptEnable = ((value & RTCTEVIE) == RTCTEVIE); + alarmInterruptEnable = ((value & RTCAIE) == RTCAIE); + readyInterruptEnable = ((value & RTCRDYIE) == RTCRDYIE); + oscFaultInterruptFlag = ((value & RTCOFIFG) == RTCOFIFG); + eventInterruptFlag = ((value & RTCEVIFG) == RTCEVIFG); + alarmInterruptFlag = ((value & RTCAIFG) == RTCAIFG); + readyInterruptFlag = ((value & RTCRDYIFG) == RTCRDYIFG); + + rtcHold = ((value & RTCHOLD) == RTCHOLD); + rtcReady = ((value & RTCRDY) == RTCRDY); + clockSource = (value & 0x0C00) >> 10; + rtcEvent = (value & 0x0300) >> 8; + + /* + * Either the BCD has changed and is in mode calendar or the mode + * calendar has been enabled + */ + if ((modeCalendar && formatBCD != ((value & RTCBCD) == RTCBCD)) + || (modeCalendar == false && (value & RTCMODE) == RTCMODE)) { + /* + * Changing this bit clears seconds, minutes, hours, day of week, + * and year to 0 and sets day of month and month to 1. The real-time + * clock registers must be set by software afterwards. + */ + resetCalendar(); + } + modeCalendar = ((value & RTCMODE) == RTCMODE); + formatBCD = ((value & RTCBCD) == RTCBCD); + + /* Initialize the RTC or stop it */ + if (!rtcHold) { + rtcInit(); + } + + break; + + case RTCPS0CTL: + preScaler0Src = ((value & 0x4000) >> 14); + preScaler0Div = ((value & 0x3800) >> 11); + preScaler0Hold = ((value & RT0PSHOLD) == RT0PSHOLD); + + if (((value & (RT0PSIE | RT0PSIFG | RT0IP)) > 0)) { + logNotImplemented("prescaling interrupts"); + } + break; + + case RTCPS1CTL: + preScaler1Src = ((value & 0xC000) >> 14); + preScaler1Div = ((value & 0x3800) >> 11); + preScaler1Hold = ((value & RT1PSHOLD) == RT1PSHOLD); + + if (((value & (RT1PSIE | RT1PSIFG | RT1IP)) > 0)) { + logNotImplemented("prescaling interrupts"); + } + break; + + case RTCCTL23: + logNotImplemented("Calibration"); + break; + + case RTCPS: + logNotImplemented("prescaling counter"); + break; + + case RTCTIM0: // RTCNT12 + if (modeCalendar) { + parseCalReg(Calendar.SECOND, lo); + parseCalReg(Calendar.MINUTE, hi); + } else { + rtcCount &= 0xffff0000; + rtcCount |= value; + } + break; + + case RTCTIM1: // RTCNT34 + if (modeCalendar) { + parseCalReg(Calendar.HOUR_OF_DAY, lo); + parseCalReg(Calendar.DAY_OF_WEEK, hi); + } else { + rtcCount &= 0x0000ffff; + rtcCount |= (long) (value << 16); + } + break; + + case RTCDATE: + if (modeCalendar) { + parseCalReg(Calendar.DAY_OF_MONTH, lo); + parseCalReg(Calendar.MONTH, hi); + } + break; + + case RTCYEAR: + if (modeCalendar) { + parseCalReg(Calendar.YEAR, value); + } + break; + + case RTCIV: + /* + * Any access, read or write, of the RTCIV register automatically + * resets the highest-pending interrupt flag. If another interrupt flag + * is set, another interrupt is immediately generated after servicing + * the initial interrupt. In addition, all flags can be cleared via + * software. + */ + clearHighestInterrupt(); + + default: + logNotImplemented("register: " + address); + } + } + + /** + * Registers are read + */ + public int read(int address, boolean word, long cycles) { + + if (!word) { + logw(WarningType.MISALIGNED_READ, "byte access not implemented"); + } + + switch (address - offset) { + case RTCCTL01: + if (word) { + return getCTL01Reg(); + } else { + return getCTL0Reg(); + } + + case RTCCTL23: + logNotImplemented("calibration"); + break; + + case RTCPS: + logNotImplemented("prescaling coutner"); + break; + + case RTCPS0CTL: + return getPS0CTL(); + + case RTCPS1CTL: + return getPS1CTL(); + + case RTCIV: + /* + * Any access, read or write, of the RTCIV register automatically + * resets the highest-pending interrupt flag. If another interrupt flag + * is set, another interrupt is immediately generated after servicing + * the initial interrupt. In addition, all flags can be cleared via + * software. + */ + int tmp = getIV(); + clearHighestInterrupt(); + return tmp; + + case RTCTIM0: // RTCNT12 + if (modeCalendar) { + return formatField(Calendar.MINUTE) << 8 + | formatField(Calendar.SECOND); + } else { + return (int) (rtcCount & 0xffff); + } + + case RTCTIM1: // RTCNT34 + if (modeCalendar) { + return formatField(Calendar.DAY_OF_WEEK) << 8 + | formatField(Calendar.HOUR_OF_DAY); + } else { + return (int) ((rtcCount >> 16) & 0xffff); + } + + case RTCDATE: + if (modeCalendar) { + return formatField(Calendar.MONTH) << 8 + | formatField(Calendar.DAY_OF_MONTH); + } + break; + + case RTCYEAR: + if (modeCalendar) { + return formatField(Calendar.YEAR); + } + break; + + default: + logNotImplemented("register: " + address); + } + return 0; + } + + public void interruptServiced(int vector) { + /* + * NOTE this function is called BEFORE the interrupt is actually + * serviced!!! + */ + if (vector == rtcIntVector) { + cpu.flagInterrupt(rtcIntVector, this, false); + } + } + + /* + * The inherited log function is not working for whatever reason. A quick + * redefinition helps a lot while developing the module + */ + @Override + protected void log(String msg) { + if (DEBUG) { + System.out.println(msg); + } + } + + /** + * Log using printf format + * + * @param format + * @param arguments + */ + protected void log(final String format, final Object... arguments) { + if (DEBUG) { + System.out.printf(format, arguments); + } + } + + private void logNotImplemented(String feature) { + logw(WarningType.EMULATION_ERROR, feature + " is not implemented"); + } + +}