Browse Source

adding something after merging

Klaas, Wilfried 4 years ago
parent
commit
d3479c45a7
32 changed files with 3117 additions and 0 deletions
  1. 39 0
      .classpath
  2. 23 0
      .project
  3. 72 0
      LICENSE
  4. 19 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorInput.java
  5. 33 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorInternals.java
  6. 24 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorOutput.java
  7. 36 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommand.java
  8. 40 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandData.java
  9. 100 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandDataImpl.java
  10. 78 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandImpl.java
  11. 88 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSEmulator.java
  12. 14 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/TOutputCallback.java
  13. 141 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/AbstractEmulator.java
  14. 682 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulator.java
  15. 111 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInput.java
  16. 107 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInternals.java
  17. 53 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorOutput.java
  18. 35 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataJumpDown.java
  19. 30 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataNOP.java
  20. 72 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataWait.java
  21. 8 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/package-info.java
  22. 26 0
      SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/package-info.java
  23. 548 0
      SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekCommands.java
  24. 355 0
      SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekEmulator.java
  25. 55 0
      SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekProgram.java
  26. 48 0
      SimpleServo.tps
  27. 37 0
      examples/Blink.tps
  28. 6 0
      examples/Blink2.tps
  29. 32 0
      examples/BlinkKomment.tps
  30. 20 0
      examples/Blink_LOOP.tps
  31. 6 0
      examples/includes/macro_blink.tps
  32. 179 0
      pom.xml

+ 39 - 0
.classpath

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>

+ 23 - 0
.project

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>SPSEmulator</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

+ 72 - 0
LICENSE

@@ -0,0 +1,72 @@
+Apache License 
+Version 2.0, January 2004 
+http://www.apache.org/licenses/
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.

+ 19 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorInput.java

@@ -0,0 +1,19 @@
+package de.mcs.tools.sps.emulator;
+
+public interface EmulatorInput {
+
+  byte getInput();
+
+  void setInput(byte data);
+
+  void reset();
+
+  void setFeature(String name, byte value);
+
+  byte getFeature(String name);
+
+  void setFeature(String name, boolean value);
+
+  boolean getFeatureAsBool(String name);
+
+}

+ 33 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorInternals.java

@@ -0,0 +1,33 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+/**
+ * This interface is for getting the emualtor internal
+ * 
+ * @author w.klaas
+ *
+ */
+public interface EmulatorInternals {
+
+  void reset();
+
+  int getAddress();
+
+  void incAddress(int i);
+
+  void setDelay(long l);
+
+  long getDelay();
+
+  void setRegister(String register, byte data);
+
+  byte getRegister(String register);
+
+  void setAddress(int i);
+
+  void pushRtrAddress();
+
+  void popRtrAddress();
+}

+ 24 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/EmulatorOutput.java

@@ -0,0 +1,24 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+/**
+ * this is the interface of all Emulatoroutputs
+ * 
+ * @author w.klaas
+ *
+ */
+public interface EmulatorOutput {
+
+  void reset();
+
+  byte getOutput();
+
+  byte getFeature(String name);
+
+  void setFeature(String name, byte register);
+
+  void setOutput(byte data);
+
+}

+ 36 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommand.java

@@ -0,0 +1,36 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+import java.util.List;
+
+/**
+ * a single Command for the emulator
+ * 
+ * @author w.klaas
+ *
+ */
+public interface SPSCommand {
+
+  /**
+   * @return the name of this command
+   */
+  String getName();
+
+  /**
+   * @return the short Mnemonic
+   */
+  String getMnemonic();
+
+  /**
+   * @return the byte code for this command
+   */
+  byte getCommandByte();
+
+  /**
+   * @return a lsit of all supported datas of this command
+   */
+  List<SPSCommandData> getCommandDatas();
+
+}

+ 40 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandData.java

@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+/**
+ * a single command data for the emulator
+ * 
+ * @author w.klaas
+ *
+ */
+public interface SPSCommandData {
+
+  /**
+   * 
+   * @return the command this data belongs to
+   */
+  SPSCommand getCommand();
+
+  /**
+   * @return the name of this command
+   */
+  String getName();
+
+  /**
+   * @return the short Mnemonic
+   */
+  String getMnemonic();
+
+  /**
+   * @return the byte code for this command and data
+   */
+  byte getCommandByte();
+
+  /**
+   * here the work should be done
+   */
+  EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output);
+
+}

+ 100 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandDataImpl.java

@@ -0,0 +1,100 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+/**
+ * @author w.klaas
+ *
+ */
+public abstract class SPSCommandDataImpl implements SPSCommandData {
+
+  String name;
+  String mnemonic;
+  byte cmdByte;
+  SPSCommand command;
+
+  public SPSCommandDataImpl() {
+  }
+
+  @Override
+  public SPSCommand getCommand() {
+    return command;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getMnemonic() {
+    return mnemonic;
+  }
+
+  /**
+   * @return the cmdByte
+   */
+  @Override
+  public byte getCommandByte() {
+    return cmdByte;
+  }
+
+  /**
+   * @param cmdByte
+   *          the cmdByte to set
+   * @return this instance
+   */
+  public SPSCommandDataImpl setCommandByte(byte cmdByte) {
+    this.cmdByte = cmdByte;
+    return this;
+  }
+
+  /**
+   * @param name
+   *          the name to set
+   * @return this instance
+   */
+  public SPSCommandDataImpl setName(String name) {
+    this.name = name;
+    return this;
+  }
+
+  /**
+   * @param mnemonic
+   *          the mnemonic to set
+   * @return this instance
+   */
+  public SPSCommandDataImpl setMnemonic(String mnemonic) {
+    this.mnemonic = mnemonic;
+    return this;
+  }
+
+  /**
+   * @param command
+   *          the command to set
+   * @return this instance
+   */
+  public SPSCommandDataImpl setCommand(SPSCommand command) {
+    this.command = command;
+    return this;
+  }
+
+  /**
+   * @param command
+   *          the command to set
+   * @return this instance
+   */
+  public byte getData() {
+    return (byte) (getCommandByte() & (byte) 0x0F);
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%s (%s): %.2X", getMnemonic(), getName(), getCommandByte());
+  }
+
+  @Override
+  public abstract EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output);
+
+}

+ 78 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSCommandImpl.java

