Wilfried Klaas 6 роки тому
батько
коміт
4b7bdaff20
100 змінених файлів з 5945 додано та 480 видалено
  1. 2 2
      .classpath
  2. 12 0
      .gitignore
  3. 0 6
      .settings/org.eclipse.core.resources.prefs
  4. 0 9
      .settings/org.eclipse.jdt.core.prefs
  5. 0 4
      .settings/org.eclipse.m2e.core.prefs
  6. 48 0
      SimpleServo.tps
  7. 33 0
      examples/Blink.tps
  8. 6 0
      examples/Blink2.tps
  9. 32 0
      examples/BlinkKomment.tps
  10. 20 0
      examples/Blink_LOOP.tps
  11. 6 0
      examples/includes/macro_blink.tps
  12. 62 54
      pom.xml
  13. 90 0
      src/main/java/de/mcs/tools/IntelHex.java
  14. 0 61
      src/main/java/de/mcs/tools/sps/AbstractEmulator.java
  15. 57 0
      src/main/java/de/mcs/tools/sps/HEXTextOutputter.java
  16. 47 0
      src/main/java/de/mcs/tools/sps/IntelHEXOutputter.java
  17. 105 0
      src/main/java/de/mcs/tools/sps/Macro.java
  18. 21 0
      src/main/java/de/mcs/tools/sps/Outputter.java
  19. 525 0
      src/main/java/de/mcs/tools/sps/SPSAssembler.java
  20. 0 14
      src/main/java/de/mcs/tools/sps/TEmulatorInternals.java
  21. 0 14
      src/main/java/de/mcs/tools/sps/TEmulatorOutput.java
  22. 67 0
      src/main/java/de/mcs/tools/sps/TPSTextOutputter.java
  23. 22 0
      src/main/java/de/mcs/tools/sps/annotations/SPSOutputter.java
  24. 8 0
      src/main/java/de/mcs/tools/sps/annotations/package-info.java
  25. 19 0
      src/main/java/de/mcs/tools/sps/emulator/EmulatorInput.java
  26. 33 0
      src/main/java/de/mcs/tools/sps/emulator/EmulatorInternals.java
  27. 24 0
      src/main/java/de/mcs/tools/sps/emulator/EmulatorOutput.java
  28. 1 1
      src/main/java/de/mcs/tools/sps/emulator/SPSCommand.java
  29. 6 1
      src/main/java/de/mcs/tools/sps/emulator/SPSCommandData.java
  30. 19 2
      src/main/java/de/mcs/tools/sps/emulator/SPSCommandDataImpl.java
  31. 5 1
      src/main/java/de/mcs/tools/sps/emulator/SPSCommandImpl.java
  32. 17 4
      src/main/java/de/mcs/tools/sps/emulator/SPSEmulator.java
  33. 2 2
      src/main/java/de/mcs/tools/sps/emulator/TOutputCallback.java
  34. 141 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/AbstractEmulator.java
  35. 682 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulator.java
  36. 111 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInput.java
  37. 107 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorInternals.java
  38. 53 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/HoltekEmulatorOutput.java
  39. 35 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataJumpDown.java
  40. 30 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataNOP.java
  41. 72 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/SPSCommandDataWait.java
  42. 8 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/holtek/package-info.java
  43. 26 0
      src/main/java/de/mcs/tools/sps/emulator/emulator/package-info.java
  44. 1 1
      src/main/java/de/mcs/tools/sps/emulator/exceptions/WrongProgramSizeException.java
  45. 8 0
      src/main/java/de/mcs/tools/sps/emulator/exceptions/package-info.java
  46. 26 0
      src/main/java/de/mcs/tools/sps/emulator/package-info.java
  47. 38 0
      src/main/java/de/mcs/tools/sps/exceptions/IllegalArgument.java
  48. 38 0
      src/main/java/de/mcs/tools/sps/exceptions/SyntaxError.java
  49. 0 8
      src/main/java/de/mcs/tools/sps/exceptions/package-info.java
  50. 0 288
      src/main/java/de/mcs/tools/sps/holtec/HoltecEmulator.java
  51. 0 8
      src/main/java/de/mcs/tools/sps/holtec/package-info.java
  52. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/ADD.java
  53. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/AEQB.java
  54. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/AGTB.java
  55. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/ALTB.java
  56. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/AND.java
  57. 149 0
      src/main/java/de/mcs/tools/sps/mnemonic/AbstractMnemonic.java
  58. 119 0
      src/main/java/de/mcs/tools/sps/mnemonic/BLDA.java
  59. 120 0
      src/main/java/de/mcs/tools/sps/mnemonic/BSTA.java
  60. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/BSUBA.java
  61. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/BYTE.java
  62. 84 0
      src/main/java/de/mcs/tools/sps/mnemonic/CALL.java
  63. 88 0
      src/main/java/de/mcs/tools/sps/mnemonic/CASB.java
  64. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/DEC.java
  65. 77 0
      src/main/java/de/mcs/tools/sps/mnemonic/DEQ0.java
  66. 77 0
      src/main/java/de/mcs/tools/sps/mnemonic/DEQ1.java
  67. 88 0
      src/main/java/de/mcs/tools/sps/mnemonic/DFSB.java
  68. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/DIV.java
  69. 5 0
      src/main/java/de/mcs/tools/sps/mnemonic/HARDWARE.java
  70. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/INC.java
  71. 84 0
      src/main/java/de/mcs/tools/sps/mnemonic/JMP.java
  72. 160 0
      src/main/java/de/mcs/tools/sps/mnemonic/LDA.java
  73. 84 0
      src/main/java/de/mcs/tools/sps/mnemonic/LOOPC.java
  74. 84 0
      src/main/java/de/mcs/tools/sps/mnemonic/LOOPD.java
  75. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/MOD.java
  76. 133 0
      src/main/java/de/mcs/tools/sps/mnemonic/MOV.java
  77. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/MUL.java
  78. 31 0
      src/main/java/de/mcs/tools/sps/mnemonic/Mnemonic.java
  79. 67 0
      src/main/java/de/mcs/tools/sps/mnemonic/MnemonicFactory.java
  80. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/NOP.java
  81. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/NOT.java
  82. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/OR.java
  83. 94 0
      src/main/java/de/mcs/tools/sps/mnemonic/PAGE.java
  84. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/PEND.java
  85. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/POP.java
  86. 86 0
      src/main/java/de/mcs/tools/sps/mnemonic/PORT.java
  87. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/PRG0.java
  88. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/PRG1.java
  89. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/PUSH.java
  90. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/REST.java
  91. 89 0
      src/main/java/de/mcs/tools/sps/mnemonic/RJMP.java
  92. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/RTR.java
  93. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/SEL0.java
  94. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/SEL1.java
  95. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/SKIP0.java
  96. 151 0
      src/main/java/de/mcs/tools/sps/mnemonic/STA.java
  97. 43 0
      src/main/java/de/mcs/tools/sps/mnemonic/SUB.java
  98. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/SWAP.java
  99. 51 0
      src/main/java/de/mcs/tools/sps/mnemonic/TONE.java
  100. 118 0
      src/main/java/de/mcs/tools/sps/mnemonic/WAIT.java

+ 2 - 2
.classpath

@@ -13,15 +13,15 @@
 	</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"/>
-			<attribute name="test" value="true"/>
 		</attributes>
 	</classpathentry>
 	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
 		<attributes>
-			<attribute name="maven.pomderived" value="true"/>
 			<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">

+ 12 - 0
.gitignore

@@ -1 +1,13 @@
 /target/
+
+SimpleServo\.hex
+
+SimpleServo\.txt
+
+*.hex
+
+dependency-reduced-pom\.xml
+
+\.settings/
+
+examples/Blink\.txt

+ 0 - 6
.settings/org.eclipse.core.resources.prefs

@@ -1,6 +0,0 @@
-eclipse.preferences.version=1
-encoding//src/main/java=UTF-8
-encoding//src/main/resources=UTF-8
-encoding//src/test/java=UTF-8
-encoding//src/test/resources=UTF-8
-encoding/<project>=UTF-8

+ 0 - 9
.settings/org.eclipse.jdt.core.prefs

@@ -1,9 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.compliance=1.8
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.release=disabled
-org.eclipse.jdt.core.compiler.source=1.8

+ 0 - 4
.settings/org.eclipse.m2e.core.prefs

@@ -1,4 +0,0 @@
-activeProfiles=
-eclipse.preferences.version=1
-resolveWorkspaceProjects=true
-version=1

+ 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

+ 33 - 0
examples/Blink.tps

@@ -0,0 +1,33 @@
+.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
+
+.macro empty
+.endmacro

+ 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

+ 62 - 54
pom.xml

@@ -1,11 +1,13 @@
-<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.0.1-SNAPSHOT</version>
-  <name>SPS_Emulator</name>
-  <url>http://www.wk-music.de</url>
-  <description>SPS Emulator wirtten in java.</description>
+<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.0.1-SNAPSHOT</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>
@@ -38,6 +40,26 @@
 					<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>
@@ -45,15 +67,15 @@
 				<configuration>
 					<archive>
 						<manifest>
-							<mainClass>de.mcs.tools.addsub.Main</mainClass>
+							<mainClass>de.mcs.tools.sps.SPSAssembler</mainClass>
 							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
 							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
 						</manifest>
 						<manifestEntries>
 							<Build-Time>${maven.build.timestamp}</Build-Time>
-							<Application-Name>MCSAddSub</Application-Name>
+							<Application-Name>SPSTools</Application-Name>
 							<Application-Update-Id>72</Application-Update-Id>
-							<Application-Update-Url>http\://wkla.no-ip.biz/downloader/version.php?ID\=72</Application-Update-Url>
+							<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>
@@ -75,52 +97,33 @@
 					</execution>
 				</executions>
 			</plugin>
-			<plugin>
-				<groupId>com.akathist.maven.plugins.launch4j</groupId>
-				<artifactId>launch4j-maven-plugin</artifactId>
-				<version>1.7.8</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}/MCSAddSub.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>
-								<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>MCSAddSub</internalName>
-								<originalFilename>MCSAddSub.exe</originalFilename>
-							</versionInfo>
-						</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.150</version>
+			<version>1.0.151-SNAPSHOT</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>
@@ -163,9 +166,14 @@
 			<version>2.1</version>
 		</dependency>
 		<dependency>