@@ -0,0 +1,78 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class SPSCommandImpl implements SPSCommand {
+
+  String name;
+  String mnemonic;
+  byte cmdByte;
+  List<SPSCommandData> datas = new ArrayList<>();
+
+  /**
+   * 
+   */
+  public SPSCommandImpl() {
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getMnemonic() {
+    return mnemonic;
+  }
+
+  @Override
+  public byte getCommandByte() {
+    return cmdByte;
+  }
+
+  @Override
+  public List<SPSCommandData> getCommandDatas() {
+    return datas;
+  }
+
+  /**
+   * @param cmdByte
+   *          the cmdByte to set
+   */
+  public SPSCommandImpl setCommandByte(byte cmdByte) {
+    this.cmdByte = cmdByte;
+    return this;
+  }
+
+  /**
+   * @param name
+   *          the name to set
+   */
+  public SPSCommandImpl setName(String name) {
+    this.name = name;
+    return this;
+  }
+
+  /**
+   * @param mnemonic
+   *          the mnemonic to set
+   * @return
+   */
+  public SPSCommandImpl setMnemonic(String mnemonic) {
+    this.mnemonic = mnemonic;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%s (%s): %.2X", getMnemonic(), getName(), getCommandByte());
+  }
+}

+ 88 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/SPSEmulator.java

@@ -0,0 +1,88 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+import java.util.List;
+
+import de.mcs.tools.sps.emulator.exceptions.WrongProgramSizeException;
+
+/**
+ * @author w.klaas
+ *
+ */
+public interface SPSEmulator {
+
+  /**
+   * returning all commands supported by this emulator
+   * 
+   * @return lsit of all supported commands
+   */
+  List<SPSCommand> getCommands();
+
+  /**
+   * @return the size of the internal program area
+   */
+  int getProgramSize();
+
+  /**
+   * loading a program into the emulator
+   * 
+   * @param program
+   *          to load
+   * @throws WrongProgramSizeException
+   */
+  void loadProgram(byte[] program) throws WrongProgramSizeException;
+
+  /**
+   * starting the program with or without debug mode
+   * 
+   * @param debug
+   *          if debug = <code>true</code> than the program will stop on every step, otherwise only the callbacks will
+   *          be called
+   */
+  void startProgram(boolean debug);
+
+  /**
+   * adding a callback for the outputs of the emulator
+   * 
+   * @param callback
+   */
+  void addCallback(TOutputCallback callback);
+
+  /**
+   * do the next step of the program
+   */
+  void nextStep();
+
+  /**
+   * sps emulator is active
+   * 
+   * @return
+   */
+  boolean isActive();
+
+  /**
+   * stop the actual process
+   */
+  void stop();
+
+  /**
+   * setting the input for the emulator
+   * 
+   * @param input
+   */
+  void setEmulatorInput(EmulatorInput input);
+
+  EmulatorInput getEmulatorInput();
+
+  /**
+   * 
+   * @return the actual emulator internals
+   */
+  EmulatorInternals getEmulatorInternals();
+
+  EmulatorOutput getEmulatorOutput();
+
+  SPSCommandData getCommandData(byte command);
+}

+ 14 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/TOutputCallback.java

@@ -0,0 +1,14 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator;
+
+/**
+ * Interface for the callbacks on the emulator
+ * 
+ * @author w.klaas
+ *
+ */
+public interface TOutputCallback {
+  void onOutput(EmulatorOutput output);
+}

+ 141 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/AbstractEmulator.java

@@ -0,0 +1,141 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator;
+
+import java.util.List;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommand;
+import de.mcs.tools.sps.emulator.SPSCommandData;
+import de.mcs.tools.sps.emulator.SPSEmulator;
+import de.mcs.tools.sps.emulator.TOutputCallback;
+import de.mcs.tools.sps.emulator.exceptions.WrongProgramSizeException;
+
+/**
+ * @author w.klaas
+ *
+ */
+public abstract class AbstractEmulator implements SPSEmulator {
+
+  protected byte[] programMemory;
+  private boolean debug;
+  private boolean active;
+  private TOutputCallback outputCallback;
+  private EmulatorInput emulatorInput;
+  private EmulatorOutput emulatorOutput;
+  private EmulatorInternals emulatorInternals;
+
+  @Override
+  public abstract List<SPSCommand> getCommands();
+
+  @Override
+  public abstract int getProgramSize();
+
+  @Override
+  public void loadProgram(byte[] program) throws WrongProgramSizeException {
+    if (program.length > getProgramSize()) {
+      throw new WrongProgramSizeException(
+          String.format("the program is to long. Only %d bytes are permitted", getProgramSize()));
+    }
+    this.programMemory = program;
+  }
+
+  @Override
+  public void startProgram(boolean debug) {
+    this.debug = debug;
+    if (emulatorInput == null) {
+      emulatorInput = createEmulatorInput();
+    }
+    emulatorOutput = createEmulatorOutput();
+    emulatorInternals = createEmulatorInternals();
+    reset();
+    active = true;
+    if (outputCallback != null) {
+      outputCallback.onOutput(getEmulatorOutput());
+    }
+    if (!debug) {
+      while (active) {
+        callNextCommand();
+      }
+    }
+  }
+
+  protected abstract void callNextCommand();
+
+  protected abstract EmulatorInput createEmulatorInput();
+
+  protected abstract EmulatorOutput createEmulatorOutput();
+
+  protected abstract EmulatorInternals createEmulatorInternals();
+
+  @Override
+  public SPSCommandData getCommandData(byte command) {
+    SPSCommandData commandData = null;
+    byte commandByte = (byte) (command & (byte) 0xF0);
+
+    List<SPSCommand> commands = getCommands();
+    for (SPSCommand c : commands) {
+      if (c.getCommandByte() == commandByte) {
+        for (SPSCommandData cd : c.getCommandDatas()) {
+          if (cd.getCommandByte() == command) {
+            commandData = cd;
+          }
+        }
+      }
+    }
+    return commandData;
+  }
+
+  protected void runCommand(SPSCommandData commandData) {
+    EmulatorOutput emulatorOutput = commandData.doWork(getEmulatorInput(), getEmulatorInternals(), getEmulatorOutput());
+    if (outputCallback != null) {
+      outputCallback.onOutput(emulatorOutput);
+    }
+  }
+
+  protected abstract void reset();
+
+  @Override
+  public void addCallback(TOutputCallback callback) {
+    this.outputCallback = callback;
+  }
+
+  @Override
+  public void nextStep() {
+    callNextCommand();
+  }
+
+  @Override
+  public boolean isActive() {
+    return active;
+  }
+
+  @Override
+  public void stop() {
+    active = false;
+  }
+
+  @Override
+  public void setEmulatorInput(EmulatorInput input) {
+    this.emulatorInput = input;
+  };
+
+  @Override
+  public EmulatorInput getEmulatorInput() {
+    return emulatorInput;
+  };
+
+  @Override
+  public EmulatorInternals getEmulatorInternals() {
+    return emulatorInternals;
+  };
+
+  @Override
+  public EmulatorOutput getEmulatorOutput() {
+    return emulatorOutput;
+  };
+
+}

+ 682 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulator.java

@@ -0,0 +1,682 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommand;
+import de.mcs.tools.sps.emulator.SPSCommandData;
+import de.mcs.tools.sps.emulator.SPSCommandDataImpl;
+import de.mcs.tools.sps.emulator.SPSCommandImpl;
+import de.mcs.tools.sps.emulator.SPSEmulator;
+import de.mcs.tools.sps.emulator.emulator.AbstractEmulator;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class HoltekEmulator extends AbstractEmulator implements SPSEmulator {
+  private List<SPSCommand> commands = new ArrayList<>();
+  {
+    commands.add(SPS_NOP);
+    commands.add(SPS_PORT);
+    commands.add(SPS_WAIT);
+    commands.add(SPS_JUMP_DOWN);
+    commands.add(SPS_A_EQUALS);
+    commands.add(SPS_EQUALS_A);
+    commands.add(SPS_A_INPUT);
+    commands.add(SPS_A_CALCULATE);
+    commands.add(SPS_PAGE);
+    commands.add(SPS_JUMP);
+    commands.add(SPS_C_LOOP);
+    commands.add(SPS_D_LOOP);
+    commands.add(SPS_SKIP_IF);
+    commands.add(SPS_CALL);
+    commands.add(SPS_RETURN);
+  }
+
+  private static SPSCommand SPS_NOP = new SPSCommandImpl().setName("no operation").setMnemonic("NOP")
+      .setCommandByte((byte) 0x00);
+  static {
+    SPS_NOP.getCommandDatas().add(new SPSCommandDataNOP().setName("nop").setMnemonic("NOP")
+        .setCommandByte((byte) (SPS_NOP.getCommandByte())).setCommand(SPS_NOP));
+  }
+
+  private static SPSCommand SPS_PORT = new SPSCommandImpl().setName("Port Output").setMnemonic("PORT")
+      .setCommandByte((byte) 0x10);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_PORT.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte data = getData();
+          HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+          emulatorOutput.setOutput(data);
+          internals.incAddress(1);
+          return emulatorOutput;
+        }
+      }.setName("Port " + Integer.toString(i)).setMnemonic("PO" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_PORT.getCommandByte() + i)).setCommand(SPS_PORT));
+    }
+  }
+
+  private static SPSCommand SPS_WAIT = new SPSCommandImpl().setName("WAIT").setMnemonic("WAIT")
+      .setCommandByte((byte) 0x20);
+  static {
+    List<SPSCommandData> datas = SPS_WAIT.getCommandDatas();
+    for (int i = 0; i < 16; i++) {
+      datas.add(
+          new SPSCommandDataWait().setName(SPSCommandDataWait.getName(i)).setMnemonic(SPSCommandDataWait.getMnemonic(i))
+              .setCommandByte((byte) (SPS_WAIT.getCommandByte() + i)).setCommand(SPS_WAIT));
+    }
+  }
+
+  private static SPSCommand SPS_JUMP_DOWN = new SPSCommandImpl().setName("jump back").setMnemonic("JMP-")
+      .setCommandByte((byte) 0x30);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_JUMP_DOWN.getCommandDatas()
+          .add(new SPSCommandDataJumpDown().setName("Jump -" + Integer.toString(i))
+              .setMnemonic("JN" + Integer.toString(i)).setCommandByte((byte) (SPS_JUMP_DOWN.getCommandByte() + i))
+              .setCommand(SPS_JUMP_DOWN));
+    }
+  }
+
+  private static SPSCommand SPS_A_EQUALS = new SPSCommandImpl().setName("Set A").setMnemonic("A=")
+      .setCommandByte((byte) 0x40);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_A_EQUALS.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte data = getData();
+          internals.setRegister("A", data);
+          internals.incAddress(1);
+          return output;
+        }
+      }.setName("A= " + Integer.toString(i)).setMnemonic(String.format("A%02d", i))
+          .setCommandByte((byte) (SPS_A_EQUALS.getCommandByte() + i)).setCommand(SPS_A_EQUALS));
+    }
+  }
+
+  private static SPSCommand SPS_EQUALS_A = new SPSCommandImpl().setName("=A").setMnemonic("=A")
+      .setCommandByte((byte) 0x50);
+  static {
+    List<SPSCommandData> datas = SPS_EQUALS_A.getCommandDatas();
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("B", internals.getRegister("A"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("B=A").setMnemonic("BEA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x01))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("C", internals.getRegister("A"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("C=A").setMnemonic("CEA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x02))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("D", internals.getRegister("A"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("D=A").setMnemonic("DEA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x03))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        emulatorOutput.setOutput(internals.getRegister("A"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("Output=A").setMnemonic("OEA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x04))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        byte dataNew = internals.getRegister("A");
+        byte dataOld = emulatorOutput.getOutput();
+        byte data = (byte) ((dataOld & 0b00001110) + (dataNew & 0x01));
+        emulatorOutput.setOutput(data);
+        internals.incAddress(1);
+        return emulatorOutput;
+      }
+    }.setName("Output.1=A").setMnemonic("O1EA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x05))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        byte dataNew = internals.getRegister("A");
+        byte dataOld = emulatorOutput.getOutput();
+        byte data = (byte) ((dataOld & 0b00001101) + ((dataNew & 0x01) << 1));
+        emulatorOutput.setOutput(data);
+        internals.incAddress(1);
+        return emulatorOutput;
+      }
+    }.setName("Output.2=A").setMnemonic("O2EA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x06))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        byte dataNew = internals.getRegister("A");
+        byte dataOld = emulatorOutput.getOutput();
+        byte data = (byte) ((dataOld & 0b00001011) + ((dataNew & 0x01) << 2));
+        emulatorOutput.setOutput(data);
+        internals.incAddress(1);
+        return emulatorOutput;
+      }
+    }.setName("Output.3=A").setMnemonic("O3EA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x07))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        byte dataNew = internals.getRegister("A");
+        byte dataOld = emulatorOutput.getOutput();
+        byte data = (byte) ((dataOld & 0b00000111) + ((dataNew & 0x01) << 3));
+        emulatorOutput.setOutput(data);
+        internals.incAddress(1);
+        return emulatorOutput;
+      }
+    }.setName("Output.4=A").setMnemonic("O4EA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x08))
+        .setCommand(SPS_EQUALS_A));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) output;
+        emulatorOutput.setFeature("PWM", internals.getRegister("A"));
+        internals.incAddress(1);
+        return emulatorOutput;
+      }
+    }.setName("PWM=A").setMnemonic("PEA").setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x09))
+        .setCommand(SPS_EQUALS_A));
+  }
+
+  private static SPSCommand SPS_A_INPUT = new SPSCommandImpl().setName("A=(some input)").setMnemonic("AEI")
+      .setCommandByte((byte) 0x60);
+  static {
+    List<SPSCommandData> datas = SPS_A_INPUT.getCommandDatas();
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", internals.getRegister("B"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=B").setMnemonic("AEB").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x01))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", internals.getRegister("C"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=C").setMnemonic("AEC").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x02))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", internals.getRegister("D"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=D").setMnemonic("AED").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x03))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", input.getInput());
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=Input").setMnemonic("AEI").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x04))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        boolean value = (input.getInput() & 0x01) > 0;
+        internals.setRegister("A", (byte) (value ? 1 : 0));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=Input.1").setMnemonic("AEI1").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x05))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        boolean value = (input.getInput() & 0x02) > 0;
+        internals.setRegister("A", (byte) (value ? 1 : 0));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=Input.2").setMnemonic("AEI2").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x06))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        boolean value = (input.getInput() & 0x04) > 0;
+        internals.setRegister("A", (byte) (value ? 1 : 0));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=Input.3").setMnemonic("AEI3").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x07))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        boolean value = (input.getInput() & 0x08) > 0;
+        internals.setRegister("A", (byte) (value ? 1 : 0));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=Input.4").setMnemonic("AEI4").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x08))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", input.getFeature("AD1"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=AD.1").setMnemonic("AEA1").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x09))
+        .setCommand(SPS_A_INPUT));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", input.getFeature("AD2"));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=AD.2").setMnemonic("AEA2").setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x0A))
+        .setCommand(SPS_A_INPUT));
+  }
+
+  private static SPSCommand SPS_A_CALCULATE = new SPSCommandImpl().setName("Calculate with A").setMnemonic("CALC")
+      .setCommandByte((byte) 0x70);
+  static {
+    List<SPSCommandData> datas = SPS_A_CALCULATE.getCommandDatas();
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") + 1));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A+1").setMnemonic("INCA").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x01))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") - 1));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A-1").setMnemonic("DECA").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x02))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") + internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A+B").setMnemonic("ADDB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x03))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") - internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A-B").setMnemonic("SUBB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x04))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") * internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A*B").setMnemonic("MULB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x05))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") / internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A/B").setMnemonic("DIVB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x06))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") & internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A AND B").setMnemonic("ANDB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x07))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") | internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A OR B").setMnemonic("ORB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x08))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") ^ internals.getRegister("B")));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=A XOR B").setMnemonic("XORB").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x09))
+        .setCommand(SPS_A_CALCULATE));
+    datas.add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.setRegister("A", (byte) (internals.getRegister("A") ^ 0xFF));
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=NOT A").setMnemonic("NOTA").setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x0A))
+        .setCommand(SPS_A_CALCULATE));
+  }
+
+  private static SPSCommand SPS_PAGE = new SPSCommandImpl().setName("Page").setMnemonic("PG")
+      .setCommandByte((byte) 0x80);
+  static {
+    for (int i = 0; i < 8; i++) {
+      SPS_PAGE.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte data = getData();
+          if (data <= 0x07) {
+            internals.setRegister(HoltekEmulatorInternals.REGISTER_PAGE, data);
+          }
+          internals.incAddress(1);
+          return output;
+        }
+      }.setName("Page " + Integer.toString(i)).setMnemonic("PG" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_PAGE.getCommandByte() + i)).setCommand(SPS_PAGE));
+    }
+  }
+
+  private static SPSCommand SPS_JUMP = new SPSCommandImpl().setName("Jump").setMnemonic("JMP")
+      .setCommandByte((byte) 0x90);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_JUMP.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          ((HoltekEmulatorInternals) internals).address = getData()
+              + (internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE) * 16);
+          return output;
+        }
+      }.setName("Jump " + Integer.toString(i)).setMnemonic("JMP" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_JUMP.getCommandByte() + i)).setCommand(SPS_JUMP));
+    }
+  }
+
+  private static SPSCommand SPS_C_LOOP = new SPSCommandImpl().setName("C* Loop").setMnemonic("CLOP")
+      .setCommandByte((byte) 0xA0);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_C_LOOP.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte c = internals.getRegister(HoltekEmulatorInternals.REGISTER_C);
+          if (c > 0) {
+            c -= 1;
+            c = (byte) (c & 0x0F);
+            internals.setRegister(HoltekEmulatorInternals.REGISTER_C, c);
+            ((HoltekEmulatorInternals) internals).address = getData()
+                + (internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE) * 16);
+          } else {
+            internals.incAddress(1);
+          }
+          return output;
+        }
+      }.setName("C* Loop " + Integer.toString(i)).setMnemonic("CLOP" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_C_LOOP.getCommandByte() + i)).setCommand(SPS_C_LOOP));
+    }
+  }
+
+  private static SPSCommand SPS_D_LOOP = new SPSCommandImpl().setName("D* Loop").setMnemonic("DLOP")
+      .setCommandByte((byte) 0xB0);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_D_LOOP.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte d = internals.getRegister(HoltekEmulatorInternals.REGISTER_D);
+          if (d > 0) {
+            d -= 1;
+            d = (byte) (d & 0x0F);
+            internals.setRegister(HoltekEmulatorInternals.REGISTER_D, d);
+            ((HoltekEmulatorInternals) internals).address = getData()
+                + (internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE) * 16);
+          } else {
+            internals.incAddress(1);
+          }
+          return output;
+        }
+      }.setName("D* Loop " + Integer.toString(i)).setMnemonic("DLOP" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_D_LOOP.getCommandByte() + i)).setCommand(SPS_D_LOOP));
+    }
+  }
+
+  private static SPSCommand SPS_SKIP_IF = new SPSCommandImpl().setName("Skip if").setMnemonic("SKIP")
+      .setCommandByte((byte) 0xC0);
+  static {
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        byte a = internals.getRegister(HoltekEmulatorInternals.REGISTER_A);
+        byte b = internals.getRegister(HoltekEmulatorInternals.REGISTER_B);
+        if (a > b) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A>B").setMnemonic("AGRB").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x01))
+        .setCommand(SPS_SKIP_IF));
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        byte a = internals.getRegister(HoltekEmulatorInternals.REGISTER_A);
+        byte b = internals.getRegister(HoltekEmulatorInternals.REGISTER_B);
+        if (a < b) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A<B").setMnemonic("ASMB").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x02))
+        .setCommand(SPS_SKIP_IF));
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        byte a = internals.getRegister(HoltekEmulatorInternals.REGISTER_A);
+        byte b = internals.getRegister(HoltekEmulatorInternals.REGISTER_B);
+        if (a == b) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("A=B").setMnemonic("AEQB").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x03))
+        .setCommand(SPS_SKIP_IF));
+
+    for (int i = 0; i < 4; i++) {
+      SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte in = input.getInput();
+          byte data = (byte) (getData() - 0x04);
+          if ((in & (0x01 << data)) > 0) {
+            internals.incAddress(1);
+          }
+          internals.incAddress(1);
+          return output;
+        }
+      }.setName(String.format("In.%d=1", i + 1)).setMnemonic(String.format("IN%d1", i + 1))
+          .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x04 + i)).setCommand(SPS_SKIP_IF));
+    }
+
+    for (int i = 0; i < 4; i++) {
+      SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          byte in = input.getInput();
+          byte data = (byte) (getData() - 0x08);
+          if ((in & (0x01 << data)) == 0) {
+            internals.incAddress(1);
+          }
+          internals.incAddress(1);
+          return output;
+        }
+      }.setName(String.format("In.%d=0", i + 1)).setMnemonic(String.format("IN%d0", i + 1))
+          .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x08 + i)).setCommand(SPS_SKIP_IF));
+    }
+
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        if (!input.getFeatureAsBool(HoltekEmulatorInput.S1)) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("S1=0").setMnemonic("S10").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0C))
+        .setCommand(SPS_SKIP_IF));
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        if (!input.getFeatureAsBool(HoltekEmulatorInput.S2)) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("S2=0").setMnemonic("S20").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0D))
+        .setCommand(SPS_SKIP_IF));
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        if (input.getFeatureAsBool(HoltekEmulatorInput.S1)) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("S1=1").setMnemonic("S11").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0E))
+        .setCommand(SPS_SKIP_IF));
+    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        if (input.getFeatureAsBool(HoltekEmulatorInput.S2)) {
+          internals.incAddress(1);
+        }
+        internals.incAddress(1);
+        return output;
+      }
+    }.setName("S2=1").setMnemonic("S21").setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0F))
+        .setCommand(SPS_SKIP_IF));
+  };
+
+  private static SPSCommand SPS_CALL = new SPSCommandImpl().setName("Call").setMnemonic("CALL")
+      .setCommandByte((byte) 0xD0);
+  static {
+    for (int i = 0; i < 16; i++) {
+      SPS_CALL.getCommandDatas().add(new SPSCommandDataImpl() {
+        @Override
+        public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+          internals.pushRtrAddress();
+          ((HoltekEmulatorInternals) internals).address = getData()
+              + (internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE) * 16);
+          return output;
+        }
+      }.setName("Call " + Integer.toString(i)).setMnemonic("CALL" + Integer.toString(i))
+          .setCommandByte((byte) (SPS_CALL.getCommandByte() + i)).setCommand(SPS_CALL));
+    }
+  }
+
+  private static SPSCommand SPS_RETURN = new SPSCommandImpl().setName("return").setMnemonic("RET")
+      .setCommandByte((byte) 0xE0);
+  private static HoltekEmulator instance;
+  static {
+    SPS_RETURN.getCommandDatas().add(new SPSCommandDataImpl() {
+      @Override
+      public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+        internals.popRtrAddress();
+        return output;
+      }
+    }.setName("Return").setMnemonic("RET").setCommandByte((byte) (SPS_RETURN.getCommandByte())).setCommand(SPS_RETURN));
+  }
+
+  @Override
+  public List<SPSCommand> getCommands() {
+    return commands;
+  }
+
+  @Override
+  public int getProgramSize() {
+    return 128;
+  }
+
+  @Override
+  protected void reset() {
+    getEmulatorOutput().reset();
+    getEmulatorInternals().reset();
+    System.out.println("holtec emulator resettet");
+
+  }
+
+  @Override
+  protected void callNextCommand() {
+    byte command = programMemory[getEmulatorInternals().getAddress()];
+    SPSCommandData commandData = getCommandData(command);
+    runCommand(commandData);
+  }
+
+  @Override
+  protected EmulatorInput createEmulatorInput() {
+    return new HoltekEmulatorInput();
+  }
+
+  @Override
+  protected EmulatorOutput createEmulatorOutput() {
+    return new HoltekEmulatorOutput();
+  }
+
+  @Override
+  protected EmulatorInternals createEmulatorInternals() {
+    return new HoltekEmulatorInternals();
+  }
+
+}

+ 111 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInput.java

@@ -0,0 +1,111 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class HoltekEmulatorInput implements EmulatorInput {
+
+  public static final String AD1 = "AD1";
+  public static final String AD2 = "AD2";
+  public static final String S1 = "S1";
+  public static final String S2 = "S2";
+  boolean i0, i1, i2, i3, s1, s2;
+  int ad1, ad2;
+
+  @Override
+  public void reset() {
+    i0 = false;
+    i1 = false;
+    i2 = false;
+    i3 = false;
+    s1 = false;
+    s2 = false;
+    ad1 = 0;
+    ad2 = 0;
+  }
+
+  @Override
+  public void setInput(byte data) {
+    i0 = (data & (byte) 0x01) > 0;
+    i1 = (data & (byte) 0x02) > 0;
+    i2 = (data & (byte) 0x04) > 0;
+    i3 = (data & (byte) 0x08) > 0;
+  }
+
+  public byte getInput() {
+    return (byte) ((i0 ? 1 : 0) + (i1 ? 2 : 0) + (i2 ? 4 : 0) + (i3 ? 8 : 0));
+  }
+
+  @Override
+  public void setFeature(String name, byte value) {
+    if (AD1.equals(name.toUpperCase())) {
+      ad1 = value;
+    }
+    if (AD2.equals(name.toUpperCase())) {
+      ad2 = value;
+    }
+    if (S1.equals(name.toUpperCase())) {
+      s1 = value > 0;
+    }
+    if (S2.equals(name.toUpperCase())) {
+      s2 = value > 0;
+    }
+  }
+
+  @Override
+  public void setFeature(String name, boolean value) {
+    if (AD1.equals(name.toUpperCase())) {
+      ad1 = value ? 1 : 0;
+    }
+    if (AD2.equals(name.toUpperCase())) {
+      ad2 = value ? 1 : 0;
+    }
+    if (S1.equals(name.toUpperCase())) {
+      s1 = value;
+    }
+    if (S2.equals(name.toUpperCase())) {
+      s2 = value;
+    }
+  }
+
+  @Override
+  public byte getFeature(String name) {
+    if (AD1.equals(name.toUpperCase())) {
+      return (byte) ad1;
+    }
+    if (AD2.equals(name.toUpperCase())) {
+      return (byte) ad2;
+    }
+    if (S1.equals(name.toUpperCase())) {
+      return (byte) ad1;
+    }
+    if (S2.equals(name.toUpperCase())) {
+      return (byte) ad2;
+    }
+    return 0;
+  }
+
+  @Override
+  public boolean getFeatureAsBool(String name) {
+    if (AD1.equals(name.toUpperCase())) {
+      return (byte) ad1 > 0;
+    }
+    if (AD2.equals(name.toUpperCase())) {
+      return (byte) ad2 > 0;
+    }
+    if (S1.equals(name.toUpperCase())) {
+      return s1;
+    }
+    if (S2.equals(name.toUpperCase())) {
+      return s2;
+    }
+    return false;
+  }
+
+}