-		  <groupId>org.apache.commons</groupId>
-		  <artifactId>commons-lang3</artifactId>
-		  <version>3.4</version>
+			<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>

+ 90 - 0
src/main/java/de/mcs/tools/IntelHex.java

@@ -0,0 +1,90 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: IntelHex.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/>
+ */
+package de.mcs.tools;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class IntelHex {
+
+  private int byteCount;
+
+  public IntelHex() {
+    byteCount = 8;
+  }
+
+  public void writeHexStream(OutputStream output, byte[] data) {
+    int address = 0;
+    int crc = 0;
+    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output))) {
+      for (int i = 0; i < data.length; i++) {
+        if ((i % byteCount) == 0) {
+          if (i > 0) {
+            crc = writeCRC(crc, writer);
+          }
+          crc = 0;
+          writer.append(':');
+          if ((data.length - i) > byteCount) {
+            writer.append(String.format("%02X", byteCount));
+            crc += byteCount;
+          } else {
+            writer.append(String.format("%02X", data.length - i));
+            crc += (data.length - i);
+          }
+          writer.append(String.format("%04X", address));
+          crc += address >> 8;
+          crc += address & 0x00FF;
+          writer.append("00");
+        }
+        writer.append(String.format("%02X", data[i]));
+        address++;
+      }
+      writeCRC(crc, writer);
+      crc = 0;
+      writer.append(':');
+      writer.append(String.format("%02X", 0));
+      crc += byteCount;
+      writer.append(String.format("%04X", 0));
+      crc += address >> 8;
+      crc += address & 0x00FF;
+      writer.append("01FF");
+
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  private int writeCRC(int crc, BufferedWriter writer) throws IOException {
+    crc = crc & 0x00FF;
+    crc = crc ^ 0x00FF;
+    crc++;
+    writer.append(String.format("%02X", crc));
+    writer.append("\r\n");
+    return crc;
+  }
+}

+ 0 - 61
src/main/java/de/mcs/tools/sps/AbstractEmulator.java

@@ -1,61 +0,0 @@
-/**
- * 
- */
-package de.mcs.tools.sps;
-
-import java.util.List;
-
-import de.mcs.tools.sps.exceptions.WrongProgramSizeException;
-
-/**
- * @author w.klaas
- *
- */
-public abstract class AbstractEmulator implements SPSEmulator {
-
-  @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()));
-    }
-
-  }
-
-  @Override
-  public void startProgram(boolean debug) {
-
-  }
-
-  @Override
-  public void addCallback(TOutputCallback callback) {
-
-  }
-
-  @Override
-  public void nextStep() {
-
-  }
-
-  @Override
-  public boolean isActive() {
-    return false;
-  }
-
-  @Override
-  public void stop() {
-
-  }
-
-  @Override
-  public TEmulatorInternals getInternals() {
-    return null;
-  }
-
-}

+ 57 - 0
src/main/java/de/mcs/tools/sps/HEXTextOutputter.java

@@ -0,0 +1,57 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.List;
+
+import de.mcs.tools.sps.annotations.SPSOutputter;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+
+/**
+ * @author w.klaas
+ *
+ */
+@SPSOutputter(name = "HEXTXT")
+public class HEXTextOutputter implements Outputter {
+
+  /**
+   * 
+   */
+  public HEXTextOutputter() {
+    // TODO Auto-generated constructor stub
+  }
+
+  /* (non-Javadoc)
+   * @see de.mcs.tools.sps.Outputter#output(java.util.List, java.io.OutputStream)
+   */
+  @Override
+  public void output(List<Mnemonic> mnemonics, OutputStream output) {
+    try (Writer writer = new BufferedWriter(new OutputStreamWriter(output))) {
+      int address = 0;
+      for (Mnemonic mnemonic : mnemonics) {
+        writer.write(String.format("%02x", mnemonic.getByte()));
+        address++;
+        if ((address % 8) == 0) {
+          writer.write("\r\n");
+        } else {
+          writer.write(" ");
+        }
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    } finally {
+    }
+  }
+
+  @Override
+  public String getDefaultExtension() {
+    return ".txt";
+  }
+
+}

+ 47 - 0
src/main/java/de/mcs/tools/sps/IntelHEXOutputter.java

@@ -0,0 +1,47 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps;
+
+import java.io.OutputStream;
+import java.util.List;
+
+import de.mcs.tools.IntelHex;
+import de.mcs.tools.sps.annotations.SPSOutputter;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+
+/**
+ * @author w.klaas
+ *
+ */
+@SPSOutputter(name = "HEX")
+public class IntelHEXOutputter implements Outputter {
+
+  /**
+   * 
+   */
+  public IntelHEXOutputter() {
+    // TODO Auto-generated constructor stub
+  }
+
+  /* (non-Javadoc)
+   * @see de.mcs.tools.sps.Outputter#output(java.util.List, java.io.OutputStream)
+   */
+  @Override
+  public void output(List<Mnemonic> mnemonics, OutputStream output) {
+    IntelHex intelHex = new IntelHex();
+    byte[] data = new byte[mnemonics.size()];
+    int i = 0;
+    for (Mnemonic mnemonic : mnemonics) {
+      data[i] = (byte) mnemonic.getByte();
+      i++;
+    }
+    intelHex.writeHexStream(output, data);
+  }
+
+  @Override
+  public String getDefaultExtension() {
+    return ".hex";
+  }
+
+}

+ 105 - 0
src/main/java/de/mcs/tools/sps/Macro.java

@@ -0,0 +1,105 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Macro.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 06.12.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/>
+ */
+package de.mcs.tools.sps;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class Macro {
+
+  private String[] args;
+  private String name;
+  private List<String> lines;
+
+  public Macro(String name, String[] args) {
+    this.name = name;
+    this.args = args;
+    lines = new ArrayList<>();
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder b = new StringBuilder();
+    b.append(String.format("name: %s", name));
+    if (args != null) {
+      StringBuilder b1 = new StringBuilder();
+      b1.append("[");
+      boolean start = true;
+      for (String arg : args) {
+        if (!start) {
+          b1.append(",");
+        }
+        start = false;
+        b1.append(arg);
+      }
+      b1.append("]");
+      b.append(String.format(", args: %s", b1));
+    }
+    return b.toString();
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void addLine(String line) {
+    lines.add(line);
+  }
+
+  public List<String> processMacro(String[] values, int lineNumber) throws SyntaxError {
+    if ((values != null) && (args == null)) {
+      throw new IllegalArgument(lineNumber,
+          String.format("count of macro arguments (%d) are not the same as in macro definition (%d) of macro \"%s\".",
+              values.length, 0, name));
+    }
+    if ((values == null) && (args != null)) {
+      throw new IllegalArgument(lineNumber,
+          String.format("count of macro arguments (%d) are not the same as in macro definition (%d) of macro \"%s\".",
+              0, args.length, name));
+    }
+    if (values != null && values.length != args.length) {
+      throw new SyntaxError(lineNumber,
+          String.format("count of macro arguments (%d) are not the same as in macro definition (%d) of macro \"%s\".",
+              values.length, args.length, name));
+    }
+    List<String> mnemonics = new ArrayList<>();
+    lines.forEach(l -> {
+      String line = l;
+      if (args != null) {
+        for (int i = 0; i < args.length; i++) {
+          String key = args[i];
+          String value = values[i];
+          line = line.replace(key, value);
+        }
+      }
+      mnemonics.add(line);
+    });
+    return mnemonics;
+  }
+}

+ 21 - 0
src/main/java/de/mcs/tools/sps/Outputter.java

@@ -0,0 +1,21 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps;
+
+import java.io.OutputStream;
+import java.util.List;
+
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+
+/**
+ * @author w.klaas
+ *
+ */
+public interface Outputter {
+
+  void output(List<Mnemonic> mnemonics, OutputStream output);
+
+  String getDefaultExtension();
+
+}

+ 525 - 0
src/main/java/de/mcs/tools/sps/SPSAssembler.java

@@ -0,0 +1,525 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: SPSAssembler.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/>
+ */
+package de.mcs.tools.sps;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.reflections.Reflections;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+
+import de.mcs.tools.sps.annotations.SPSOutputter;
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+import de.mcs.tools.sps.mnemonic.CALL;
+import de.mcs.tools.sps.mnemonic.HARDWARE;
+import de.mcs.tools.sps.mnemonic.JMP;
+import de.mcs.tools.sps.mnemonic.LOOPC;
+import de.mcs.tools.sps.mnemonic.LOOPD;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+import de.mcs.tools.sps.mnemonic.MnemonicFactory;
+import de.mcs.tools.sps.mnemonic.PAGE;
+import de.mcs.tools.sps.mnemonic.RJMP;
+import de.mcs.utils.jsap.Command;
+import de.mcs.utils.jsap.CommandlineProcessor;
+import de.mcs.utils.jsap.FileOption;
+import de.mcs.utils.jsap.StringOption;
+import de.mcs.utils.jsap.SwitchOption;
+
+/**
+ * @author wklaa_000
+ *
+ */
+@Command(help = "SPS Assembler \r\n usage java -jar SPSEmulator-x.x.x.jar <options>")
+public class SPSAssembler {
+
+  private static final String INCLUDE_FILE = ".include";
+  private static final String END_MACRO_DEFINITION = ".endmacro";
+  private static final String START_MACRO_DEFINITION = ".macro";
+  private static final String DEFAULT_PACKAGE_FILTER = "de.mcs.tools.sps"; //$NON-NLS-1$
+  private static File source;
+  private static HARDWARE destination;
+  private static File destinationFile;
+  private static String outputFormat;
+  private static File includes;
+
+  private int lineNumber;
+  // private int srcLineNumber;
+  private Map<String, Integer> labels;
+  private boolean inBlockComment;
+  private List<Mnemonic> mnemonics;
+  private boolean inMacro;
+  private Macro actualMacro;
+  private Map<String, Macro> macros;
+  private static Outputter outputter;
+
+  @SwitchOption(shortKey = 'h', longKey = "help", name = "help", help = "show this help page", required = false, defaultValue = false)
+  public static void doHelp(boolean value) {
+    if (value) {
+      CommandlineProcessor.showHelp();
+      System.exit(0);
+    }
+  }
+
+  @StringOption(shortKey = 'f', longKey = "format", name = "format", defaultValue = "HEX", help = "the output format. HEX: IntelHEX, TPSTXT: TPS programming text", required = false)
+  public static void setOutputFormat(String value) {
+    outputFormat = value.toUpperCase();
+  }
+
+  @StringOption(shortKey = 'd', longKey = "hardware", name = "hardware system", defaultValue = "HOLTEK", help = "the hardware system to compile to. Passible options are: HOLTEK, ATMEGA8, ARDUINOSPS, TINYSPS", required = false)
+  public static void setDestinationSystem(String value) {
+    destination = HARDWARE.valueOf((value.toUpperCase()));
+  }
+
+  @FileOption(shortKey = 'i', longKey = "includes", name = "includes", defaultValue = "", help = "where to find the includes files.", required = false)
+  public static void setDestinationSystem(File file) {
+    if ((file != null) && !file.getName().equals("")) {
+      includes = file;
+    }
+  }
+
+  @FileOption(index = 1, name = "source file", help = "source file to compile", required = true, mustExists = true)
+  public static void setSourceFile(File file) {
+    if ((file != null) && !file.getName().equals("")) {
+      source = file;
+    }
+  }
+
+  @FileOption(index = 2, name = "destination file", help = "destination file to compile to", required = false)
+  public static void setDestinationFile(File file) {
+    if ((file != null) && !file.getName().equals("")) {
+      destinationFile = file;
+    }
+  }
+
+  /**
+   * @param args
+   * @throws IOException
+   */
+  public static void main(String[] args) {
+    try {
+      destination = HARDWARE.HOLTEK;
+
+      CommandlineProcessor.processCommandline(SPSAssembler.class, args);
+
+      registerAllOutputter();
+
+      if ((source == null) || (!source.exists())) {
+        CommandlineProcessor.showHelp(
+            String.format("source file null or not found. %s", (source == null) ? "" : source.getAbsolutePath()));
+        System.exit(-1);
+      }
+
+      if (destinationFile == null) {
+        String name = source.getName();
+        name = name.substring(0, source.getName().lastIndexOf("."));
+        String ext = outputter.getDefaultExtension();
+        destinationFile = new File(source.getParentFile(), name + ext);
+      }
+
+      System.out.printf("source file: %s \r\n", source.getName());
+      System.out.printf("destination file: %s \r\n", destinationFile.getName());
+      System.out.printf("output format: %s \r\n", outputFormat);
+      System.out.println();
+
+      try {
+        SPSAssembler spsAssembler = new SPSAssembler();
+        spsAssembler.doWork(source);
+
+        List<Mnemonic> mnemonics = spsAssembler.getMnemonics();
+
+        outputMnemonics(mnemonics);
+
+        outputLabels(spsAssembler);
+
+        outputMacros(spsAssembler);
+
+        outputFile(mnemonics);
+
+        System.out.println("assembling succesfully");
+      } catch (SyntaxError e) {
+        e.printStackTrace();
+        System.err.println(e.getMessage());
+        System.exit(-1);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * @throws InstantiationException
+   * @throws IllegalAccessException
+   * @throws Exception
+   */
+  private static void registerAllOutputter() throws InstantiationException, IllegalAccessException, Exception {
+    Set<Class<?>> outputClasses = searchOutputter(SPSOutputter.class);
+    for (Class<?> outClass : outputClasses) {
+      SPSOutputter annotation = outClass.getAnnotation(SPSOutputter.class);
+      if (annotation.name().equalsIgnoreCase(outputFormat)) {
+        outputter = (Outputter) outClass.newInstance();
+      }
+    }
+
+    if (outputter == null) {
+      throw new Exception(String.format("can't find outputter for format: %s", outputFormat));
+    }
+  }
+
+  private static Set<Class<?>> searchOutputter(Class<? extends Annotation> annotationClass) {
+    ConfigurationBuilder builder = new ConfigurationBuilder();
+    builder.addUrls(ClasspathHelper.forPackage(DEFAULT_PACKAGE_FILTER));
+    Reflections reflections = new Reflections(builder);
+    Set<Class<?>> classes = new HashSet<Class<?>>();
+    classes.addAll(reflections.getTypesAnnotatedWith(annotationClass));
+    return classes;
+  }
+
+  /**
+   * @param mnemonics
+   * @throws IOException
+   * @throws FileNotFoundException
+   */
+  private static void outputFile(List<Mnemonic> mnemonics) throws IOException, FileNotFoundException {
+    try (FileOutputStream output = new FileOutputStream(destinationFile)) {
+      outputter.output(mnemonics, output);
+    } finally {
+    }
+  }
+
+  /**
+   * @param spsAssembler
+   */
+  private static void outputMacros(SPSAssembler spsAssembler) {
+    Map<String, Macro> macros = spsAssembler.getMacros();
+    if ((macros != null) && (macros.size() > 0)) {
+      System.out.println();
+      System.out.println("macros");
+      for (Macro macro : macros.values()) {
+        System.out.printf("%s: %s\r\n", macro.getName(), macro.toString());
+      }
+    }
+  }
+
+  /**
+   * @param spsAssembler
+   */
+  private static void outputLabels(SPSAssembler spsAssembler) {
+    Map<String, Integer> labels = spsAssembler.getLabels();
+    if ((labels != null) && (labels.size() > 0)) {
+      System.out.println();
+      System.out.println("labels");
+      for (Entry<String, Integer> entry : labels.entrySet()) {
+        System.out.printf("%s: 0x%03x\r\n", entry.getKey(), entry.getValue());
+      }
+    }
+  }
+
+  /**
+   * @param mnemonics
+   */
+  private static void outputMnemonics(List<Mnemonic> mnemonics) {
+    System.out.println("Mnemonics");
+    int pos = 0;
+    for (Iterator<Mnemonic> iterator = mnemonics.iterator(); iterator.hasNext();) {
+      Mnemonic mnemonic = iterator.next();
+      System.out.printf("0x%03x: %s\r\n", pos, mnemonic.toString());
+      pos++;
+    }
+  }
+
+  /**
+   * 
+   */
+  public SPSAssembler() {
+    lineNumber = 0;
+    // srcLineNumber = 0;
+    labels = new HashMap<>();
+    inBlockComment = false;
+    mnemonics = new ArrayList<>();
+    inMacro = false;
+    actualMacro = null;
+    macros = new HashMap<>();
+  }
+
+  private void doWork(File source) throws IOException, SyntaxError {
+    List<String> sourceFile = Files.readAllLines(source.toPath(), Charset.forName("UTF-8"));
+    for (int i = 0; i < sourceFile.size(); i++) {
+      String line = sourceFile.get(i);
+
+      List<String> linesToAdd;
+      do {
+        linesToAdd = parseLine(i, line);
+        if (linesToAdd != null) {
+          sourceFile.remove(i);
+          sourceFile.addAll(i, linesToAdd);
+          line = sourceFile.get(i);
+          linesToAdd = parseLine(i, line);
+        }
+      } while (linesToAdd != null);
+    }
+
+    // Checking destination
+    checkingHardware();
+
+    System.out.printf("start parsing file: %s\r\n", source.getName());
+    int address = 0;
+    Mnemonic predecessor = null;
+    for (Iterator<Mnemonic> iterator = mnemonics.iterator(); iterator.hasNext();) {
+      Mnemonic mnemonic = iterator.next();
+      if ((mnemonic instanceof JMP) || (mnemonic instanceof LOOPC) || (mnemonic instanceof LOOPD)
+          || (mnemonic instanceof CALL)) {
+        if (mnemonic.isLabel()) {
+          processJMP(mnemonic, predecessor);
+        }
+      }
+      if (mnemonic instanceof RJMP) {
+        if (mnemonic.isLabel()) {
+          processRJMP(address, mnemonic);
+        }
+      }
+      address++;
+      predecessor = mnemonic;
+    }
+  }
+
+  /**
+   * @throws SyntaxError
+   */
+  private void checkingHardware() throws SyntaxError {
+    for (Iterator<Mnemonic> iterator = mnemonics.iterator(); iterator.hasNext();) {
+      Mnemonic mnemonic = iterator.next();
+      if (!mnemonic.allowedHardware().contains(destination)) {
+        throw new SyntaxError(mnemonic.getLineNumber(),
+            String.format("the mnemonic \"%s\" with the argument \"%s\" is not availble on the choosen hardware \"%s\"",
+                mnemonic.getName(), mnemonic.getArgument() == null ? "" : mnemonic.getArgument(), destination.name()));
+      }
+    }
+  }
+
+  private void processJMP(Mnemonic mnemonic, Mnemonic predecessor) throws SyntaxError {
+    String label = mnemonic.getArgument();
+    if (!labels.containsKey(label)) {
+      throw new SyntaxError(mnemonic.getLineNumber(),
+          String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
+    }
+    int lineNumber = (Integer) labels.get(label);
+    int page = lineNumber / 16;
+    lineNumber = lineNumber % 16;
+    mnemonic.setArgument(Integer.toString(lineNumber));
+    mnemonic.checkArgument();
+    if ((predecessor != null) && (predecessor instanceof PAGE)) {
+      PAGE pageMne = (PAGE) predecessor;
+      if (pageMne.isCalculate()) {
+        pageMne.setArgument(Integer.toString(page));
+        pageMne.checkArgument();
+      }
+    }
+  }
+
+  private void processRJMP(int address, Mnemonic mnemonic) throws SyntaxError {
+    String label = mnemonic.getArgument();
+    if (!labels.containsKey(label)) {
+      throw new SyntaxError(mnemonic.getLineNumber(),
+          String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
+    }
+    int lineNumber = (Integer) labels.get(label);
+    lineNumber = address - lineNumber;
+    if (lineNumber < 0) {
+      throw new SyntaxError(mnemonic.getLineNumber(),
+          String.format("label %s defined after mnemonic \"%s\". JMP can only step back!", label, mnemonic.getName()));
+    }
+    mnemonic.setArgument(Integer.toString(lineNumber));
+    mnemonic.checkArgument();
+  }
+
+  private List<String> parseLine(int srcLineNumber, String srcLine) throws SyntaxError, IOException {
+    String line = srcLine.trim();
+    if (line.startsWith(".")) {
+      // process pre compiler part
+      String label = getLabel(line);
+      return precompiler(srcLineNumber, label);
+    } else {
+      if (inMacro) {
+        actualMacro.addLine(line);
+      } else {
+        if (line.startsWith(":")) {
+          String label = getLabel(line);
+          if (labels.containsKey(label)) {
+            throw new SyntaxError(srcLineNumber,
+                String.format("Label \"%s\" already definied in line %d.", label, labels.get(label)));
+          }
+          labels.put(label, lineNumber);
+        } else {
+          Mnemonic mnemonic = getMnemonic(line, srcLineNumber);
+          if (mnemonic != null) {
+            lineNumber++;
+            mnemonics.add(mnemonic);
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  private List<String> precompiler(int srcLineNumber, String value) throws IOException, SyntaxError {
+    String label = value.trim().toLowerCase();
+    String name = label;
+    if (label.indexOf(" ") > 0) {
+      name = label.substring(0, label.indexOf(" "));
+    }
+    if (INCLUDE_FILE.equals(name)) {
+      includeFile(srcLineNumber, label);
+    } else if (START_MACRO_DEFINITION.equals(name)) {
+      startMacroDefinition(srcLineNumber, label);
+      inMacro = true;
+    } else if (END_MACRO_DEFINITION.equalsIgnoreCase(label)) {
+      // end of macro definition
+      inMacro = false;
+      macros.put(actualMacro.getName(), actualMacro);
+    } else {
+      // seems to be a macro usage
+      name = name.substring(1);
+      String[] arguments = label.split(" ");
+      if (arguments.length < 1) {
+        throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
+      }
+      String[] args = null;
+      if (arguments.length > 1) {
+        args = Arrays.copyOfRange(arguments, 1, arguments.length);
+      }
+      Macro macro = macros.get(name);
+      if (macro == null) {
+        throw new SyntaxError(srcLineNumber, String.format("macro with name %s not define.", name));
+      }
+      List<String> macroMnemonics = macro.processMacro(args, srcLineNumber);
+      return macroMnemonics;
+    }
+    return null;
+  }
+
+  /**
+   * @param srcLineNumber
+   * @param label
+   * @throws IllegalArgument
+   */
+  private void startMacroDefinition(int srcLineNumber, String label) throws IllegalArgument {
+    // macro definition
+    String[] arguments = label.split(" ");
+    if (arguments.length < 2) {
+      throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
+    }
+    String macroName = arguments[1];
+    String[] args = null;
+    if (arguments.length > 2) {
+      args = Arrays.copyOfRange(arguments, 2, arguments.length);
+    }
+    actualMacro = new Macro(macroName, args);
+  }
+
+  /**
+   * @param srcLineNumber
+   * @param label
+   * @throws IllegalArgument
+   * @throws IOException
+   * @throws SyntaxError
+   */
+  private void includeFile(int srcLineNumber, String label) throws IllegalArgument, IOException, SyntaxError {
+    String[] arguments = label.split(" ");
+    if (arguments.length < 2) {
+      throw new IllegalArgument(srcLineNumber, "Need at least one argument for the filename to include.");
+    }
+    File includeFile = new File(includes, String.format("%s.tps", arguments[1]));
+    if (!includeFile.exists()) {
+      throw new IllegalArgument(srcLineNumber, String.format("include file not found: %s", includeFile.getName()));
+    }
+    System.out.println("=====");
+    System.out.printf("include file: %s \r\n", includeFile.getName());
+    SPSAssembler spsAssembler = new SPSAssembler();
+    spsAssembler.doWork(includeFile);
+    outputMnemonics(spsAssembler.getMnemonics());
+    outputLabels(spsAssembler);
+    outputMacros(spsAssembler);
+    System.out.println("=====");
+  }
+
+  private Mnemonic getMnemonic(String line, int srcLineNumber) throws SyntaxError {
+    String newLine = stripComments(line);
+    if (StringUtils.isNotEmpty(newLine)) {
+      Mnemonic mnemonic = MnemonicFactory.getMnemonic(newLine, srcLineNumber);
+      return mnemonic;
+    }
+    return null;
+  }
+
+  private String stripComments(String line) {
+    if (line.indexOf("*/") >= 0) {
+      inBlockComment = false;
+      return null;
+    }
+    if (inBlockComment) {
+      return null;
+    }
+    if (line.indexOf(";") >= 0) {
+      line = line.substring(0, line.indexOf(";")).trim();
+      return line;
+    }
+    if (line.startsWith("/*")) {
+      inBlockComment = true;
+      return null;
+    }
+    return line.trim();
+  }
+
+  private String getLabel(String line) {
+    line = stripComments(line);
+    return line;
+  }
+
+  private List<Mnemonic> getMnemonics() {
+    return mnemonics;
+  }
+
+  private Map<String, Macro> getMacros() {
+    return macros;
+  }
+
+  private Map<String, Integer> getLabels() {
+    return labels;
+  }
+}

+ 0 - 14
src/main/java/de/mcs/tools/sps/TEmulatorInternals.java

@@ -1,14 +0,0 @@
-/**
- * 
- */
-package de.mcs.tools.sps;
-
-/**
- * This interface is for getting the emualtor internal
- * 
- * @author w.klaas
- *
- */
-public interface TEmulatorInternals {
-
-}

+ 0 - 14
src/main/java/de/mcs/tools/sps/TEmulatorOutput.java

@@ -1,14 +0,0 @@
-/**
- * 
- */
-package de.mcs.tools.sps;
-
-/**
- * this is the interface of all Emulatoroutputs
- * 
- * @author w.klaas
- *
- */
-public interface TEmulatorOutput {
-
-}

+ 67 - 0
src/main/java/de/mcs/tools/sps/TPSTextOutputter.java

@@ -0,0 +1,67 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.annotations.SPSOutputter;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+
+/**
+ * @author w.klaas
+ *
+ */
+@SPSOutputter(name = "TPSTXT")
+public class TPSTextOutputter implements Outputter {
+
+  /**
+   * 
+   */
+  public TPSTextOutputter() {
+  }
+
+  /* (non-Javadoc)
+   * @see de.mcs.tools.sps.Outputter#output(java.util.List, java.io.OutputStream)
+   */
+  @Override
+  public void output(List<Mnemonic> mnemonics, OutputStream output) {
+    try (Writer writer = new BufferedWriter(new OutputStreamWriter(output))) {
+      writer.write("Addr   BD   Befehl   Daten     Kommentar\r\n");
+      int address = 0;
+      for (Mnemonic mnemonic : mnemonics) {
+        byte lowNibble = (byte) (mnemonic.getByte() & 0x0f);
+        byte highNibble = (byte) ((mnemonic.getByte() >> 4) & 0x0f);
+        writer.write(String.format("0x%03x  %02x   %4s     %4s      %s %s\r\n", address, mnemonic.getByte(),
+            nibbleToString(highNibble), nibbleToString(lowNibble), mnemonic.getName(),
+            StringUtils.isEmpty(mnemonic.getArgument()) ? "" : mnemonic.getArgument()));
+        address++;
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    } finally {
+    }
+  }
+
+  private String nibbleToString(byte lowNibble) {
+    StringBuilder b = new StringBuilder();
+    b.append((lowNibble & 0x08) > 0 ? "X" : "0");
+    b.append((lowNibble & 0x04) > 0 ? "X" : "0");
+    b.append((lowNibble & 0x02) > 0 ? "X" : "0");
+    b.append((lowNibble & 0x01) > 0 ? "X" : "0");
+    return b.toString();
+  }
+
+  @Override
+  public String getDefaultExtension() {
+    return ".txt";
+  }
+
+}

+ 22 - 0
src/main/java/de/mcs/tools/sps/annotations/SPSOutputter.java

@@ -0,0 +1,22 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target(TYPE)
+/**
+ * @author w.klaas
+ *
+ */
+public @interface SPSOutputter {
+
+  String name();
+
+}

+ 8 - 0
src/main/java/de/mcs/tools/sps/annotations/package-info.java

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

+ 19 - 0
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
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
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);
+
+}

+ 1 - 1
src/main/java/de/mcs/tools/sps/SPSCommand.java → src/main/java/de/mcs/tools/sps/emulator/SPSCommand.java

@@ -1,7 +1,7 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 import java.util.List;
 

+ 6 - 1
src/main/java/de/mcs/tools/sps/SPSCommandData.java → src/main/java/de/mcs/tools/sps/emulator/SPSCommandData.java

@@ -1,7 +1,7 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 /**
  * a single command data for the emulator
@@ -32,4 +32,9 @@ public interface SPSCommandData {
    */
   byte getCommandByte();
 
+  /**
+   * here the work should be done
+   */
+  EmulatorOutput doWork(EmulatorInput input, EmulatorInternals internals, EmulatorOutput output);
+
 }

+ 19 - 2
src/main/java/de/mcs/tools/sps/SPSCommandDataImpl.java → src/main/java/de/mcs/tools/sps/emulator/SPSCommandDataImpl.java

@@ -1,13 +1,13 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 /**
  * @author w.klaas
  *
  */
-public class SPSCommandDataImpl implements SPSCommandData {
+public abstract class SPSCommandDataImpl implements SPSCommandData {
 
   String name;
   String mnemonic;
@@ -80,4 +80,21 @@ public class SPSCommandDataImpl implements SPSCommandData {
     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);
+
 }

+ 5 - 1
src/main/java/de/mcs/tools/sps/SPSCommandImpl.java → src/main/java/de/mcs/tools/sps/emulator/SPSCommandImpl.java

@@ -1,7 +1,7 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -71,4 +71,8 @@ public class SPSCommandImpl implements SPSCommand {
     return this;
   }
 
+  @Override
+  public String toString() {
+    return String.format("%s (%s): %.2X", getMnemonic(), getName(), getCommandByte());
+  }
 }

+ 17 - 4
src/main/java/de/mcs/tools/sps/SPSEmulator.java → src/main/java/de/mcs/tools/sps/emulator/SPSEmulator.java

@@ -1,11 +1,11 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 import java.util.List;
 
-import de.mcs.tools.sps.exceptions.WrongProgramSizeException;
+import de.mcs.tools.sps.emulator.exceptions.WrongProgramSizeException;
 
 /**
  * @author w.klaas
@@ -68,8 +68,21 @@ public interface SPSEmulator {
   void stop();
 
   /**
+   * setting the input for the emulator
    * 
-   * @return the actual emualtor internals
+   * @param input
    */
-  TEmulatorInternals getInternals();
+  void setEmulatorInput(EmulatorInput input);
+
+  EmulatorInput getEmulatorInput();
+
+  /**
+   * 
+   * @return the actual emulator internals
+   */
+  EmulatorInternals getEmulatorInternals();
+
+  EmulatorOutput getEmulatorOutput();
+
+  SPSCommandData getCommandData(byte command);
 }

+ 2 - 2
src/main/java/de/mcs/tools/sps/TOutputCallback.java → src/main/java/de/mcs/tools/sps/emulator/TOutputCallback.java

@@ -1,7 +1,7 @@
 /**
  * 
  */
-package de.mcs.tools.sps;
+package de.mcs.tools.sps.emulator;
 
 /**
  * Interface for the callbacks on the emulator
@@ -10,5 +10,5 @@ package de.mcs.tools.sps;
  *
  */
 public interface TOutputCallback {
-  void onOutput(TEmulatorOutput output);
+  void onOutput(EmulatorOutput output);
 }

+ 141 - 0
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
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
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
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
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
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
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
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
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
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;

+ 1 - 1
src/main/java/de/mcs/tools/sps/exceptions/WrongProgramSizeException.java → src/main/java/de/mcs/tools/sps/emulator/exceptions/WrongProgramSizeException.java

@@ -1,7 +1,7 @@
 /**
  * 
  */
-package de.mcs.tools.sps.exceptions;
+package de.mcs.tools.sps.emulator.exceptions;
 
 /**
  * @author w.klaas

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

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

+ 26 - 0
src/main/java/de/mcs/tools/sps/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;

+ 38 - 0
src/main/java/de/mcs/tools/sps/exceptions/IllegalArgument.java

@@ -0,0 +1,38 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: SyntaxError.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/>
+ */
+package de.mcs.tools.sps.exceptions;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class IllegalArgument extends SyntaxError {
+
+  public IllegalArgument(String message) {
+    super(message);
+  }
+
+  public IllegalArgument(int lineNumber, String message) {
+    super(String.format("Illegal argument in line %d. %s", lineNumber, message));
+  }
+
+}

+ 38 - 0
src/main/java/de/mcs/tools/sps/exceptions/SyntaxError.java

@@ -0,0 +1,38 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: SyntaxError.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/>
+ */
+package de.mcs.tools.sps.exceptions;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class SyntaxError extends Exception {
+
+  public SyntaxError(String message) {
+    super(message);
+  }
+
+  public SyntaxError(int lineNumber, String message) {
+    super(String.format("Syntax error in line %d. %s", lineNumber, message));
+  }
+
+}

+ 0 - 8
src/main/java/de/mcs/tools/sps/exceptions/package-info.java

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

+ 0 - 288
src/main/java/de/mcs/tools/sps/holtec/HoltecEmulator.java

@@ -1,288 +0,0 @@
-/**
- * 
- */
-package de.mcs.tools.sps.holtec;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.mcs.tools.sps.AbstractEmulator;
-import de.mcs.tools.sps.SPSCommand;
-import de.mcs.tools.sps.SPSCommandData;
-import de.mcs.tools.sps.SPSCommandDataImpl;
-import de.mcs.tools.sps.SPSCommandImpl;
-import de.mcs.tools.sps.SPSEmulator;
-
-/**
- * @author w.klaas
- *
- */
-public class HoltecEmulator extends AbstractEmulator implements SPSEmulator {
-  private List<SPSCommand> commands = new ArrayList<>();
-  {
-    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_UP);
-    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_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().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();
-    datas.add(new SPSCommandDataImpl().setName("Wait 1ms").setMnemonic("D1M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 2ms").setMnemonic("D2M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 5ms").setMnemonic("D5M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 10ms").setMnemonic("D10M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 20ms").setMnemonic("D20M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 50ms").setMnemonic("D50M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 100ms").setMnemonic("D100M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 200ms").setMnemonic("D200M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 500ms").setMnemonic("D500M")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 1s").setMnemonic("D1S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 2s").setMnemonic("D2S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 5s").setMnemonic("D5S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 10s").setMnemonic("D10S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 20s").setMnemonic("D20S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 30s").setMnemonic("D30S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).setCommand(SPS_WAIT));
-    datas.add(new SPSCommandDataImpl().setName("Wait 60s").setMnemonic("D60S")
-        .setCommandByte((byte) SPS_WAIT.getCommandByte()).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 SPSCommandDataImpl().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().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().setName("B=A").setMnemonic("BEA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x01)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("C=A").setMnemonic("CEA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x02)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("D=A").setMnemonic("DEA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x03)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("Output=A").setMnemonic("OEA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x04)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("Output.1=A").setMnemonic("O1EA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x05)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("Output.2=A").setMnemonic("O2EA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x06)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("Output.3=A").setMnemonic("O3EA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x07)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().setName("Output.4=A").setMnemonic("O4EA")
-        .setCommandByte((byte) (SPS_EQUALS_A.getCommandByte() + 0x08)).setCommand(SPS_EQUALS_A));
-    datas.add(new SPSCommandDataImpl().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().setName("A=B").setMnemonic("AEB")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x01)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=C").setMnemonic("AEC")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x02)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=D").setMnemonic("AED")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x03)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=Input").setMnemonic("AEI")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x04)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=Input.1").setMnemonic("AEI1")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x05)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=Input.2").setMnemonic("AEI2")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x06)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=Input.3").setMnemonic("AEI3")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x07)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=Input.4").setMnemonic("AEI4")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x08)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().setName("A=AD.1").setMnemonic("AEA1")
-        .setCommandByte((byte) (SPS_A_INPUT.getCommandByte() + 0x09)).setCommand(SPS_A_INPUT));
-    datas.add(new SPSCommandDataImpl().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().setName("A=A+1").setMnemonic("INCA")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x01)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A-1").setMnemonic("DECA")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x02)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A+B").setMnemonic("ADDB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x03)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A-B").setMnemonic("SUBB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x04)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A*B").setMnemonic("MULB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x05)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A/B").setMnemonic("DIVB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x06)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A AND B").setMnemonic("ANDB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x07)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A OR B").setMnemonic("ORB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x08)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().setName("A=A XOR B").setMnemonic("XORB")
-        .setCommandByte((byte) (SPS_A_CALCULATE.getCommandByte() + 0x09)).setCommand(SPS_A_CALCULATE));
-    datas.add(new SPSCommandDataImpl().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().setName("Page " + Integer.toString(i)).setMnemonic("PG" + Integer.toString(i))
-              .setCommandByte((byte) (SPS_PAGE.getCommandByte() + i)).setCommand(SPS_PAGE));
-    }
-  }
-
-  private static SPSCommand SPS_JUMP_UP = new SPSCommandImpl().setName("Jump").setMnemonic("JMP")
-      .setCommandByte((byte) 0x90);
-  static {
-    for (int i = 0; i < 16; i++) {
-      SPS_JUMP_UP.getCommandDatas()
-          .add(new SPSCommandDataImpl().setName("Jump " + Integer.toString(i)).setMnemonic("JMP" + Integer.toString(i))
-              .setCommandByte((byte) (SPS_JUMP_UP.getCommandByte() + i)).setCommand(SPS_JUMP_UP));
-    }
-  }
-
-  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().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().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().setName("A>B").setMnemonic("AGRB")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x01)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("A<B").setMnemonic("ASMB")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x02)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("A=B").setMnemonic("AEQB")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x03)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.1=1").setMnemonic("IN11")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x04)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.2=1").setMnemonic("IN21")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x05)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.3=1").setMnemonic("IN31")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x06)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.4=1").setMnemonic("IN41")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x07)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.1=0").setMnemonic("IN10")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x08)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.2=0").setMnemonic("IN20")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x09)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.3=0").setMnemonic("IN30")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0A)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("In.4=0").setMnemonic("IN40")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0B)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("S1=0").setMnemonic("S10")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0C)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("S2=0").setMnemonic("S20")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0D)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().setName("S1=1").setMnemonic("S11")
-        .setCommandByte((byte) (SPS_SKIP_IF.getCommandByte() + 0x0E)).setCommand(SPS_SKIP_IF));
-    SPS_SKIP_IF.getCommandDatas().add(new SPSCommandDataImpl().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().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);
-  static {
-    SPS_RETURN.getCommandDatas().add(new SPSCommandDataImpl().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;
-  }
-
-}