+ 107 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInternals.java

@@ -0,0 +1,107 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class HoltekEmulatorInternals implements EmulatorInternals {
+  public static final String REGISTER_A = "A";
+  public static final String REGISTER_B = "B";
+  public static final String REGISTER_C = "C";
+  public static final String REGISTER_D = "D";
+  public static final String REGISTER_PAGE = "PAGE";
+  int address;
+  int a, b, c, d;
+  int page;
+  long delay;
+  public int rtrAddress;
+
+  @Override
+  public void reset() {
+    address = 0x00;
+    a = 0;
+    b = 0;
+    c = 0;
+    d = 0;
+    page = 0;
+  }
+
+  @Override
+  public int getAddress() {
+    return address;
+  }
+
+  @Override
+  public void incAddress(int i) {
+    address += i;
+  }
+
+  @Override
+  public void setAddress(int i) {
+    address = i;
+  }
+
+  @Override
+  public void setDelay(long l) {
+    delay = l;
+  }
+
+  @Override
+  public long getDelay() {
+    return delay;
+  }
+
+  @Override
+  public void setRegister(String registerName, byte data) {
+    if (REGISTER_A.equals(registerName.toUpperCase())) {
+      a = data;
+    }
+    if (REGISTER_B.equals(registerName.toUpperCase())) {
+      b = data;
+    }
+    if (REGISTER_C.equals(registerName.toUpperCase())) {
+      c = data;
+    }
+    if (REGISTER_D.equals(registerName.toUpperCase())) {
+      d = data;
+    }
+    if (REGISTER_PAGE.equals(registerName.toUpperCase())) {
+      page = data;
+    }
+  }
+
+  @Override
+  public byte getRegister(String register) {
+    if (REGISTER_A.equals(register.toUpperCase())) {
+      return (byte) (a & 0x0F);
+    }
+    if (REGISTER_B.equals(register.toUpperCase())) {
+      return (byte) (b & 0x0F);
+    }
+    if (REGISTER_C.equals(register.toUpperCase())) {
+      return (byte) (c & 0x0F);
+    }
+    if (REGISTER_D.equals(register.toUpperCase())) {
+      return (byte) (d & 0x0F);
+    }
+    if (REGISTER_PAGE.equals(register.toUpperCase())) {
+      return (byte) (page & 0x0F);
+    }
+    return 0;
+  }
+
+  @Override
+  public void pushRtrAddress() {
+    rtrAddress = address;
+  }
+
+  @Override
+  public void popRtrAddress() {
+    address = rtrAddress;
+  }
+}

+ 53 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorOutput.java

@@ -0,0 +1,53 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class HoltekEmulatorOutput implements EmulatorOutput {
+
+  boolean d0, d1, d2, d3;
+  int pwm;
+
+  @Override
+  public void reset() {
+    d0 = false;
+    d1 = false;
+    d2 = false;
+    d3 = false;
+    pwm = 0;
+  }
+
+  @Override
+  public void setOutput(byte data) {
+    d0 = (data & (byte) 0x01) > 0;
+    d1 = (data & (byte) 0x02) > 0;
+    d2 = (data & (byte) 0x04) > 0;
+    d3 = (data & (byte) 0x08) > 0;
+  }
+
+  @Override
+  public byte getOutput() {
+    return (byte) ((d0 ? 1 : 0) + (d1 ? 2 : 0) + (d2 ? 4 : 0) + (d3 ? 8 : 0));
+  }
+
+  @Override
+  public void setFeature(String name, byte register) {
+    if ("PWM".equals(name.toUpperCase())) {
+      pwm = register;
+    }
+  }
+
+  @Override
+  public byte getFeature(String name) {
+    if ("PWM".equals(name.toUpperCase())) {
+      return (byte) pwm;
+    }
+    return 0;
+  }
+}

+ 35 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataJumpDown.java

@@ -0,0 +1,35 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommandDataImpl;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class SPSCommandDataJumpDown extends SPSCommandDataImpl {
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * de.mcs.tools.sps.SPSCommandDataImpl#doWork(de.mcs.tools.sps.EmulatorInput,
+   * de.mcs.tools.sps.EmulatorInternals)
+   */
+  @Override
+  public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+    byte data = getData();
+    if ((data >= 0) && (data < 16)) {
+      internals.incAddress(-1 * data);
+    } else {
+      throw new IllegalArgumentException(String.format("jump target %d illegal", data));
+    }
+    return output;
+  }
+
+}

+ 30 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataNOP.java

@@ -0,0 +1,30 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommandDataImpl;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class SPSCommandDataNOP extends SPSCommandDataImpl {
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * de.mcs.tools.sps.SPSCommandDataImpl#doWork(de.mcs.tools.sps.EmulatorInput,
+   * de.mcs.tools.sps.EmulatorInternals)
+   */
+  @Override
+  public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+    internals.incAddress(1);
+    return output;
+  }
+
+}

+ 72 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataWait.java

@@ -0,0 +1,72 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommandDataImpl;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class SPSCommandDataWait extends SPSCommandDataImpl {
+
+  private static final long[] WAIT_TIMES = new long[] {
+      // @formatter-off
+      1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 30000, 60000
+      // @formatter-on
+  };
+
+  private static final String[] WAIT_NAMES = new String[] {
+      // @formatter-off
+      "1ms", "2ms", "5ms", "10ms", "20ms", "50ms", "100ms", "200ms", "500ms", "1s", "2s", "5s", "10s", "20s", "30s",
+      "60s"
+      // @formatter-on
+  };
+
+  static String getName(int i) {
+    return String.format("Wait %s", WAIT_NAMES[i]);
+  }
+
+  static String getMnemonic(int i) {
+    return String.format("D", WAIT_NAMES[i]).toUpperCase();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see
+   * de.mcs.tools.sps.SPSCommandDataImpl#doWork(de.mcs.tools.sps.EmulatorInput,
+   * de.mcs.tools.sps.EmulatorInternals)
+   */
+  @Override
+  public EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output) {
+    byte data = getData();
+    if ((data >= 0) && (data < 16)) {
+      try {
+        internals.setDelay(WAIT_TIMES[data]);
+        long delay = WAIT_TIMES[data];
+        if (delay <= 100) {
+          Thread.sleep(delay);
+        } else {
+          long count = delay / 100;
+          while (count > 0) {
+            count--;
+            Thread.sleep(100);
+            internals.setDelay(count * 100);
+          }
+        }
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+    } else {
+      throw new IllegalArgumentException(String.format("Wait time %d illegal", data));
+    }
+    internals.incAddress(1);
+    return output;
+  }
+
+}

+ 8 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/package-info.java

@@ -0,0 +1,8 @@
+/**
+ * 
+ */
+/**
+ * @author w.klaas
+ *
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;

+ 26 - 0
SPSEmulator-service/src/main/java/de/mcs/tools/sps/emulator/emulator/package-info.java

@@ -0,0 +1,26 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: package-info.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 25.11.2018 wklaa_000
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @author wklaa_000
+ *
+ */
+package de.mcs.tools.sps.emulator.emulator;

+ 548 - 0
SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekCommands.java