+ 0 - 8
src/main/java/de/mcs/tools/sps/holtec/package-info.java

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

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/ADD.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * ADD: A = A + B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class ADD extends AbstractMnemonic implements Mnemonic {
+
+  public ADD(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x73;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/AEQB.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * AEQB: skip next command if A = B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class AEQB extends AbstractMnemonic implements Mnemonic {
+
+  public AEQB(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC3;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/AGTB.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * AGTB: skip next command if A > B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class AGTB extends AbstractMnemonic implements Mnemonic {
+
+  public AGTB(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC1;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/ALTB.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * ALTB: skip next command if A < B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class ALTB extends AbstractMnemonic implements Mnemonic {
+
+  public ALTB(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC2;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/AND.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * AND: A = A & B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class AND extends AbstractMnemonic implements Mnemonic {
+
+  public AND(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x77;
+  }
+
+}

+ 149 - 0
src/main/java/de/mcs/tools/sps/mnemonic/AbstractMnemonic.java

@@ -0,0 +1,149 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: AbstractMnemonic.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public abstract class AbstractMnemonic implements Mnemonic {
+
+  private String srcLine;
+  private String name;
+  private String argument;
+  private int lineNumber;
+
+  public static String getName(String line) {
+    String srcLine = line.trim();
+    if (srcLine.indexOf(" ") >= 0) {
+      String[] values = srcLine.split(" ");
+      return values[0].toUpperCase();
+    }
+    return srcLine.toUpperCase();
+  }
+
+  AbstractMnemonic(String line) throws SyntaxError {
+    this.srcLine = line.trim();
+    this.name = srcLine;
+    int pos = srcLine.indexOf(" ");
+    if (pos >= 0) {
+      this.name = srcLine.substring(0, pos).toUpperCase();
+      this.argument = srcLine.substring(pos).trim();
+    }
+  }
+
+  /**
+   * default implementation for checking no argument mnemonics
+   */
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isNotEmpty(argument)) {
+      throw new SyntaxError(getLineNumber(), String.format("%s has no argument.", this.getClass().getSimpleName()));
+    }
+  };
+
+  @Override
+  public String toString() {
+    return String.format("%s %s", name, StringUtils.isEmpty(argument) ? "" : argument);
+  }
+
+  /**
+   * @return the name
+   */
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * @return the argument
+   */
+  @Override
+  public String getArgument() {
+    return argument;
+  }
+
+  @Override
+  public void setArgument(String argument) {
+    this.argument = argument;
+  }
+
+  public int getArgumentAsNumber() throws IllegalArgument {
+    return getArgumentAsNumber(getArgument());
+  };
+
+  public int getArgumentAsNumber(String arg) throws IllegalArgument {
+    int value = 0;
+    try {
+      String argument = arg.toLowerCase();
+      if (argument.startsWith("0b")) {
+        value = Integer.parseInt(argument.substring(2), 2);
+      } else {
+        value = Integer.decode(argument);
+      }
+    } catch (Exception e) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not valid for %s.", arg, this.getClass().getSimpleName()));
+    }
+    return value;
+  };
+
+  public abstract int getByte();
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public int getLineNumber() {
+    return lineNumber;
+  }
+
+  @Override
+  public void setLineNumber(int lineNumber) {
+    this.lineNumber = lineNumber;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return false;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return false;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.allOf(HARDWARE.class);
+  }
+}

+ 119 - 0
src/main/java/de/mcs/tools/sps/mnemonic/BLDA.java

@@ -0,0 +1,119 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+//@formatter:off
+/**
+ * BLDA ADC#x: byte analog input #x to register A. x should be 1 or 2
+ * BLDA RC#x: byte rc receiver input #x to register A. x should be 1 or 2
+ * @author wklaa_000
+ *
+ */
+//@formatter:on
+public class BLDA extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+
+  enum INPUTSOURCE {
+    ANALOG, RC
+  };
+
+  INPUTSOURCE inputSource;
+
+  public BLDA(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+
+    String inputString = getArgument().toUpperCase();
+    if (inputString.startsWith("ADC")) {
+      inputSource = INPUTSOURCE.ANALOG;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(3));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range ADC1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else if (inputString.startsWith("RC")) {
+      inputSource = INPUTSOURCE.RC;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(2));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range RC1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else {
+      throw new SyntaxError(getLineNumber(),
+          String.format("unknown argument %s for %s.", this.getArgument(), this.getClass().getSimpleName()));
+    }
+  }
+
+  @Override
+  public int getByte() {
+    switch (inputSource) {
+    case ANALOG:
+      return 0xF0 + value;
+    case RC:
+      return 0xF2 + value;
+    }
+    return 0x00;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 120 - 0
src/main/java/de/mcs/tools/sps/mnemonic/BSTA.java

@@ -0,0 +1,120 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+//@formatter:off
+/**
+ * BSTA SRVx: output byte register A to servo #x. x should be 1 or 2
+ * BSTA PWMx: output byte register A to PWM #x. x should be 1 or 2
+ * 
+ * @author wklaa_000
+ *
+ */
+//@formatter:on
+public class BSTA extends AbstractMnemonic implements Mnemonic {
+
+  private static final int PWM = 0xf4;
+  private static final int SRV = 0xf6;
+  byte value;
+
+  enum OUTPUTSOURCE {
+    PWM, SRV
+  };
+
+  OUTPUTSOURCE inputSource;
+
+  public BSTA(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String inputString = getArgument().toUpperCase();
+    if (inputString.startsWith("PWM")) {
+      inputSource = OUTPUTSOURCE.PWM;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(3));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range PWM1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else if (inputString.startsWith("SRV")) {
+      inputSource = OUTPUTSOURCE.SRV;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(3));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range SRV1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("illegal argument %s in %s", getArgument(), this.getClass().getSimpleName()));
+    }
+  }
+
+  @Override
+  public int getByte() {
+    switch (inputSource) {
+    case PWM:
+      return PWM + value;
+    case SRV:
+      return SRV + value;
+    }
+    return 0x00;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/BSUBA.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * BSUBA: A = B - A
+ * 
+ * @author wklaa_000
+ *
+ */
+public class BSUBA extends AbstractMnemonic implements Mnemonic {
+
+  public BSUBA(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7D;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/BYTE.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * BYTE: A = A + 16 * B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class BYTE extends AbstractMnemonic implements Mnemonic {
+
+  public BYTE(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7C;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 84 - 0
src/main/java/de/mcs/tools/sps/mnemonic/CALL.java

@@ -0,0 +1,84 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * CALL #x: Jump to address #x + 16 * PAGE and return on RTR
+ * 
+ * @author wklaa_000
+ *
+ */
+public class CALL extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public CALL(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 0) || (myValue > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (myValue & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xD0 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return true;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+}

+ 88 - 0
src/main/java/de/mcs/tools/sps/mnemonic/CASB.java

@@ -0,0 +1,88 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * CASB #x: call to subroutine #x, #x in range 1..6
+ * 
+ * @author wklaa_000
+ *
+ */
+public class CASB extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public CASB(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 1) || (myValue > 6)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) ((myValue - 1) & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xE1 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/DEC.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * DEC: A = A - 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class DEC extends AbstractMnemonic implements Mnemonic {
+
+  public DEC(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x72;
+  }
+
+}

+ 77 - 0
src/main/java/de/mcs/tools/sps/mnemonic/DEQ0.java

@@ -0,0 +1,77 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * DEQ0 #x: Skip next command if digital input #x = 0. #x in range 1..4
+ * 
+ * @author wklaa_000
+ *
+ */
+public class DEQ0 extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+
+  public DEQ0(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    if (StringUtils.isEmpty(getArgument())) {
+      value = 0;
+    } else {
+      int myValue = getArgumentAsNumber();
+      if ((myValue < 1) || (myValue > 4)) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in range 1..4 for %s.", getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    }
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC8 + value;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+}

+ 77 - 0
src/main/java/de/mcs/tools/sps/mnemonic/DEQ1.java

@@ -0,0 +1,77 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * DEQ1 #x: Skip next command if digital input #x = 0. #x in range 1..4
+ * 
+ * @author wklaa_000
+ *
+ */
+public class DEQ1 extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+
+  public DEQ1(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    if (StringUtils.isEmpty(getArgument())) {
+      value = 0;
+    } else {
+      int myValue = getArgumentAsNumber();
+      if ((myValue < 1) || (myValue > 4)) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in range 1..4 for %s.", getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    }
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC4 + value;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+}

+ 88 - 0
src/main/java/de/mcs/tools/sps/mnemonic/DFSB.java

@@ -0,0 +1,88 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * DFSB #x: define subroutine #x, #x in range 1..6
+ * 
+ * @author wklaa_000
+ *
+ */
+public class DFSB extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public DFSB(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 1) || (myValue > 6)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) ((myValue - 1) & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xE8 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/DIV.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * DIV: A = A / B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class DIV extends AbstractMnemonic implements Mnemonic {
+
+  public DIV(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x76;
+  }
+
+}

+ 5 - 0
src/main/java/de/mcs/tools/sps/mnemonic/HARDWARE.java

@@ -0,0 +1,5 @@
+package de.mcs.tools.sps.mnemonic;
+
+public enum HARDWARE {
+  HOLTEK, ATMEGA8, ARDUINOSPS, TINYSPS
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/INC.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * INC: A = A + 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class INC extends AbstractMnemonic implements Mnemonic {
+
+  public INC(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x71;
+  }
+
+}

+ 84 - 0
src/main/java/de/mcs/tools/sps/mnemonic/JMP.java

@@ -0,0 +1,84 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * JMP #x: Jump to address #x + 16 * PAGE
+ * 
+ * @author wklaa_000
+ *
+ */
+public class JMP extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public JMP(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 0) || (myValue > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (myValue & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x90 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return true;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+}

+ 160 - 0
src/main/java/de/mcs/tools/sps/mnemonic/LDA.java

@@ -0,0 +1,160 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+// @formatter:off
+/**
+ * LDA ##x: Load directly, setting register A to a static value. x in range
+ * 0..15;
+ * LDA DIN: Load register A from Din
+ * LDA DINx: Load register A from Pin Din.X (x in range 1..4)
+ * LDA ADCx: Load register A from analog input X (x in range 1..2)
+ * LDA RCx: Load register A from rc input X (x in range 1..2)
+ * @author wklaa_000
+ *
+ */
+// @formatter:on
+public class LDA extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+
+  enum INPUTSOURCE {
+    DIRECT, DIGITAL, ANALOG, RC
+  };
+
+  INPUTSOURCE inputSource;
+
+  public LDA(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    inputSource = INPUTSOURCE.DIRECT;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    if (!getArgument().startsWith("#")) {
+      String inputString = getArgument().toUpperCase();
+      if (inputString.startsWith("DIN")) {
+        inputSource = INPUTSOURCE.DIGITAL;
+        if ("DIN".equalsIgnoreCase(inputString)) {
+          value = 0;
+        } else {
+          int myValue = Integer.parseInt(inputString.substring(3));
+          if ((myValue < 1) || (myValue > 4)) {
+            throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range DIN1..4 for %s.",
+                getArgument(), this.getClass().getSimpleName()));
+          }
+          value = (byte) (myValue & 0xFF);
+        }
+      } else if (inputString.startsWith("ADC")) {
+        inputSource = INPUTSOURCE.ANALOG;
+        int myValue = 0;
+        try {
+          myValue = Integer.parseInt(inputString.substring(3));
+        } catch (NumberFormatException e) {
+        }
+        if ((myValue < 1) || (myValue > 2)) {
+          throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range ADC1..2 for %s.",
+              getArgument(), this.getClass().getSimpleName()));
+        }
+        value = (byte) ((myValue - 1) & 0xFF);
+      } else if (inputString.startsWith("RC")) {
+        inputSource = INPUTSOURCE.RC;
+        int myValue = 0;
+        try {
+          myValue = Integer.parseInt(inputString.substring(2));
+        } catch (NumberFormatException e) {
+        }
+        if ((myValue < 1) || (myValue > 2)) {
+          throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range RC1..2 for %s.",
+              getArgument(), this.getClass().getSimpleName()));
+        }
+        value = (byte) ((myValue - 1) & 0xFF);
+      } else {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s sould start with \"#\".", getArgument(), this.getClass().getSimpleName()));
+      }
+    } else {
+      String argument = getArgument();
+      argument = argument.substring(1);
+      int myValue = getArgumentAsNumber(argument);
+      if ((myValue < 0) || (myValue > 15)) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) (myValue & 0xFF);
+    }
+  }
+
+  @Override
+  public int getByte() {
+    switch (inputSource) {
+    case DIRECT:
+      return 0x40 + value;
+    case DIGITAL:
+      return 0x64 + value;
+    case ANALOG:
+      return 0x69 + value;
+    case RC:
+      return 0x6b + value;
+    default:
+      return 0x40 + value;
+    }
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    switch (inputSource) {
+    case DIRECT:
+    case DIGITAL:
+    case ANALOG:
+      return super.allowedHardware();
+    case RC:
+      return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+    default:
+      return super.allowedHardware();
+    }
+  }
+
+}

+ 84 - 0
src/main/java/de/mcs/tools/sps/mnemonic/LOOPC.java

@@ -0,0 +1,84 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * LOOPC #x: if C > 0, C = C - 1 and JMP #x + 16 * PAGE
+ * 
+ * @author wklaa_000
+ *
+ */
+public class LOOPC extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public LOOPC(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 0) || (myValue > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (myValue & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xA0 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return true;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+}

+ 84 - 0
src/main/java/de/mcs/tools/sps/mnemonic/LOOPD.java

@@ -0,0 +1,84 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * LOOPD #x: if D > 0, D = D - 1 and JMP #x + 16 * PAGE
+ * 
+ * @author wklaa_000
+ *
+ */
+public class LOOPD extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean isLabel;
+
+  public LOOPD(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 0) || (myValue > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (myValue & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xB0 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return true;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/MOD.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * MOD: A = A % B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class MOD extends AbstractMnemonic implements Mnemonic {
+
+  public MOD(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7B;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 133 - 0
src/main/java/de/mcs/tools/sps/mnemonic/MOV.java

@@ -0,0 +1,133 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * MOV X, Y: setting register X to value of register Y (X=Y)
+ * 
+ * @author wklaa_000
+ *
+ */
+public class MOV extends AbstractMnemonic implements Mnemonic {
+
+  private enum MOVE_VALUE {
+    MOV_BA("A", "B", 0x51), MOV_CA("A", "C", 0x52), MOV_DA("A", "D", 0x53), MOV_EA("A", "E", 0x5d), MOV_FA("A", "F",
+        0x5e), MOV_AB("B", "A",
+            0x61), MOV_AC("C", "A", 0x62), MOV_AD("D", "A", 0x63), MOV_AE("E", "A", 0x6d), MOV_AF("F", "A", 0x6e);
+
+    String source;
+    String dest;
+    int value;
+
+    MOVE_VALUE(String source, String dest, int value) {
+      this.source = source;
+      this.dest = dest;
+      this.value = value;
+    }
+
+  };
+
+  byte output;
+  MOVE_VALUE actual;
+
+  public MOV(String line) throws SyntaxError {
+    super(line);
+    output = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    String argument = getArgument().trim();
+    if (StringUtils.isEmpty(argument)) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing arguments for %s.", this.getClass().getSimpleName()));
+    }
+    if (argument.indexOf(",") < 0) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing arguments for %s.", this.getClass().getSimpleName()));
+    }
+    int value = -1;
+    argument = argument.replace(",", "");
+    argument = argument.replace(" ", "");
+
+    actual = getActualMoveValue(argument);
+    if (actual != null) {
+      value = actual.value;
+    }
+
+    if (value < 0) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in enum for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+
+    output = (byte) (value & 0xFF);
+  }
+
+  private MOVE_VALUE getActualMoveValue(String argument) throws IllegalArgument {
+    try {
+      MOVE_VALUE moveValue = MOVE_VALUE.valueOf(String.format("MOV_%S", argument.trim()));
+      if (moveValue != null) {
+        return moveValue;
+      }
+    } catch (IllegalArgumentException e) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in enum for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    return null;
+  }
+
+  @Override
+  public int getByte() {
+    return output;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    switch (actual) {
+    case MOV_EA:
+    case MOV_FA:
+    case MOV_AE:
+    case MOV_AF:
+      return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+    default:
+      return super.allowedHardware();
+    }
+  }
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/MUL.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * MUL: A = A * B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class MUL extends AbstractMnemonic implements Mnemonic {
+
+  public MUL(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x75;
+  }
+
+}

+ 31 - 0
src/main/java/de/mcs/tools/sps/mnemonic/Mnemonic.java

@@ -0,0 +1,31 @@
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+public interface Mnemonic {
+
+  String getArgument();
+
+  String getName();
+
+  int getByte();
+
+  boolean isUsingPage();
+
+  void setLineNumber(int lineNumber);
+
+  boolean hasArgument();
+
+  void checkArgument() throws SyntaxError;
+
+  int getLineNumber();
+
+  boolean isLabel();
+
+  Set<HARDWARE> allowedHardware();
+
+  void setArgument(String string);
+
+}

+ 67 - 0
src/main/java/de/mcs/tools/sps/mnemonic/MnemonicFactory.java

@@ -0,0 +1,67 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Mnemonic.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class MnemonicFactory {
+
+  public MnemonicFactory() {
+  }
+
+  public static Mnemonic getMnemonic(String line, int lineNumber) throws SyntaxError {
+    String name = AbstractMnemonic.getName(line);
+    try {
+      Class<Mnemonic> mnemonicClass = (Class<Mnemonic>) Class.forName("de.mcs.tools.sps.mnemonic." + name);
+      Constructor<Mnemonic> constructor = mnemonicClass.getConstructor(String.class);
+      Mnemonic newInstance = constructor.newInstance(line);
+      newInstance.setLineNumber(lineNumber);
+      newInstance.checkArgument();
+      return newInstance;
+    } catch (SyntaxError e) {
+      throw e;
+    } catch (ClassNotFoundException e) {
+      throw new SyntaxError(String.format("syntax error in line %d. Illegal Mnemonic %s", lineNumber, name));
+    } catch (SecurityException e) {
+      e.printStackTrace();
+    } catch (NoSuchMethodException e) {
+      e.printStackTrace();
+    } catch (InstantiationException e) {
+      e.printStackTrace();
+    } catch (IllegalAccessException e) {
+      e.printStackTrace();
+    } catch (IllegalArgumentException e) {
+      e.printStackTrace();
+    } catch (InvocationTargetException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/NOP.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * NOP: No operation, nothing to do here.
+ * 
+ * @author wklaa_000
+ *
+ */
+public class NOP extends AbstractMnemonic implements Mnemonic {
+
+  public NOP(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x00;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/NOT.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * NOT: A = !A
+ * 
+ * @author wklaa_000
+ *
+ */
+public class NOT extends AbstractMnemonic implements Mnemonic {
+
+  public NOT(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7A;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/OR.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * OR: A = A | B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class OR extends AbstractMnemonic implements Mnemonic {
+
+  public OR(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x78;
+  }
+
+}

+ 94 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PAGE.java

@@ -0,0 +1,94 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PAGE #x: setting register PAGE #x, #x in range 0..15
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PAGE extends AbstractMnemonic implements Mnemonic {
+
+  byte value;
+  private boolean calculate;
+
+  public PAGE(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    calculate = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument();
+    if (":?".equals(argument)) {
+      calculate = true;
+      return;
+    }
+    int myValue = getArgumentAsNumber();
+    if ((myValue < 0) || (myValue > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (myValue & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x80 + value;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    if (value > 8) {
+      return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+    }
+    return super.allowedHardware();
+  }
+
+  public boolean isCalculate() {
+    return calculate;
+  }
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PEND.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PEND: program end
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PEND extends AbstractMnemonic implements Mnemonic {
+
+  public PEND(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xFF;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/POP.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * POP: Pop a value from the stack to register A.
+ * 
+ * @author wklaa_000
+ *
+ */
+public class POP extends AbstractMnemonic implements Mnemonic {
+
+  public POP(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x6F;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 86 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PORT.java

@@ -0,0 +1,86 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PORT, PORT #x: Output of register A. If no #x given, register a is output fully, otherwise the lowest bit of A will
+ * be shon on output #x. #x in range 1..4
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PORT extends AbstractMnemonic implements Mnemonic {
+
+  static int PORT = 0x10;
+
+  byte value;
+  boolean isPort;
+
+  public PORT(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isPort = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument();
+    if (argument.startsWith("#")) {
+      isPort = true;
+      String myArgument = argument.substring(1);
+      int myValue = getArgumentAsNumber(myArgument);
+      if ((myValue < 0) || (myValue > 15)) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in range 1..4 for %s.", argument, this.getClass().getSimpleName()));
+      }
+      value = (byte) (myValue & 0xFF);
+    } else {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("wrong argument for %s. Should start with '#'", argument, this.getClass().getSimpleName()));
+    }
+  }
+
+  @Override
+  public int getByte() {
+    return PORT + value;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PRG0.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PRG0: skip if PRG is 0
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PRG0 extends AbstractMnemonic implements Mnemonic {
+
+  public PRG0(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xCC;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PRG1.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PRG1: skip if PRG is 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PRG1 extends AbstractMnemonic implements Mnemonic {
+
+  public PRG1(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xCE;
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/PUSH.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * PUSH: Push register A on stack.
+ * 
+ * @author wklaa_000
+ *
+ */
+public class PUSH extends AbstractMnemonic implements Mnemonic {
+
+  public PUSH(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x5F;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/REST.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * REST: restart controller
+ * 
+ * @author wklaa_000
+ *
+ */
+public class REST extends AbstractMnemonic implements Mnemonic {
+
+  public REST(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xEF;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 89 - 0
src/main/java/de/mcs/tools/sps/mnemonic/RJMP.java

@@ -0,0 +1,89 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * RJMP: Jump back x steps. x in range 0..15
+ * 
+ * @author wklaa_000
+ *
+ */
+public class RJMP extends AbstractMnemonic implements Mnemonic {
+
+  int value;
+  private boolean isLabel;
+
+  public RJMP(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+    isLabel = false;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    String argument = getArgument().trim();
+    if (argument.startsWith(":")) {
+      isLabel = true;
+      return;
+    }
+    value = getArgumentAsNumber();
+    if ((value < 0) || (value > 15)) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    value = (byte) (value & 0xFF);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x30 + value;
+  }
+
+  @Override
+  public boolean isLabel() {
+    return isLabel;
+  }
+
+  public void setValue(int value) {
+    this.value = value;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/RTR.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * RTR: Return from sub routine.
+ * 
+ * @author wklaa_000
+ *
+ */
+public class RTR extends AbstractMnemonic implements Mnemonic {
+
+  public RTR(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xE0;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/SEL0.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * SEL0: skip if SEL is 0
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SEL0 extends AbstractMnemonic implements Mnemonic {
+
+  public SEL0(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xCD;
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/SEL1.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * SEL1: skip if SEL is 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SEL1 extends AbstractMnemonic implements Mnemonic {
+
+  public SEL1(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xCF;
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/SKIP0.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * SKIP0: skip next command if A = 0
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SKIP0 extends AbstractMnemonic implements Mnemonic {
+
+  public SKIP0(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xC0;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 151 - 0
src/main/java/de/mcs/tools/sps/mnemonic/STA.java

@@ -0,0 +1,151 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+//@formatter:off
+/**
+ * STA PORT: output register A to digital out, all bits
+ * STA DOUTx: output register A lowest bit to digital out x
+ * STA SRVx: output register A to SRV #x. x should be 1 or 2
+ * STA PWMx: output register A to PWM #x. x should be 1 or 2
+ * @author wklaa_000
+ *
+ */
+//@formatter:on
+public class STA extends AbstractMnemonic implements Mnemonic {
+
+  private static final int PWM = 0x59;
+  private static final int SRV = 0x5B;
+  byte value;
+  static int DOUT = 0x54;
+
+  enum OUTPUTSOURCE {
+    DIGITAL, PWM, SRV
+  };
+
+  OUTPUTSOURCE inputSource;
+
+  public STA(String line) throws SyntaxError {
+    super(line);
+    value = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    // int myValue = getArgumentAsNumber();
+    // if ((myValue < 1) || (myValue > 2)) {
+    // throw new IllegalArgument(getLineNumber(),
+    // String.format("argument %s is not in range 1..2 for %s.", getArgument(), this.getClass().getSimpleName()));
+    // }
+    // value = (byte) ((myValue - 1) & 0xFF);
+    String inputString = getArgument().toUpperCase();
+    if (inputString.startsWith("DOUT")) {
+      inputSource = OUTPUTSOURCE.DIGITAL;
+      if ("DOUT".equalsIgnoreCase(inputString)) {
+        value = 0;
+      } else {
+        int myValue = Integer.parseInt(inputString.substring(4));
+        if ((myValue < 1) || (myValue > 4)) {
+          throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range DOUT1..4 for %s.",
+              getArgument(), this.getClass().getSimpleName()));
+        }
+        value = (byte) (myValue & 0xFF);
+      }
+    } else if (inputString.startsWith("PWM")) {
+      inputSource = OUTPUTSOURCE.PWM;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(3));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range PWM1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else if (inputString.startsWith("SRV")) {
+      inputSource = OUTPUTSOURCE.SRV;
+      int myValue = 0;
+      try {
+        myValue = Integer.parseInt(inputString.substring(3));
+      } catch (NumberFormatException e) {
+      }
+      if ((myValue < 1) || (myValue > 2)) {
+        throw new IllegalArgument(getLineNumber(), String.format("argument %s is not in range SRV1..2 for %s.",
+            getArgument(), this.getClass().getSimpleName()));
+      }
+      value = (byte) ((myValue - 1) & 0xFF);
+    } else {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("illegal argument %s in %s", getArgument(), this.getClass().getSimpleName()));
+    }
+  }
+
+  @Override
+  public int getByte() {
+    switch (inputSource) {
+    case DIGITAL:
+      return DOUT + value;
+    case PWM:
+      return PWM + value;
+    case SRV:
+      return SRV + value;
+    }
+    return 0x00;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    switch (inputSource) {
+    case DIGITAL:
+    case PWM:
+      return super.allowedHardware();
+    case SRV:
+      return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+    default:
+      return super.allowedHardware();
+    }
+  }
+
+}

+ 43 - 0
src/main/java/de/mcs/tools/sps/mnemonic/SUB.java

@@ -0,0 +1,43 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * SUB: A = A - B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SUB extends AbstractMnemonic implements Mnemonic {
+
+  public SUB(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x74;
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/SWAP.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * SWAP: swapping register A and B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SWAP extends AbstractMnemonic implements Mnemonic {
+
+  public SWAP(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x50;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 51 - 0
src/main/java/de/mcs/tools/sps/mnemonic/TONE.java

@@ -0,0 +1,51 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * TONE: output tone with value from register A, A in range 36..108
+ * 
+ * @author wklaa_000
+ *
+ */
+public class TONE extends AbstractMnemonic implements Mnemonic {
+
+  public TONE(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0xF8;
+  }
+
+  @Override
+  public Set<HARDWARE> allowedHardware() {
+    return EnumSet.of(HARDWARE.ARDUINOSPS, HARDWARE.TINYSPS);
+  }
+
+}

+ 118 - 0
src/main/java/de/mcs/tools/sps/mnemonic/WAIT.java

@@ -0,0 +1,118 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: Not.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/>
+ */
+package de.mcs.tools.sps.mnemonic;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+/**
+ * WAIT: wating a little time. Argument should be in range 0..15 or one of this
+ * enumerations: 1ms, 2ms, 5ms, 10ms, 20ms, 50ms, 100ms, 200ms, 500ms, 1s, 2s,
+ * 5s, 10s, 20s, 30s, 60s
+ * 
+ * @author wklaa_000
+ *
+ */
+public class WAIT extends AbstractMnemonic implements Mnemonic {
+
+  private enum TIME_VALUE {
+    TIME_1MS("1ms", 0x00), TIME_2MS("2ms", 0x01), TIME_5MS("5ms", 0x02), TIME_10MS("10ms", 0x03), TIME_20MS("20ms",
+        0x04), TIME_50MS("50ms", 0x05), TIME_100MS("100ms", 0x06), TIME_200MS("200ms", 0x07), TIME_500MS("500ms",
+            0x08), TIME_1S("1s", 0x09), TIME_2S("2s", 0x0a), TIME_5S("5s",
+                0x0b), TIME_10S("10s", 0x0c), TIME_20S("20s", 0x0d), TIME_30S("30s", 0x0e), TIME_60S("60s", 0x0f);
+
+    String name;
+    int value;
+
+    TIME_VALUE(String name, int value) {
+      this.name = name;
+      this.value = value;
+    }
+
+  };
+
+  byte output;
+
+  public WAIT(String line) throws SyntaxError {
+    super(line);
+    output = 0;
+  }
+
+  @Override
+  public void checkArgument() throws SyntaxError {
+    if (StringUtils.isEmpty(getArgument())) {
+      throw new SyntaxError(getLineNumber(),
+          String.format("missing argument for %s.", this.getClass().getSimpleName()));
+    }
+    int value = 0;
+    String argument = getArgument();
+    if (isTimeArgument(argument)) {
+      value = getTime(argument);
+      if (value < 0) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in enum for %s.", getArgument(), this.getClass().getSimpleName()));
+      }
+    } else {
+      value = getArgumentAsNumber();
+      if ((value < 0) || (value > 15)) {
+        throw new IllegalArgument(getLineNumber(),
+            String.format("argument %s is not in range 0..15 for %s.", getArgument(), this.getClass().getSimpleName()));
+      }
+    }
+    output = (byte) (value & 0xFF);
+  }
+
+  private int getTime(String argument) throws IllegalArgument {
+    try {
+      TIME_VALUE timeValue = TIME_VALUE.valueOf(String.format("TIME_%S", argument.trim()));
+      if (timeValue != null) {
+        return timeValue.value;
+      }
+    } catch (IllegalArgumentException e) {
+      throw new IllegalArgument(getLineNumber(),
+          String.format("argument %s is not in enum for %s.", getArgument(), this.getClass().getSimpleName()));
+    }
+    return -1;
+  }
+
+  private boolean isTimeArgument(String argument) {
+    return argument.toLowerCase().indexOf("s") > 0;
+  }
+
+  @Override
+  public int getByte() {
+    return 0x20 + output;
+  }
+
+  @Override
+  public boolean isUsingPage() {
+    return false;
+  }
+
+  @Override
+  public boolean hasArgument() {
+    return true;
+  }
+
+}

Деякі файли не було показано, через те що забагато файлів було змінено