@@ -0,0 +1,548 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommandData;
+import de.mcs.tools.sps.emulator.SPSEmulator;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulator;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulatorInput;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulatorInternals;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulatorOutput;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class TestHoltekCommands {
+
+  private static SPSEmulator holtekEmulator;
+  private static EmulatorInput input;
+  private static EmulatorOutput output;
+  private static EmulatorInternals internals;
+
+  @BeforeAll
+  static void setUp() throws Exception {
+    holtekEmulator = new HoltekEmulator();
+    input = new HoltekEmulatorInput();
+    output = new HoltekEmulatorOutput();
+    internals = new HoltekEmulatorInternals();
+  }
+
+  SPSCommandData getCommandData(byte command) {
+    return holtekEmulator.getCommandData(command);
+  }
+
+  @Test
+  public void test0x70Empty() {
+    SPSCommandData data = getCommandData((byte) 0x70);
+    assertNull(data);
+  }
+
+  // A = A + 1
+  @Test
+  public void testIncA() {
+    SPSCommandData data = getCommandData((byte) 0x71);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x04, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+  }
+
+  // A = A - 1
+  @Test
+  public void testDecA() {
+    SPSCommandData data = getCommandData((byte) 0x72);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x02, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x00);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+  }
+
+  // A = A + B
+  @Test
+  public void testADDB() {
+    SPSCommandData data = getCommandData((byte) 0x73);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x03);
+    internals.setRegister("B", (byte) 0x04);
+    data.doWork(input, internals, output);
+    assertEquals(0x07, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x07);
+    internals.setRegister("B", (byte) 0x07);
+    data.doWork(input, internals, output);
+    assertEquals(0x0e, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x08);
+    internals.setRegister("B", (byte) 0x08);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+  }
+
+  // A = A - B
+  @Test
+  public void testSUBB() {
+    SPSCommandData data = getCommandData((byte) 0x74);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x04);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x01, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x07);
+    internals.setRegister("B", (byte) 0x07);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x08);
+    data.doWork(input, internals, output);
+    assertEquals(0x0E, internals.getRegister("A"));
+  }
+
+  // A = A * B
+  @Test
+  public void testMULB() {
+    SPSCommandData data = getCommandData((byte) 0x75);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    internals.setRegister("B", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x04, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x03);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x09, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x08);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+  }
+
+  // A = A / B
+  @Test
+  public void testDIVB() {
+    SPSCommandData data = getCommandData((byte) 0x76);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    internals.setRegister("B", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x01, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x02, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x04);
+    data.doWork(input, internals, output);
+    assertEquals(0x01, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x07);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+  }
+
+  // A = A AND B
+  @Test
+  public void testANDB() {
+    SPSCommandData data = getCommandData((byte) 0x77);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    internals.setRegister("B", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x02, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x02, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x00);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x0F);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+  }
+
+  // A = A OR B
+  @Test
+  public void testORB() {
+    SPSCommandData data = getCommandData((byte) 0x78);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    internals.setRegister("B", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x02, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x07, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x00);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x0F);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+  }
+
+  // A = A XOR B
+  @Test
+  public void testXORB() {
+    SPSCommandData data = getCommandData((byte) 0x79);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    internals.setRegister("B", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    internals.setRegister("B", (byte) 0x03);
+    data.doWork(input, internals, output);
+    assertEquals(0x05, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x00);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    internals.setRegister("B", (byte) 0x0F);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+  }
+
+  // A = NOT A
+  @Test
+  public void testNOTA() {
+    SPSCommandData data = getCommandData((byte) 0x7A);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister("A", (byte) 0x02);
+    data.doWork(input, internals, output);
+    assertEquals(0x0D, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x06);
+    data.doWork(input, internals, output);
+    assertEquals(0x09, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x0F);
+    data.doWork(input, internals, output);
+    assertEquals(0x00, internals.getRegister("A"));
+
+    internals.setRegister("A", (byte) 0x00);
+    data.doWork(input, internals, output);
+    assertEquals(0x0F, internals.getRegister("A"));
+  }
+
+  // PAGE = #
+  @Test
+  public void testPage() {
+    for (int i = 0; i < 16; i++) {
+      SPSCommandData data = getCommandData((byte) (0x80 + i));
+      if (i < 0x08) {
+        assertNotNull(data);
+        internals.reset();
+        data.doWork(input, internals, output);
+        assertEquals(i, internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE));
+      } else {
+        assertNull(data);
+      }
+    }
+  }
+
+  // JUMP #
+  @Test
+  public void testJump() {
+    for (int page = 0; page < 16; page++) {
+      SPSCommandData data = getCommandData((byte) (0x80 + page));
+      if (page < 0x08) {
+        assertNotNull(data);
+        internals.reset();
+        data.doWork(input, internals, output);
+        assertEquals(page, internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE));
+        for (int x = 0; x < 16; x++) {
+          data = getCommandData((byte) (0x90 + x));
+          data.doWork(input, internals, output);
+          assertEquals((x + 16 * page), internals.getAddress());
+        }
+      } else {
+        assertNull(data);
+      }
+    }
+  }
+
+  // C LOOP #
+  @Test
+  public void testCLoop() {
+    for (int page = 0; page < 16; page++) {
+      SPSCommandData data = getCommandData((byte) (0x80 + page));
+      if (page < 0x08) {
+        assertNotNull(data);
+        internals.reset();
+        data.doWork(input, internals, output);
+        assertEquals(page, internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE));
+        for (int x = 0; x < 16; x++) {
+          internals.setRegister(HoltekEmulatorInternals.REGISTER_C, (byte) 4);
+          data = getCommandData((byte) (0xA0 + x));
+          data.doWork(input, internals, output);
+          assertEquals((x + 16 * page), internals.getAddress());
+
+          internals.setAddress(0);
+          internals.setRegister(HoltekEmulatorInternals.REGISTER_C, (byte) 0);
+          data.doWork(input, internals, output);
+          assertEquals(0x01, internals.getAddress());
+        }
+      } else {
+        assertNull(data);
+      }
+    }
+  }
+
+  // D LOOP #
+  @Test
+  public void testDLoop() {
+    for (int page = 0; page < 16; page++) {
+      SPSCommandData data = getCommandData((byte) (0x80 + page));
+      if (page < 0x08) {
+        assertNotNull(data);
+        internals.reset();
+        data.doWork(input, internals, output);
+        assertEquals(page, internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE));
+        for (int x = 0; x < 16; x++) {
+          internals.setRegister(HoltekEmulatorInternals.REGISTER_D, (byte) 4);
+          data = getCommandData((byte) (0xB0 + x));
+          data.doWork(input, internals, output);
+          assertEquals((x + 16 * page), internals.getAddress());
+
+          internals.setAddress(0);
+          internals.setRegister(HoltekEmulatorInternals.REGISTER_D, (byte) 0);
+          data.doWork(input, internals, output);
+          assertEquals(0x01, internals.getAddress());
+        }
+      } else {
+        assertNull(data);
+      }
+    }
+  }
+
+  // SKIP IF A > B
+  @Test
+  public void testSkipIfAgtB() {
+    SPSCommandData data = getCommandData((byte) 0xC1);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_A, (byte) 0x03);
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x04);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(2, internals.getAddress());
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x02);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(3, internals.getAddress());
+  }
+
+  // SKIP IF A < B
+  @Test
+  public void testSkipIfAltB() {
+    SPSCommandData data = getCommandData((byte) 0xC2);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_A, (byte) 0x03);
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x02);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(2, internals.getAddress());
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x04);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(3, internals.getAddress());
+  }
+
+  // SKIP IF A = B
+  @Test
+  public void testSkipIfAeqB() {
+    SPSCommandData data = getCommandData((byte) 0xC3);
+    assertNotNull(data);
+    internals.reset();
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_A, (byte) 0x03);
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x02);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(2, internals.getAddress());
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x04);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(2, internals.getAddress());
+
+    internals.setRegister(HoltekEmulatorInternals.REGISTER_B, (byte) 0x03);
+    internals.setAddress(1);
+    data.doWork(input, internals, output);
+    assertEquals(3, internals.getAddress());
+  }
+
+  // SKIP IF Input = 1
+  @Test
+  public void testSkipIfIn1() {
+    for (int i = 0; i < 4; i++) {
+      SPSCommandData data = getCommandData((byte) (0xC4 + i));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setInput((byte) 0x00);
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setInput((byte) (0x01 << i));
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+    }
+  }
+
+  // SKIP IF Input = 0
+  @Test
+  public void testSkipIfIn0() {
+    for (int i = 0; i < 4; i++) {
+      SPSCommandData data = getCommandData((byte) (0xC8 + i));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setInput((byte) 0x00);
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setInput((byte) (0x01 << i));
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+    }
+  }
+
+  // SKIP IF S1, S2
+  @Test
+  public void testSkipIfS12() {
+    for (int i = 0; i < 4; i++) {
+      SPSCommandData data = getCommandData((byte) (0xCC));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S1, false);
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S1, true);
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+
+      data = getCommandData((byte) (0xCD));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S2, false);
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S2, true);
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+
+      data = getCommandData((byte) (0xCE));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S1, true);
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S1, false);
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+
+      data = getCommandData((byte) (0xCF));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S2, true);
+      data.doWork(input, internals, output);
+      assertEquals(3, internals.getAddress());
+
+      internals.setAddress(1);
+      input.setFeature(HoltekEmulatorInput.S2, false);
+      data.doWork(input, internals, output);
+      assertEquals(2, internals.getAddress());
+    }
+  }
+
+  // Call & Return
+  @Test
+  public void testCallReturn() {
+    for (int i = 0; i < 16; i++) {
+      SPSCommandData data = getCommandData((byte) (0xD0 + i));
+      assertNotNull(data);
+      internals.reset();
+      internals.setAddress(i);
+      internals.setRegister(HoltekEmulatorInternals.REGISTER_PAGE, (byte) i);
+
+      data.doWork(input, internals, output);
+      assertEquals(i + 16 * i, internals.getAddress());
+      assertEquals(i, ((HoltekEmulatorInternals) internals).rtrAddress);
+
+      data = getCommandData((byte) 0xE0);
+      assertNotNull(data);
+      data.doWork(input, internals, output);
+      assertEquals(i, internals.getAddress());
+    }
+  }
+}

+ 355 - 0
SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekEmulator.java

@@ -0,0 +1,355 @@
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.SPSCommand;
+import de.mcs.tools.sps.emulator.SPSCommandData;
+import de.mcs.tools.sps.emulator.SPSEmulator;
+import de.mcs.tools.sps.emulator.TOutputCallback;
+import de.mcs.tools.sps.emulator.exceptions.WrongProgramSizeException;
+
+class TestHoltekEmulator implements TOutputCallback {
+
+  private SPSEmulator holtekEmulator;
+
+  @BeforeEach
+  void setUp() throws Exception {
+    holtekEmulator = new HoltekEmulator();
+    holtekEmulator.addCallback(this);
+  }
+
+  @Test
+  void testOutputAllCommands() {
+    System.out.println("command list holtect");
+    List<SPSCommand> commands = holtekEmulator.getCommands();
+    commands.forEach(c -> {
+      List<SPSCommandData> commandDatas = c.getCommandDatas();
+      System.out.printf("Command %s(%s):", c.getMnemonic(), c.getName());
+      commandDatas.forEach(cd -> {
+        System.out.printf("%s(%s) ,", cd.getMnemonic(), cd.getName());
+      });
+      System.out.println();
+    });
+  }
+
+  @Test
+  void testProgramSize() throws WrongProgramSizeException {
+    assertEquals(128, holtekEmulator.getProgramSize());
+    byte[] prgMemory = new byte[128];
+    holtekEmulator.loadProgram(prgMemory);
+  }
+
+  @Test
+  void testWrongProgramSize() throws WrongProgramSizeException {
+    byte[] prgMemory = new byte[129];
+    Throwable exception = assertThrows(WrongProgramSizeException.class, () -> holtekEmulator.loadProgram(prgMemory));
+  }
+
+  @Test
+  void testCommandArray() {
+    List<SPSCommand> commands = holtekEmulator.getCommands();
+    assertEquals(15, commands.size());
+    for (SPSCommand spsCommand : commands) {
+      switch (spsCommand.getCommandByte()) {
+      case 0x00:
+        assertEquals(1, spsCommand.getCommandDatas().size());
+        break;
+      case 0x10:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case 0x20:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case 0x30:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case 0x40:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case 0x50:
+        assertEquals(9, spsCommand.getCommandDatas().size());
+        break;
+      case 0x60:
+        assertEquals(10, spsCommand.getCommandDatas().size());
+        break;
+      case 0x70:
+        assertEquals(10, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0x80:
+        assertEquals(8, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0x90:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0xA0:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0xB0:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0xC0:
+        assertEquals(15, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0xD0:
+        assertEquals(16, spsCommand.getCommandDatas().size());
+        break;
+      case (byte) 0xE0:
+        assertEquals(1, spsCommand.getCommandDatas().size());
+        break;
+      default:
+        fail(String.format("unknow command %s", spsCommand.getName()));
+        break;
+      }
+    }
+  }
+
+  @Test
+  void testNOP() throws WrongProgramSizeException {
+    outputList.clear();
+    internalList.clear();
+    byte[] prg = new byte[] { 0x00, 0x00, 0x00 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+
+    assertEquals(4, outputList.size());
+    assertEquals(4, internalList.size());
+  }
+
+  List<EmulatorOutput> outputList = new ArrayList();
+  List<EmulatorInternals> internalList = new ArrayList<>();
+
+  @Override
+  public void onOutput(EmulatorOutput output) {
+    outputList.add(output);
+    internalList.add(holtekEmulator.getEmulatorInternals());
+  }
+
+  @Test
+  void testPort() throws WrongProgramSizeException {
+    byte[] prg = new byte[] { 0x10, 0x1F, 0x10 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    HoltekEmulatorOutput emulatorOutput = (HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput();
+    assertFalse(emulatorOutput.d0);
+    assertFalse(emulatorOutput.d1);
+    assertFalse(emulatorOutput.d2);
+    assertFalse(emulatorOutput.d3);
+    holtekEmulator.nextStep();
+    assertTrue(emulatorOutput.d0);
+    assertTrue(emulatorOutput.d1);
+    assertTrue(emulatorOutput.d2);
+    assertTrue(emulatorOutput.d3);
+    holtekEmulator.nextStep();
+    assertFalse(emulatorOutput.d0);
+    assertFalse(emulatorOutput.d1);
+    assertFalse(emulatorOutput.d2);
+    assertFalse(emulatorOutput.d3);
+  }
+
+  @Test
+  void testDelay() throws WrongProgramSizeException {
+    // byte[] prg = new byte[] { 0x29, 0x2C };
+    // holtekEmulator.loadProgram(prg);
+    // holtekEmulator.startProgram(true);
+    // long start = System.currentTimeMillis();
+    // holtekEmulator.nextStep();
+    // long delta = System.currentTimeMillis() - start;
+    // assertTrue(delta > 900 && delta < 1100);
+    // start = System.currentTimeMillis();
+    // holtekEmulator.nextStep();
+    // delta = System.currentTimeMillis() - start;
+    // assertTrue(delta > 9000 && delta < 11000);
+  }
+
+  @Test
+  void testJumpDown() throws WrongProgramSizeException {
+    byte[] prg = new byte[] { 0x00, 0x00, 0x32 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    int addr = holtekEmulator.getEmulatorInternals().getAddress();
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getAddress());
+
+    prg = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    addr = holtekEmulator.getEmulatorInternals().getAddress();
+    holtekEmulator.nextStep();
+    assertEquals(0x03, holtekEmulator.getEmulatorInternals().getAddress());
+  }
+
+  @Test
+  void testAEquals() throws WrongProgramSizeException {
+    byte[] prg = new byte[16];
+    for (int i = 0; i < prg.length; i++) {
+      prg[i] = (byte) (0x40 + i);
+    }
+    ;
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    for (int i = 0; i < prg.length; i++) {
+      holtekEmulator.nextStep();
+      assertEquals(i, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    }
+  }
+
+  @Test
+  void testEqualsA() throws WrongProgramSizeException {
+    byte[] prg = new byte[] { 0x48, 0x51, 0x52, 0x53, 0x40, 0x54, 0x4F, 0x54, 0x55 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x08, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    assertEquals(0x08, holtekEmulator.getEmulatorInternals().getRegister("B"));
+    assertEquals(0x08, holtekEmulator.getEmulatorInternals().getRegister("C"));
+    assertEquals(0x08, holtekEmulator.getEmulatorInternals().getRegister("D"));
+
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x00, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x0F, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d0);
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d1);
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d2);
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d3);
+
+    holtekEmulator.stop();
+
+    prg = new byte[] { 0x41, 0x55, 0x56, 0x57, 0x58 };
+
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x01, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+    holtekEmulator.nextStep();
+    assertEquals(0x03, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d0);
+    assertTrue(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d1);
+    assertFalse(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d2);
+    assertFalse(((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).d3);
+    holtekEmulator.nextStep();
+    assertEquals(0x07, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getOutput());
+
+    holtekEmulator.stop();
+
+    prg = new byte[] { 0x41, 0x59, 0x4F, 0x59 };
+
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x01, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getFeature("PWM"));
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    assertEquals(0x0F, ((HoltekEmulatorOutput) holtekEmulator.getEmulatorOutput()).getFeature("PWM"));
+  }
+
+  @Test
+  void testAInput() throws WrongProgramSizeException {
+    byte[] prg = new byte[] { 0x4F, 0x51, 0x52, 0x53, 0x40, 0x61, 0x40, 0x62, 0x40, 0x63 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    for (int i = 0; i < 5; i++) {
+      holtekEmulator.nextStep();
+    }
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.stop();
+
+    prg = new byte[] { 0x64, 0x64, 0x40, 0x65, 0x40, 0x66, 0x40, 0x67, 0x40, 0x68 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.getEmulatorInput().setInput((byte) 0x00);
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.getEmulatorInput().setInput((byte) 0x0f);
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x01, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x01, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x01, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x01, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.stop();
+
+    prg = new byte[] { 0x40, 0x69, 0x40, 0x69, 0x40, 0x6A, 0x40, 0x6A };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.getEmulatorInput().setFeature("AD1", (byte) 0x00);
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.getEmulatorInput().setFeature("AD1", (byte) 0x0F);
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.getEmulatorInput().setFeature("AD2", (byte) 0x00);
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.nextStep();
+    assertEquals(0x00, holtekEmulator.getEmulatorInternals().getRegister("A"));
+    holtekEmulator.getEmulatorInput().setFeature("AD2", (byte) 0x0F);
+    holtekEmulator.nextStep();
+    assertEquals(0x0f, holtekEmulator.getEmulatorInternals().getRegister("A"));
+  }
+}

+ 55 - 0
SPSEmulator-service/src/test/java/de/mcs/tools/sps/emulator/emulator/holtek/TestHoltekProgram.java

@@ -0,0 +1,55 @@
+package de.mcs.tools.sps.emulator.emulator.holtek;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.emulator.EmulatorInput;
+import de.mcs.tools.sps.emulator.EmulatorInternals;
+import de.mcs.tools.sps.emulator.EmulatorOutput;
+import de.mcs.tools.sps.emulator.TOutputCallback;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulator;
+import de.mcs.tools.sps.emulator.emulator.holtek.HoltekEmulatorInternals;
+import de.mcs.tools.sps.emulator.exceptions.WrongProgramSizeException;
+
+class TestHoltekProgram implements TOutputCallback {
+
+  private HoltekEmulator holtekEmulator;
+
+  @BeforeEach
+  void setUp() throws Exception {
+    holtekEmulator = new HoltekEmulator();
+    holtekEmulator.addCallback(this);
+  }
+
+  @Test
+  void test() throws WrongProgramSizeException {
+    byte[] prg = new byte[] { 0x10, 0x1F, 0x10 };
+    holtekEmulator.loadProgram(prg);
+    holtekEmulator.startProgram(true);
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+    holtekEmulator.nextStep();
+  }
+
+  @Override
+  public void onOutput(EmulatorOutput output) {
+    EmulatorInternals internals = holtekEmulator.getEmulatorInternals();
+    EmulatorInput input = holtekEmulator.getEmulatorInput();
+    System.out.printf("adr: 0x%04x, a:0x%02x, b:0x%02x, c:0x%02x, d:0x%02x, page:0x%02x\r\n", internals.getAddress(),
+        internals.getRegister(HoltekEmulatorInternals.REGISTER_A),
+        internals.getRegister(HoltekEmulatorInternals.REGISTER_B),
+        internals.getRegister(HoltekEmulatorInternals.REGISTER_C),
+        internals.getRegister(HoltekEmulatorInternals.REGISTER_D),
+        internals.getRegister(HoltekEmulatorInternals.REGISTER_PAGE));
+    String value = getNibbleString(output.getOutput());
+    System.out.printf("in: %4s, s1: %b, s2: %b, adc1: 0x%02x, adc2: 0x%02x\r\n", value, input.getFeatureAsBool("S1"),
+        input.getFeatureAsBool("S2"), input.getFeature("ADC1"), input.getFeature("ADC2"));
+    System.out.printf("out: %4s, pwm: 0x%02x\r\n", value, output.getFeature("PWM"));
+    System.out.println();
+  }
+
+  private String getNibbleString(byte value) {
+    return String.format("%4s", Integer.toBinaryString(value & 0xFF)).replace(' ', '0');
+  }
+
+}

+ 48 - 0
SimpleServo.tps

@@ -0,0 +1,48 @@
+:loop
+DOUT #15
+WAIT 500ms
+DOUT #0
+WAIT 200ms
+RJMP :loop
+LDA RC1
+; Zeilen kommentar
+NOP
+DOUT #5
+WAIT 200ms
+/*
+block Kommentar
+*/
+DOUT #0x0F
+WAIT 200ms  ; inline Kommentar
+NOP
+LDA #0x04
+LDA DIN1
+MOV a, B
+DOUT
+DOUT 3
+PWM 2
+SRV 0x01
+:loop2
+RJMP 0x06
+RJMP :loop2
+JMP 0x08 ;this will cause an syntax error
+JMP :loop
+PUSH
+POP
+DIN
+DIN 0x03
+ADC 1
+ARC 2
+ADD
+AND
+BSUBA
+BYTE
+DEC
+DIV
+INC
+MOD
+MOV A,B
+MUL
+NOT
+OR
+XOR

+ 37 - 0
examples/Blink.tps

@@ -0,0 +1,37 @@
+.macro blink
+PORT #0B0101
+WAIT 200ms
+PORT #0B1010
+WAIT 200ms
+.endmacro
+
+:loop
+.blink
+RJMP :loop
+/* 
+Kommentar über mehrere Zeilen
+*/
+
+.macro macro1 output time
+PORT output
+WAIT time
+PORT #0x00
+WAIT time
+.endmacro
+
+.include macro_blink
+:loop1
+.macro1 #0x0f 200ms
+
+PORT #0x0F ;Zeilenkommentar
+WAIT 200ms
+PORT #0x00
+WAIT 200ms
+RJMP :loop1
+
+DFSB 1
+PORT #0x0F ;Zeilenkommentar
+WAIT 200ms
+PORT #0x00
+WAIT 200ms
+RTR

+ 6 - 0
examples/Blink2.tps

@@ -0,0 +1,6 @@
+:loop
+PORT #0x0F
+WAIT 200ms
+PORT #0x00
+WAIT 200ms
+RJMP :loop

+ 32 - 0
examples/BlinkKomment.tps

@@ -0,0 +1,32 @@
+LDA #6
+MOV C,A
+:loop
+NOP
+NOP
+NOP
+NOP
+NOP
+PORT #0x0F
+WAIT 200ms
+PORT #0x00
+WAIT 200ms
+NOP
+PAGE :?
+CALL :loop3
+NOP
+NOP
+NOP
+PAGE :?
+LOOPC :loop
+NOP
+NOP
+:loop2
+PORT #0x0F
+:loop3
+PORT #0x00
+NOP
+NOP
+NOP
+PAGE :?
+LOOPD :loop2
+NOP

+ 20 - 0
examples/Blink_LOOP.tps

@@ -0,0 +1,20 @@
+PAGE :?
+JMP :loop2
+:loop
+NOP
+NOP
+NOP
+NOP
+NOP
+PORT #0x0F
+WAIT 200ms
+PORT #0x00
+WAIT 200ms
+NOP
+NOP
+NOP
+NOP
+:loop2
+RJMP :loop
+NOP
+NOP

+ 6 - 0
examples/includes/macro_blink.tps

@@ -0,0 +1,6 @@
+.macro blink output time
+PORT output
+WAIT time
+PORT #0x00
+WAIT time
+.endmacro

+ 179 - 0
pom.xml

@@ -0,0 +1,179 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>de.mcs.tools.sps</groupId>
+	<artifactId>SPSEmulator</artifactId>
+	<version>0.2.0</version>
+	<name>${project.groupId}:${project.artifactId}</name>
+	<url>http://www.wk-music.de</url>
+	<description>SPS Emulator wirtten in java.</description>
+	<licenses>
+		<license>
+			<name>The Apache License, Version 2.0</name>
+			<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+		</license>
+	</licenses>
+
+	<developers>
+		<developer>
+			<name>Wilfried Klaas</name>
+			<email>w.klaas@gmx.de</email>
+			<organization>MCS</organization>
+			<organizationUrl>http://www.wk-music.de</organizationUrl>
+		</developer>
+	</developers>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<timestamp>${maven.build.timestamp}</timestamp>
+		<maven.build.timestamp.format>dd.mm.yyyy HH:mm</maven.build.timestamp.format>
+		<jackson.version>2.8.5</jackson.version>
+	</properties>
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.3</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.19.1</version>
+				<dependencies>
+					<dependency>
+						<groupId>org.junit.platform</groupId>
+						<artifactId>junit-platform-surefire-provider</artifactId>
+						<version>1.1.0</version>
+					</dependency>
+					<dependency>
+						<groupId>org.junit.jupiter</groupId>
+						<artifactId>junit-jupiter-engine</artifactId>
+						<version>5.1.0</version>
+					</dependency>
+				</dependencies>
+				<configuration>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.6</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>de.mcs.tools.sps.SPSAssembler</mainClass>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+						</manifest>
+						<manifestEntries>
+							<Build-Time>${maven.build.timestamp}</Build-Time>
+							<Application-Name>SPSTools</Application-Name>
+							<Application-Update-Id>72</Application-Update-Id>
+							<Application-Update-Url>http\://wkla.no-ip.biz/downloader/version.php?ID\=73</Application-Update-Url>
+							<Application-Url>http\://wkla.no-ip.biz/</Application-Url>
+							<Implementation-Vendor>MCS, Media Computer Software</Implementation-Vendor>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-shade-plugin</artifactId>
+				<version>2.4.1</version>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>shade</goal>
+						</goals>
+						<!-- <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> 
+							<mainClass></mainClass> </transformer> </transformers> </configuration> -->
+					</execution>
+				</executions>
+			</plugin>
+			<!-- <plugin> <groupId>com.akathist.maven.plugins.launch4j</groupId> <artifactId>launch4j-maven-plugin</artifactId> 
+				<version>1.7.24</version> <executions> <execution> <id>l4j-clui</id> <phase>package</phase> 
+				<goals> <goal>launch4j</goal> </goals> <configuration> <dontWrapJar>false</dontWrapJar> 
+				<headerType>console</headerType> <jar>${project.build.directory}/${project.artifactId}-${project.version}.jar</jar> 
+				<outfile>${project.build.directory}/MCSSPSTools.exe</outfile> <downloadUrl>http://java.com/download</downloadUrl> 
+				<classPath> <mainClass>com.howtodoinjava.ApplicationMain</mainClass> <preCp>anything</preCp> 
+				</classPath> <icon>src\main\resources\MSA.ico</icon> <jre> <path>/jre</path> 
+				<minVersion>1.8.0</minVersion> <jdkPreference>jreOnly</jdkPreference> </jre> 
+				<versionInfo> <fileVersion>1.0.0.0</fileVersion> <txtFileVersion>${project.version}</txtFileVersion> 
+				<fileDescription>${project.name}</fileDescription> <copyright>2018 MCS</copyright> 
+				<productVersion>1.0.0.0</productVersion> <txtProductVersion>1.0.0.0</txtProductVersion> 
+				<productName>${project.name}</productName> <companyName>MCS</companyName> 
+				<internalName>MCSSPSTools</internalName> <originalFilename>MCSSPSTools.exe</originalFilename> 
+				</versionInfo> </configuration> </execution> </executions> </plugin> -->
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>net.sourceforge.jmeasurement2</groupId>
+			<artifactId>MCSUtils</artifactId>
+			<version>1.0.152</version>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+			<version>5.1.0</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.5</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sourceforge.jmeasurement2</groupId>
+			<artifactId>JMeasurement</artifactId>
+			<version>1.1.225</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-core</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-annotations</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.fasterxml.jackson.dataformat</groupId>
+			<artifactId>jackson-dataformat-yaml</artifactId>
+			<version>${jackson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>1.2.17</version>
+		</dependency>
+		<dependency>
+			<groupId>com.martiansoftware</groupId>
+			<artifactId>jsap</artifactId>
+			<version>2.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.reflections</groupId>
+			<artifactId>reflections</artifactId>
+			<version>0.9.10</version>
+		</dependency>
+	</dependencies>
+</project>