Ver Fonte

FEATURE: adding new stand alone SPS assembler with dropwizard microservice

Klaas, Wilfried há 4 anos atrás
pai
commit
e8f06dba95
100 ficheiros alterados com 6592 adições e 0 exclusões
  1. 39 0
      spsassembler/.classpath
  2. 23 0
      spsassembler/.project
  3. 72 0
      spsassembler/LICENSE
  4. 48 0
      spsassembler/SimpleServo.tps
  5. 34 0
      spsassembler/examples/Blink.hex
  6. 0 0
      spsassembler/examples/Blink.tps
  7. 0 0
      spsassembler/examples/Blink2.tps
  8. 0 0
      spsassembler/examples/BlinkKomment.tps
  9. 0 0
      spsassembler/examples/Blink_LOOP.tps
  10. 0 0
      spsassembler/examples/includes/macro_blink.tps
  11. 204 0
      spsassembler/pom.xml
  12. 58 0
      spsassembler/src/main/java/de/mcs/tools/sps/HEXTextOutputter.java
  13. 48 0
      spsassembler/src/main/java/de/mcs/tools/sps/IntelHEXOutputter.java
  14. 105 0
      spsassembler/src/main/java/de/mcs/tools/sps/Macro.java
  15. 21 0
      spsassembler/src/main/java/de/mcs/tools/sps/Outputter.java
  16. 652 0
      spsassembler/src/main/java/de/mcs/tools/sps/SPSAssembler.java
  17. 68 0
      spsassembler/src/main/java/de/mcs/tools/sps/TPSTextOutputter.java
  18. 22 0
      spsassembler/src/main/java/de/mcs/tools/sps/annotations/SPSOutputter.java
  19. 8 0
      spsassembler/src/main/java/de/mcs/tools/sps/annotations/package-info.java
  20. 37 0
      spsassembler/src/main/java/de/mcs/tools/sps/exceptions/HardwareException.java
  21. 38 0
      spsassembler/src/main/java/de/mcs/tools/sps/exceptions/IllegalArgument.java
  22. 38 0
      spsassembler/src/main/java/de/mcs/tools/sps/exceptions/SyntaxError.java
  23. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/ADD.java
  24. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/AEQB.java
  25. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/AGTB.java
  26. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/ALTB.java
  27. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/AND.java
  28. 149 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/AbstractMnemonic.java
  29. 119 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/BLDA.java
  30. 120 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/BSTA.java
  31. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/BSUBA.java
  32. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/BYTE.java
  33. 84 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/CALL.java
  34. 88 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/CASB.java
  35. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/DEC.java
  36. 77 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/DEQ0.java
  37. 77 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/DEQ1.java
  38. 88 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/DFSB.java
  39. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/DIV.java
  40. 5 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/HARDWARE.java
  41. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/INC.java
  42. 84 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/JMP.java
  43. 160 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/LDA.java
  44. 84 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/LOOPC.java
  45. 84 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/LOOPD.java
  46. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/MOD.java
  47. 133 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/MOV.java
  48. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/MUL.java
  49. 31 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/Mnemonic.java
  50. 67 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/MnemonicFactory.java
  51. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/NOP.java
  52. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/NOT.java
  53. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/OR.java
  54. 94 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PAGE.java
  55. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PEND.java
  56. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/POP.java
  57. 86 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PORT.java
  58. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PRG0.java
  59. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PRG1.java
  60. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/PUSH.java
  61. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/REST.java
  62. 89 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/RJMP.java
  63. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/RTR.java
  64. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SEL0.java
  65. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SEL1.java
  66. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SHL.java
  67. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SHR.java
  68. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SKIP0.java
  69. 151 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/STA.java
  70. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SUB.java
  71. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SWAP.java
  72. 51 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/TONE.java
  73. 118 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/WAIT.java
  74. 43 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/XOR.java
  75. 26 0
      spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/package-info.java
  76. 38 0
      spsassembler/src/main/java/de/mcs/tools/sps/service/AsmApplication.java
  77. 14 0
      spsassembler/src/main/java/de/mcs/tools/sps/service/AsmConfiguration.java
  78. 19 0
      spsassembler/src/main/java/de/mcs/tools/sps/service/AsmHealthCheck.java
  79. 63 0
      spsassembler/src/main/java/de/mcs/tools/sps/service/AsmResource.java
  80. 54 0
      spsassembler/src/main/java/de/mcs/tools/sps/service/model/AsmModel.java
  81. 94 0
      spsassembler/src/main/java/de/mcs/tools/sps/utils/IntelHex.java
  82. 8 0
      spsassembler/src/main/java/de/mcs/tools/sps/utils/package-info.java
  83. 93 0
      spsassembler/src/main/java/de/mcs/utils/Kryption.java
  84. 40 0
      spsassembler/src/main/java/de/mcs/utils/Logger.java
  85. 69 0
      spsassembler/src/main/java/de/mcs/utils/Macro.java
  86. 119 0
      spsassembler/src/main/java/de/mcs/utils/Macros.java
  87. 13 0
      spsassembler/src/main/java/de/mcs/utils/Singleton.java
  88. 39 0
      spsassembler/src/main/resources/log4j.xml
  89. 32 0
      spsassembler/src/test/java/de/mcs/tools/TestIntelHex.java
  90. 83 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestBLDA.java
  91. 85 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestBSRV.java
  92. 68 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestCALL.java
  93. 123 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestDEQ.java
  94. 68 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestJMP.java
  95. 164 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLDA.java
  96. 68 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLOOPC.java
  97. 68 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLOOPD.java
  98. 102 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestMOV.java
  99. 54 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestNOP.java
  100. 64 0
      spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestPAGE.java

+ 39 - 0
spsassembler/.classpath

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

+ 23 - 0
spsassembler/.project

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

+ 72 - 0
spsassembler/LICENSE

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

+ 48 - 0
spsassembler/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

+ 34 - 0
spsassembler/examples/Blink.hex

@@ -0,0 +1,34 @@
+:0800000015271A27341F2710F1
+:08000800271F27102738000014
+:080010000000000000000000E8
+:080018000000000000000000E0
+:080020000000000000000000D8
+:080028000000000000000000D0
+:080030000000000000000000C8
+:080038000000000000000000C0
+:080040000000000000000000B8
+:080048000000000000000000B0
+:080050000000000000000000A8
+:080058000000000000000000A0
+:08006000000000000000000098
+:08006800000000000000000090
+:08007000000000000000000088
+:08007800000000000000000080
+:08008000000000000000000078
+:08008800000000000000000070
+:08009000000000000000000068
+:08009800000000000000000060
+:0800A000000000000000000058
+:0800A800000000000000000050
+:0800B000000000000000000048
+:0800B800000000000000000040
+:0800C000000000000000000038
+:0800C800000000000000000030
+:0800D000000000000000000028
+:0800D800000000000000000020
+:0800E000000000000000000018
+:0800E800000000000000000010
+:0800F000000000000000000008
+:0800F800000000000000000000
+:06010000E81F271027E0B4
+:00000001FF

+ 0 - 0
examples/Blink.tps → spsassembler/examples/Blink.tps


+ 0 - 0
examples/Blink2.tps → spsassembler/examples/Blink2.tps


+ 0 - 0
examples/BlinkKomment.tps → spsassembler/examples/BlinkKomment.tps


+ 0 - 0
examples/Blink_LOOP.tps → spsassembler/examples/Blink_LOOP.tps


+ 0 - 0
examples/includes/macro_blink.tps → spsassembler/examples/includes/macro_blink.tps


+ 204 - 0
spsassembler/pom.xml

@@ -0,0 +1,204 @@
+<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>de.mcs.tools.sps</groupId>
+	<artifactId>spsassembler</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>${project.groupId}:${project.artifactId}</name>
+	<description>SPS Assembler wirtten in java.</description>
+	<url>http://www.wk-music.de</url>
+	<licenses>
+		<license>
+			<name>The Apache License, Version 2.0</name>
+			<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+		</license>
+	</licenses>
+
+	<developers>
+		<developer>
+			<name>Wilfried Klaas</name>
+			<email>w.klaas@gmx.de</email>
+			<organization>MCS</organization>
+			<organizationUrl>http://www.wk-music.de</organizationUrl>
+		</developer>
+	</developers>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<timestamp>${maven.build.timestamp}</timestamp>
+		<maven.build.timestamp.format>dd.mm.yyyy HH:mm</maven.build.timestamp.format>
+		<jackson.version>2.9.6</jackson.version>
+		<jersey.client.version>2.25.1</jersey.client.version>
+	</properties>
+
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>io.dropwizard</groupId>
+				<artifactId>dropwizard-dependencies</artifactId>
+				<version>2.1.0-SNAPSHOT</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+	<build>
+		<plugins>
+			<plugin>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.3</version>
+				<configuration>
+					<source>1.8</source>
+					<target>1.8</target>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>2.19.1</version>
+				<dependencies>
+					<dependency>
+						<groupId>org.junit.platform</groupId>
+						<artifactId>junit-platform-surefire-provider</artifactId>
+						<version>1.1.0</version>
+					</dependency>
+					<dependency>
+						<groupId>org.junit.jupiter</groupId>
+						<artifactId>junit-jupiter-engine</artifactId>
+						<version>5.1.0</version>
+					</dependency>
+				</dependencies>
+				<configuration>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.6</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<mainClass>de.mcs.tools.sps.SPSAssembler</mainClass>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+							<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+						</manifest>
+						<manifestEntries>
+							<Build-Time>${maven.build.timestamp}</Build-Time>
+							<Application-Name>SPSAssembler</Application-Name>
+							<Application-Update-Id>72</Application-Update-Id>
+							<Application-Update-Url>http\://wkla.no-ip.biz/downloader/version.php?ID\=73</Application-Update-Url>
+							<Application-Url>http\://wkla.no-ip.biz/</Application-Url>
+							<Implementation-Vendor>MCS, Media Computer Software</Implementation-Vendor>
+						</manifestEntries>
+					</archive>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-shade-plugin</artifactId>
+				<version>2.4.1</version>
+				<configuration>
+					<createDependencyReducedPom>true</createDependencyReducedPom>
+					<filters>
+						<filter>
+							<artifact>*:*</artifact>
+							<excludes>
+								<exclude>META-INF/*.SF</exclude>
+								<exclude>META-INF/*.DSA</exclude>
+								<exclude>META-INF/*.RSA</exclude>
+							</excludes>
+						</filter>
+					</filters>
+				</configuration>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>shade</goal>
+						</goals>
+						<configuration>
+							<transformers>
+								<transformer
+									implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
+								<transformer
+									implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+									<mainClass>de.mcs.tools.sps.service.AsmApplication</mainClass>
+								</transformer>
+							</transformers>
+						</configuration>
+						<!-- <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> 
+							<mainClass></mainClass> </transformer> </transformers> </configuration> -->
+					</execution>
+				</executions>
+			</plugin>
+			<!-- <plugin> <groupId>com.akathist.maven.plugins.launch4j</groupId> <artifactId>launch4j-maven-plugin</artifactId> 
+				<version>1.7.24</version> <executions> <execution> <id>l4j-clui</id> <phase>package</phase> 
+				<goals> <goal>launch4j</goal> </goals> <configuration> <dontWrapJar>false</dontWrapJar> 
+				<headerType>console</headerType> <jar>${project.build.directory}/${project.artifactId}-${project.version}.jar</jar> 
+				<outfile>${project.build.directory}/MCSSPSTools.exe</outfile> <downloadUrl>http://java.com/download</downloadUrl> 
+				<classPath> <mainClass>com.howtodoinjava.ApplicationMain</mainClass> <preCp>anything</preCp> 
+				</classPath> <icon>src\main\resources\MSA.ico</icon> <jre> <path>/jre</path> 
+				<minVersion>1.8.0</minVersion> <jdkPreference>jreOnly</jdkPreference> </jre> 
+				<versionInfo> <fileVersion>1.0.0.0</fileVersion> <txtFileVersion>${project.version}</txtFileVersion> 
+				<fileDescription>${project.name}</fileDescription> <copyright>2018 MCS</copyright> 
+				<productVersion>1.0.0.0</productVersion> <txtProductVersion>1.0.0.0</txtProductVersion> 
+				<productName>${project.name}</productName> <companyName>MCS</companyName> 
+				<internalName>MCSSPSTools</internalName> <originalFilename>MCSSPSTools.exe</originalFilename> 
+				</versionInfo> </configuration> </execution> </executions> </plugin> -->
+		</plugins>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>io.dropwizard</groupId>
+			<artifactId>dropwizard-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.sourceforge.jmeasurement2</groupId>
+			<artifactId>MCSUtils</artifactId>
+			<version>1.0.152</version>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+			<version>5.1.0</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>2.5</version>
+		</dependency>
+		<dependency>
+			<groupId>net.sourceforge.jmeasurement2</groupId>
+			<artifactId>JMeasurement</artifactId>
+			<version>1.1.225</version>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>1.2.17</version>
+		</dependency>
+		<dependency>
+			<groupId>com.martiansoftware</groupId>
+			<artifactId>jsap</artifactId>
+			<version>2.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+			<version>3.4</version>
+		</dependency>
+		<dependency>
+			<groupId>org.reflections</groupId>
+			<artifactId>reflections</artifactId>
+			<version>0.9.10</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>27.0.1-jre</version>
+		</dependency>
+	</dependencies>
+</project>

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

@@ -0,0 +1,58 @@
+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";
+  }
+
+}

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

@@ -0,0 +1,48 @@
+package de.mcs.tools.sps;
+/**
+ * 
+ */
+
+
+import java.io.OutputStream;
+import java.util.List;
+
+import de.mcs.tools.sps.annotations.SPSOutputter;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+import de.mcs.tools.sps.utils.IntelHex;
+
+/**
+ * @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
spsassembler/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
spsassembler/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();
+
+}

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

@@ -0,0 +1,652 @@
+/**
+ * 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.HardwareException;
+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.DFSB;
+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.NOP;
+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 File destinationFile;
+  private static String outputFormat;
+  private static File includes;
+  private static Outputter outputter;
+  private static String destinationStr;
+
+  private HARDWARE destination;
+  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 List<Integer> lineNumbers;
+
+  @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) {
+    destinationStr = value;
+  }
+
+  @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 {
+      HARDWARE destination = HARDWARE.HOLTEK;
+
+      CommandlineProcessor.processCommandline(SPSAssembler.class, args);
+
+      try {
+        destination = HARDWARE.valueOf((destinationStr.toUpperCase()));
+      } catch (IllegalArgumentException e) {
+        throw new HardwareException(String.format("Hardware %s unknow for this assembler", destinationStr));
+      }
+
+      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.printf("hardware: %s \r\n", destination.name());
+      if (includes != null) {
+        System.out.printf("includes foldes: %s \r\n", includes.getCanonicalPath());
+      }
+      System.out.println();
+
+      try {
+        SPSAssembler spsAssembler = new SPSAssembler();
+        spsAssembler.setDestination(destination);
+        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.getConstructor().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<>();
+    lineNumbers = new ArrayList<>();
+
+    inMacro = false;
+    actualMacro = null;
+    macros = new HashMap<>();
+    destination = HARDWARE.HOLTEK;
+  }
+
+  public void doWork(File source) throws IOException, SyntaxError {
+    System.out.printf("start parsing file: %s\r\n", source.getName());
+
+    List<String> sourceFile = Files.readAllLines(source.toPath(), Charset.forName("UTF-8"));
+    doCompile(sourceFile);
+  }
+
+  public void doWork(String source) throws IOException, SyntaxError {
+    System.out.printf("start parsing source string: %s\r\n", source);
+
+    List<String> sourceFile = new ArrayList<>();
+    source.lines().forEach(l -> sourceFile.add(l));
+    doCompile(sourceFile);
+  }
+
+  public void doCompile(List<String> source) throws SyntaxError, IOException {
+    ArrayList<String> sourceFile = new ArrayList<>();
+    sourceFile.addAll(source);
+    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();
+
+    calculatingJumps();
+
+    paddingSubroutines();
+
+    // checking size of program
+    checkingProgramSize();
+  }
+
+  private void paddingSubroutines() throws SyntaxError {
+    // checking location of Subroutines
+    if (getDestination().equals(HARDWARE.ARDUINOSPS) || getDestination().equals(HARDWARE.TINYSPS)) {
+      int position = 0;
+      boolean foundSub = false;
+      for (int i = 0; i < mnemonics.size(); i++) {
+        Mnemonic mnemonic = mnemonics.get(i);
+        if (mnemonic instanceof DFSB) {
+          foundSub = true;
+          break;
+        }
+        position++;
+      }
+      if (foundSub && (position < 256)) {
+        int count = 256 - position;
+        for (int i = 0; i < count; i++) {
+          mnemonics.add(position, new NOP(""));
+          lineNumbers.add(position, -1);
+        }
+      }
+    }
+  }
+
+  private void checkingProgramSize() throws HardwareException {
+    int prgSize = 0;
+    boolean usingDFSB = false;
+    for (Mnemonic mnemonic : mnemonics) {
+      if (mnemonic instanceof DFSB) {
+        usingDFSB = true;
+        break;
+      }
+      prgSize++;
+    }
+    int maxSize = 128;
+    switch (getDestination()) {
+    case ARDUINOSPS:
+    case TINYSPS:
+    case ATMEGA8:
+      maxSize = 256;
+      break;
+    default:
+      break;
+    }
+    if (prgSize > maxSize) {
+      throw new HardwareException(
+          String.format("Program exceeding size (%d) for hardeware %s (%d).", prgSize, getDestination(), maxSize));
+    }
+
+    switch (getDestination()) {
+    case ARDUINOSPS:
+      maxSize = 1024;
+    case TINYSPS:
+      maxSize = 512;
+      break;
+    default:
+      break;
+    }
+    if (mnemonics.size() > maxSize) {
+      throw new HardwareException(
+          String.format("Program exceeding size (%d) for hardeware %s (%d).", prgSize, getDestination(), maxSize));
+    }
+  }
+
+  private void calculatingJumps() throws SyntaxError {
+    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(getDestination())) {
+        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(),
+                getDestination().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);
+            lineNumbers.add(srcLineNumber);
+          }
+        }
+      }
+    }
+    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;
+  }
+
+  public List<Mnemonic> getMnemonics() {
+    return mnemonics;
+  }
+
+  private Map<String, Macro> getMacros() {
+    return macros;
+  }
+
+  private Map<String, Integer> getLabels() {
+    return labels;
+  }
+
+  /**
+   * @return the destination
+   */
+  public HARDWARE getDestination() {
+    return destination;
+  }
+
+  /**
+   * @param destination
+   *                      the destination to set
+   */
+  public void setDestination(HARDWARE destination) {
+    this.destination = destination;
+  }
+
+  public List<Integer> getLines() {
+    return lineNumbers;
+  }
+}

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

@@ -0,0 +1,68 @@
+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
spsassembler/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
spsassembler/src/main/java/de/mcs/tools/sps/annotations/package-info.java

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

+ 37 - 0
spsassembler/src/main/java/de/mcs/tools/sps/exceptions/HardwareException.java

@@ -0,0 +1,37 @@
+/**
+ * MCS Media Computer Software
+ * Copyright 2018 by Wilfried Klaas
+ * Project: SPSEmulator
+ * File: HardwareException.java
+ * EMail: W.Klaas@gmx.de
+ * Created: 07.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.exceptions;
+
+/**
+ * @author wklaa_000
+ *
+ */
+public class HardwareException extends SyntaxError {
+
+  public HardwareException(int lineNumber, String message) {
+    super(lineNumber, message);
+  }
+
+  public HardwareException(String message) {
+    super(message);
+  }
+}

+ 38 - 0
spsassembler/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
spsassembler/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));
+  }
+
+}

+ 43 - 0
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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 0..15 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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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;
+  }
+
+}

+ 43 - 0
spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SHL.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;
+
+/**
+ * SHR: A = A >> 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SHL extends AbstractMnemonic implements Mnemonic {
+
+  public SHL(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7F;
+  }
+
+}

+ 43 - 0
spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/SHR.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;
+
+/**
+ * SHR: A = A >> 1
+ * 
+ * @author wklaa_000
+ *
+ */
+public class SHR extends AbstractMnemonic implements Mnemonic {
+
+  public SHR(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x7E;
+  }
+
+}

+ 51 - 0
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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
spsassembler/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;
+  }
+
+}

+ 43 - 0
spsassembler/src/main/java/de/mcs/tools/sps/mnemonic/XOR.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;
+
+/**
+ * XOR: A = A ^ B
+ * 
+ * @author wklaa_000
+ *
+ */
+public class XOR extends AbstractMnemonic implements Mnemonic {
+
+  public XOR(String line) throws SyntaxError {
+    super(line);
+  }
+
+  @Override
+  public int getByte() {
+    return 0x79;
+  }
+
+}

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

+ 38 - 0
spsassembler/src/main/java/de/mcs/tools/sps/service/AsmApplication.java

@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.service;
+
+import io.dropwizard.Application;
+import io.dropwizard.setup.Bootstrap;
+import io.dropwizard.setup.Environment;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class AsmApplication extends Application<AsmConfiguration> {
+
+  public static void main(String[] args) throws Exception {
+    new AsmApplication().run(args);
+  }
+
+  @Override
+  public String getName() {
+    return "sps-assembler";
+  }
+
+  @Override
+  public void initialize(Bootstrap<AsmConfiguration> bootstrap) {
+    // nothing to do yet
+  }
+
+  @Override
+  public void run(AsmConfiguration config, Environment environment) throws Exception {
+    final AsmResource resource = new AsmResource();
+    final AsmHealthCheck healthCheck = new AsmHealthCheck();
+    environment.healthChecks().register("template", healthCheck);
+    environment.jersey().register(resource);
+  }
+
+}

+ 14 - 0
spsassembler/src/main/java/de/mcs/tools/sps/service/AsmConfiguration.java

@@ -0,0 +1,14 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.service;
+
+import io.dropwizard.Configuration;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class AsmConfiguration extends Configuration {
+
+}

+ 19 - 0
spsassembler/src/main/java/de/mcs/tools/sps/service/AsmHealthCheck.java

@@ -0,0 +1,19 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.service;
+
+import com.codahale.metrics.health.HealthCheck;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class AsmHealthCheck extends HealthCheck {
+
+  @Override
+  protected Result check() throws Exception {
+    return Result.healthy();
+  }
+
+}

+ 63 - 0
spsassembler/src/main/java/de/mcs/tools/sps/service/AsmResource.java

@@ -0,0 +1,63 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.service;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+
+import com.codahale.metrics.annotation.Timed;
+
+import de.mcs.tools.sps.IntelHEXOutputter;
+import de.mcs.tools.sps.Outputter;
+import de.mcs.tools.sps.SPSAssembler;
+import de.mcs.tools.sps.exceptions.HardwareException;
+import de.mcs.tools.sps.mnemonic.HARDWARE;
+import de.mcs.tools.sps.mnemonic.Mnemonic;
+import de.mcs.tools.sps.service.model.AsmModel;
+
+/**
+ * @author w.klaas
+ *
+ */
+@Path("/assemble")
+@Produces(MediaType.APPLICATION_JSON)
+public class AsmResource {
+
+  @POST
+  @Timed
+  public AsmModel assemble(AsmModel model) throws HardwareException {
+    try {
+      HARDWARE destination = HARDWARE.HOLTEK;
+
+      try {
+        destination = HARDWARE.valueOf((model.getHardware().toUpperCase()));
+      } catch (IllegalArgumentException e) {
+        throw new HardwareException(String.format("Hardware %s unknown for this assembler", model.getHardware()));
+      }
+
+      SPSAssembler spsAssembler = new SPSAssembler();
+      spsAssembler.setDestination(destination);
+      spsAssembler.doWork(model.getContent());
+
+      List<Mnemonic> mnemonics = spsAssembler.getMnemonics();
+      ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+      Outputter outputter = new IntelHEXOutputter();
+      outputter.output(mnemonics, output);
+
+      model.setHexfile(new String(output.toByteArray()));
+
+      return model;
+    } catch (Exception e) {
+      throw new WebApplicationException(e.getMessage(), e);
+    }
+  }
+
+}

+ 54 - 0
spsassembler/src/main/java/de/mcs/tools/sps/service/model/AsmModel.java

@@ -0,0 +1,54 @@
+/**
+ * 
+ */
+package de.mcs.tools.sps.service.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author w.klaas
+ *
+ */
+public class AsmModel {
+  @JsonProperty
+  private String name;
+  @JsonProperty
+  private String hardware;
+  @JsonProperty
+  private String content;
+  @JsonProperty
+  private String hexfile;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getHardware() {
+    return hardware;
+  }
+
+  public void setHardware(String hardware) {
+    this.hardware = hardware;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  public void setContent(String content) {
+    this.content = content;
+  }
+
+  public String getHexfile() {
+    return hexfile;
+  }
+
+  public void setHexfile(String hexfile) {
+    this.hexfile = hexfile;
+  }
+
+}

+ 94 - 0
spsassembler/src/main/java/de/mcs/tools/sps/utils/IntelHex.java

@@ -0,0 +1,94 @@
+/**
+ * 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.sps.utils;
+
+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]));
+				crc += data[i];
+				address++;
+			}
+			if (data.length > 0) {
+				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++;
+		crc = crc & 0x00FF;
+		writer.append(String.format("%02X", crc));
+		writer.append("\r\n");
+		return crc;
+	}
+}

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

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

+ 93 - 0
spsassembler/src/main/java/de/mcs/utils/Kryption.java

@@ -0,0 +1,93 @@
+package de.mcs.utils;
+/**
+ * 
+ */
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.GeneralSecurityException;
+import java.util.Base64;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+/**
+ * @author wklaa_000
+ */
+public class Kryption {
+
+  /**
+   * main method for external encryption of passwords. Needed for the setup.
+   * 
+   * @param args
+   *          index 0 will be the password to encrypt
+   */
+  public static void main(String[] args) {
+    if (args.length > 0) {
+      try {
+        System.out.println(Kryption.encrypt(args[0]));
+        System.exit(0);
+      } catch (UnsupportedEncodingException | GeneralSecurityException e) {
+        e.printStackTrace();
+        System.exit(-1);
+      }
+    }
+    System.exit(-1);
+  }
+
+  private static final char[] PASSWORD = "fgagwetizxmapoywnoouh".toCharArray();
+  private static final byte[] SALT = { (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12, (byte) 0xde, (byte) 0x33,
+      (byte) 0x10, (byte) 0x12, };
+
+  /**
+   * encrypt this string.
+   * 
+   * @param string
+   *          the string to encrypt
+   * @return the encrypted and mime coded string.
+   * @throws GeneralSecurityException
+   *           if something goes wrong.
+   * @throws UnsupportedEncodingException
+   *           if something goes wrong.
+   */
+  public static String encrypt(String string) throws GeneralSecurityException, UnsupportedEncodingException {
+    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
+    SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
+    Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
+    pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
+    return base64Encode(pbeCipher.doFinal(string.getBytes("UTF-8")));
+  }
+
+  private static String base64Encode(byte[] bytes) {
+    // NB: This class is internal, and you probably should use another impl
+    return Base64.getEncoder().encodeToString(bytes);
+  }
+
+  /**
+   * decrypt this string.
+   * 
+   * @param string
+   *          the string to decrypt
+   * @return the mime decoded and decrypted string.
+   * @throws GeneralSecurityException
+   *           if something goes wrong.
+   * @throws IOException
+   *           if something goes wrong.
+   */
+  public static String decrypt(String string) throws GeneralSecurityException, IOException {
+    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
+    SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
+    Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
+    pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
+    return new String(pbeCipher.doFinal(base64Decode(string)), "UTF-8");
+  }
+
+  private static byte[] base64Decode(String property) throws IOException {
+    // NB: This class is internal, and you probably should use another impl
+    return Base64.getDecoder().decode(property);
+  }
+
+}

+ 40 - 0
spsassembler/src/main/java/de/mcs/utils/Logger.java

@@ -0,0 +1,40 @@
+/**
+ * 
+ */
+package de.mcs.utils;
+
+/**
+ * @author w.klaas
+ */
+public class Logger {
+
+  public static Logger getLogger(Class<?> class1) {
+    return new Logger(class1);
+  }
+
+  private org.apache.log4j.Logger logger;
+
+  public Logger(Class<?> class1) {
+    logger = org.apache.log4j.Logger.getLogger(class1);
+  }
+
+  public void debug(String string, Object... args) {
+    logger.debug(String.format(string, args));
+  }
+
+  public void info(String string, Object... args) {
+    logger.info(String.format(string, args));
+  }
+
+  public void error(String string, Object... args) {
+    logger.error(String.format(string, args));
+  }
+
+  public void error(Throwable t, String string, Object... args) {
+    logger.error(String.format(string, args), t);
+  }
+
+  public void error(Throwable t) {
+    logger.error(t);
+  }
+}

+ 69 - 0
spsassembler/src/main/java/de/mcs/utils/Macro.java

@@ -0,0 +1,69 @@
+package de.mcs.utils;
+/**
+ * 
+ */
+
+/**
+ * @author w.klaas
+ */
+public class Macro {
+
+  private String value;
+  private String name;
+
+  /**
+   * Constructs a new macro
+   * 
+   * @param macroName
+   *          name of the macro
+   * @param value
+   *          value for substitution
+   */
+  public Macro(String macroName, String value) {
+    this.name = macroName;
+    this.value = value;
+  }
+
+  /**
+   * @return the value for substitution
+   */
+  public String getValue() {
+    return value;
+  }
+
+  /**
+   * @param value
+   *          setting a new value for this macro
+   */
+  public void setValue(String value) {
+    this.value = value;
+  }
+
+  /**
+   * @return name of this macro.
+   */
+  public String getMacroName() {
+    return name;
+  }
+
+  /**
+   * @return same as getMacroName(), but with the {} delimiter.
+   */
+  public String getMacroDefinition() {
+    StringBuilder b = new StringBuilder();
+    b.append('{');
+    b.append(getMacroName());
+    b.append('}');
+    return b.toString();
+  }
+
+  /*
+   * (non-Javadoc)
+   * @see java.lang.Object#toString()
+   */
+  @Override
+  public String toString() {
+    return String.format("%s=\"%s\"", getMacroDefinition(), getValue());
+  }
+
+}

+ 119 - 0
spsassembler/src/main/java/de/mcs/utils/Macros.java

@@ -0,0 +1,119 @@
+package de.mcs.utils;
+/**
+ * 
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This implements a iterable list of unique {@link Macro}.
+ * 
+ * @author w.klaas
+ */
+public class Macros implements Iterable<Macro> {
+
+  private List<Macro> macroTable;
+
+  /**
+   * Default constructor for the macro list
+   */
+  public Macros() {
+    this.macroTable = new ArrayList<>();
+  }
+
+  /**
+   * adding a new macro definition to this list. If the name is already present, only the value will be changed.
+   * 
+   * @param name
+   *          name of the new macro
+   * @param value
+   *          substitution value of the macro
+   */
+  public void addMacro(String name, String value) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro macro = (Macro) iterator.next();
+      if (macro.getMacroName().equals(name)) {
+        macro.setValue(value);
+        return;
+      }
+    }
+    macroTable.add(new Macro(name, value));
+  }
+
+  /**
+   * adding a new macro definition to this list. If the name is already present, only the value will be changed.
+   * 
+   * @param macro
+   *          the macro to be added
+   */
+  public void addMacro(Macro macro) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro myMacro = (Macro) iterator.next();
+      if (myMacro.getMacroName().equals(macro.getMacroName())) {
+        myMacro.setValue(macro.getValue());
+        return;
+      }
+    }
+    macroTable.add(macro);
+  }
+
+  /**
+   * @return iterator over this list of macros
+   */
+  public Iterator<Macro> iterator() {
+    return macroTable.iterator();
+  }
+
+  /**
+   * substitue a string with macros. All Macros should be name in the string as {macroname}.
+   * 
+   * @param text
+   *          the original string with the macro definitions in
+   * @return the substituted string
+   */
+  public String substitute(final String text) {
+    StringBuilder stringBuilder = new StringBuilder(text);
+    for (Macro macro : this) {
+      int index = stringBuilder.indexOf(macro.getMacroDefinition());
+      if (index != -1) {
+        stringBuilder.replace(index, index + macro.getMacroDefinition().length(), macro.getValue());
+      }
+    }
+    return stringBuilder.toString();
+  }
+
+  /**
+   * getting a single macro from the list of macros
+   * 
+   * @param name
+   *          the name of the macros to get
+   * @return the desired macro or <code>null</code> if the macro is not present.
+   */
+  public Macro getMacro(String name) {
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro macro = (Macro) iterator.next();
+      if (macro.getMacroName().equals(name)) {
+        return macro;
+      }
+    }
+    return null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * @see java.lang.Object#toString()
+   */
+  @Override
+  public String toString() {
+    StringBuilder b = new StringBuilder();
+    for (Iterator<Macro> iterator = macroTable.iterator(); iterator.hasNext();) {
+      Macro macro = (Macro) iterator.next();
+      b.append(macro.toString());
+      b.append(',');
+    }
+    return String.format("[%s]", b.substring(0, b.length() - 1));
+  }
+
+}

+ 13 - 0
spsassembler/src/main/java/de/mcs/utils/Singleton.java

@@ -0,0 +1,13 @@
+package de.mcs.utils;
+
+public class Singleton {
+
+  private static Singleton instance = new Singleton();
+
+  private Singleton() {
+  }
+
+  public static Singleton getInstance() {
+    return instance;
+  }
+}

+ 39 - 0
spsassembler/src/main/resources/log4j.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+	<appender name="fileAppender" class="org.apache.log4j.DailyRollingFileAppender">
+		<param name="datePattern" value="'.'yyyy-MM-dd_HH-mm" />
+		<param name="file" value="log/logging.log" />
+		<param name="Append" value="true" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%d{ISO8601} %-5p [%t] %c: %m%n" />
+		</layout>
+	</appender>
+
+	<appender name="console" class="org.apache.log4j.ConsoleAppender">
+		<param name="Target" value="System.out" />
+		<layout class="org.apache.log4j.PatternLayout">
+			<param name="ConversionPattern" value="%r [%t] %-5p %C{1}#%M: %m%n" />
+		</layout>
+	</appender>
+
+	<logger name="org">
+		<level value="warn" />
+	</logger>
+	
+	<logger name="com">
+		<level value="warn" />
+	</logger>
+
+	<logger name="de">
+		<level value="debug" />
+	</logger>
+	
+	<root>
+		<priority value="info" />
+		<appender-ref ref="fileAppender" />
+		<appender-ref ref="console" />
+	</root>
+
+</log4j:configuration>

+ 32 - 0
spsassembler/src/test/java/de/mcs/tools/TestIntelHex.java

@@ -0,0 +1,32 @@
+package de.mcs.tools;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.utils.IntelHex;
+
+class TestIntelHex {
+
+	@Test
+	void test() throws IOException {
+		File outFile = new File("output.hex");
+		IntelHex intelHex = new IntelHex();
+		byte[] testData = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+		FileOutputStream output = new FileOutputStream(outFile);
+		intelHex.writeHexStream(output, testData);
+		output.close();
+	}
+
+	@Test
+	void testBlink() throws IOException {
+		File outFile = new File("blink.hex");
+		IntelHex intelHex = new IntelHex();
+		byte[] testData = { 0x1F, 0x28, 0x10, 0x28, 0x34, 0, 0, 0 };
+		FileOutputStream output = new FileOutputStream(outFile);
+		intelHex.writeHexStream(output, testData);
+		output.close();
+	}
+}

+ 83 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestBLDA.java

@@ -0,0 +1,83 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestBLDA {
+
+  private static final int BYTE_VALUE_ADC = 0xf0;
+  private final String MNEMONIC = "BLDA";
+
+  private static final int BYTE_VALUE_RC = 0xf2;
+
+  @Test
+  void testMnemonicRC() throws SyntaxError {
+    BLDA mnemonic = new BLDA(MNEMONIC + " RC1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_RC, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new BLDA(MNEMONIC + " RC2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_RC + 1, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      BLDA mno1 = new BLDA(MNEMONIC + " RC3");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      BLDA mno1 = new BLDA(MNEMONIC + " RC0");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicADC() throws SyntaxError {
+    BLDA mnemonic = new BLDA(MNEMONIC + " ADC1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_ADC, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new BLDA(MNEMONIC + " ADC2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_ADC + 1, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      BLDA mno1 = new BLDA(MNEMONIC + " ADC3");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      BLDA mno1 = new BLDA(MNEMONIC + " ADC0");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " ADC1", 0);
+    assertEquals(BLDA.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " 5", 0);
+    });
+  }
+}

+ 85 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestBSRV.java

@@ -0,0 +1,85 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestBSRV {
+
+  private static final int BYTE_VALUE_PWM = 0xf4;
+  private static final int BYTE_VALUE_SRV = 0xf6;
+  private final String MNEMONIC = "BSTA";
+
+  @Test
+  void testMnemonicPWM() throws SyntaxError {
+    BSTA mnemonic = new BSTA(MNEMONIC + " PWM1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_PWM, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new BSTA(MNEMONIC + " PWM2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_PWM + 1, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      BSTA mno1 = new BSTA(MNEMONIC + " PWM3");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      BSTA mno1 = new BSTA(MNEMONIC + " PWM0");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicSRV() throws SyntaxError {
+    BSTA mnemonic = new BSTA(MNEMONIC + " SRV1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_SRV, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new BSTA(MNEMONIC + " SRV2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_SRV + 1, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      BSTA mno1 = new BSTA(MNEMONIC + " SRV3");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      BSTA mno1 = new BSTA(MNEMONIC + " SRV0");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " PWM1", 0);
+    assertEquals(BSTA.class, mnemonic.getClass());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " SRV1", 0);
+    assertEquals(BSTA.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " 5", 0);
+    });
+  }
+}

+ 68 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestCALL.java

@@ -0,0 +1,68 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestCALL {
+
+  private static final int BYTE_VALUE = 0xD0;
+  private final String MNEMONIC = "CALL";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    CALL mnemonic = new CALL(MNEMONIC + " 0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertTrue(mnemonic.isUsingPage());
+
+    mnemonic = new CALL(MNEMONIC + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new CALL(MNEMONIC + " 0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    mnemonic = new CALL(MNEMONIC + " :test");
+    mnemonic.checkArgument();
+    assertTrue(mnemonic.isLabel());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      CALL mnemonic1 = new CALL(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      CALL mnemonic1 = new CALL(MNEMONIC + " 0x10");
+      mnemonic1.checkArgument();
+    });
+
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    assertEquals(CALL.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

+ 123 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestDEQ.java

@@ -0,0 +1,123 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestDEQ {
+
+  private static final int BYTE_VALUE_0 = 0xc8;
+  private final String MNEMONIC_0 = "DEQ0";
+  private static final int BYTE_VALUE_1 = 0xc4;
+  private final String MNEMONIC_1 = "DEQ1";
+
+  @Test
+  void testMnemonic0() throws SyntaxError {
+    DEQ0 mnemonic = new DEQ0(MNEMONIC_0 + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_0, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new DEQ0(MNEMONIC_0 + " 0x04");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_0 + 0x03, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      DEQ0 mno1 = new DEQ0(MNEMONIC_0);
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      DEQ0 mno1 = new DEQ0(MNEMONIC_0 + " 0x05");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      DEQ0 mno1 = new DEQ0(MNEMONIC_0 + " 0x10");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonic1() throws SyntaxError {
+    DEQ1 mnemonic = new DEQ1(MNEMONIC_1 + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_1, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new DEQ1(MNEMONIC_1 + " 0x04");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_1 + 0x03, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      DEQ1 mno1 = new DEQ1(MNEMONIC_1);
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      DEQ1 mno1 = new DEQ1(MNEMONIC_1 + " 0x05");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      DEQ1 mno1 = new DEQ1(MNEMONIC_1 + " 0x10");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonic0Factory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_0 + " 1", 0);
+    assertEquals(DEQ0.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC_0 + " 0x00", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC_0 + " 5", 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_0 + " 0x02", 0);
+    assertEquals(BYTE_VALUE_0 + 1, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_0 + " 3", 0);
+    assertEquals(BYTE_VALUE_0 + 2, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_0 + " 0b00000100", 0);
+    assertEquals(BYTE_VALUE_0 + 3, mnemonic.getByte());
+
+  }
+
+  @Test
+  void testMnemonic1Factory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_1 + " 1", 0);
+    assertEquals(DEQ1.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC_1 + " 0x00", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC_1 + " 5", 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_1 + " 0x02", 0);
+    assertEquals(BYTE_VALUE_1 + 1, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_1 + " 3", 0);
+    assertEquals(BYTE_VALUE_1 + 2, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC_1 + " 0b00000100", 0);
+    assertEquals(BYTE_VALUE_1 + 3, mnemonic.getByte());
+
+  }
+}

+ 68 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestJMP.java

@@ -0,0 +1,68 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestJMP {
+
+  private static final int BYTE_VALUE = 0x90;
+  private final String MNEMONIC = "JMP";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    JMP mnemonic = new JMP(MNEMONIC + " 0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertTrue(mnemonic.isUsingPage());
+
+    mnemonic = new JMP(MNEMONIC + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new JMP(MNEMONIC + " 0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    mnemonic = new JMP(MNEMONIC + " :test");
+    mnemonic.checkArgument();
+    assertTrue(mnemonic.isLabel());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      JMP mnemonic1 = new JMP(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      JMP mnemonic1 = new JMP(MNEMONIC + " 0x10");
+      mnemonic1.checkArgument();
+    });
+
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    assertEquals(JMP.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

+ 164 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLDA.java

@@ -0,0 +1,164 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestLDA {
+
+  private static final int BYTE_VALUE = 0x40;
+  private static final int BYTE_VALUE_DIN = 0x64;
+  private static final int BYTE_VALUE_ADC = 0x69;
+  private static final int BYTE_VALUE_RC = 0x6B;
+  private final String MNEMONIC = "LDA";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    LDA mnemonic = new LDA(MNEMONIC + " #0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new LDA(MNEMONIC + " #0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new LDA(MNEMONIC + " #0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " 0x01");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " #0x10");
+      mnemonic1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicDigital() throws SyntaxError {
+    LDA mnemonic = new LDA(MNEMONIC + " DIN");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_DIN, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new LDA(MNEMONIC + " DIN1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_DIN + 0x01, mnemonic.getByte());
+
+    mnemonic = new LDA(MNEMONIC + " DIN4");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_DIN + 0x04, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " PORT");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " DIN5");
+      mnemonic1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicAnalog() throws SyntaxError {
+    LDA mnemonic = new LDA(MNEMONIC + " ADC1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_ADC, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new LDA(MNEMONIC + " ADC2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_ADC + 0x01, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " ADC");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " ADC0");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " ADC3");
+      mnemonic1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicReceiver() throws SyntaxError {
+    LDA mnemonic = new LDA(MNEMONIC + " RC1");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_RC, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new LDA(MNEMONIC + " RC2");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE_RC + 0x01, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " RC");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " RC0");
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LDA mnemonic1 = new LDA(MNEMONIC + " RC3");
+      mnemonic1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " #0x00", 0);
+    assertEquals(LDA.class, mnemonic.getClass());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " DIN", 0);
+    assertEquals(LDA.class, mnemonic.getClass());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " ADC1", 0);
+    assertEquals(LDA.class, mnemonic.getClass());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " RC2", 0);
+    assertEquals(LDA.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " #0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " #12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " #0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

+ 68 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLOOPC.java

@@ -0,0 +1,68 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestLOOPC {
+
+  private static final int BYTE_VALUE = 0xA0;
+  private final String MNEMONIC = "LOOPC";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    LOOPC mnemonic = new LOOPC(MNEMONIC + " 0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertTrue(mnemonic.isUsingPage());
+
+    mnemonic = new LOOPC(MNEMONIC + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new LOOPC(MNEMONIC + " 0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    mnemonic = new LOOPC(MNEMONIC + " :test");
+    mnemonic.checkArgument();
+    assertTrue(mnemonic.isLabel());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LOOPC mnemonic1 = new LOOPC(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      LOOPC mnemonic1 = new LOOPC(MNEMONIC + " 0x10");
+      mnemonic1.checkArgument();
+    });
+
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    assertEquals(LOOPC.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

+ 68 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestLOOPD.java

@@ -0,0 +1,68 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestLOOPD {
+
+  private static final int BYTE_VALUE = 0xB0;
+  private final String MNEMONIC = "LOOPD";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    LOOPD mnemonic = new LOOPD(MNEMONIC + " 0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertTrue(mnemonic.isUsingPage());
+
+    mnemonic = new LOOPD(MNEMONIC + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new LOOPD(MNEMONIC + " 0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    mnemonic = new LOOPD(MNEMONIC + " :test");
+    mnemonic.checkArgument();
+    assertTrue(mnemonic.isLabel());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      LOOPD mnemonic1 = new LOOPD(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      LOOPD mnemonic1 = new LOOPD(MNEMONIC + " 0x10");
+      mnemonic1.checkArgument();
+    });
+
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    assertEquals(LOOPD.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

+ 102 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestMOV.java

@@ -0,0 +1,102 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestMOV {
+
+  private final String MNEMONIC = "MOV";
+  private final byte BA = 0x51;
+  private final byte CA = 0x52;
+  private final byte DA = 0x53;
+  private final byte EA = 0x5d;
+  private final byte FA = 0x5e;
+
+  private final byte AB = 0x61;
+  private final byte AC = 0x62;
+  private final byte AD = 0x63;
+  private final byte AE = 0x6d;
+  private final byte AF = 0x6e;
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    MOV mnemonic = new MOV(MNEMONIC + " A, B");
+    mnemonic.checkArgument();
+    assertEquals(AB, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new MOV(MNEMONIC + " A, C");
+    mnemonic.checkArgument();
+    assertEquals(AC, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " A, D");
+    mnemonic.checkArgument();
+    assertEquals(AD, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " A, E");
+    mnemonic.checkArgument();
+    assertEquals(AE, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " A, F");
+    mnemonic.checkArgument();
+    assertEquals(AF, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " B, A");
+    mnemonic.checkArgument();
+    assertEquals(BA, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " C, A");
+    mnemonic.checkArgument();
+    assertEquals(CA, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " D, A");
+    mnemonic.checkArgument();
+    assertEquals(DA, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " E, A");
+    mnemonic.checkArgument();
+    assertEquals(EA, mnemonic.getByte());
+
+    mnemonic = new MOV(MNEMONIC + " F, A");
+    mnemonic.checkArgument();
+    assertEquals(FA, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MOV mno1 = new MOV(MNEMONIC + " 0x05");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MOV mno1 = new MOV(MNEMONIC + " 0x10");
+      mno1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      MOV mno1 = new MOV(MNEMONIC + " A, G");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " A, B", 0);
+    assertEquals(MOV.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC + " AB", 0);
+    });
+
+  }
+}

+ 54 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestNOP.java

@@ -0,0 +1,54 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestNOP {
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    NOP mno = new NOP("NOP");
+    assertEquals(0x00, mno.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      NOP mno1 = new NOP("NOP akfhaskh");
+      mno1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic("NOP", 0);
+    assertEquals(NOP.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic("NOP kdfhkalsfh", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic("NOP 0x12", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic("NOP 12", 0);
+    });
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic("NOP 0b01001010", 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic("Nop", 2);
+    assertEquals(NOP.class, mnemonic.getClass());
+
+    mnemonic = MnemonicFactory.getMnemonic("nop", 3);
+    assertEquals(NOP.class, mnemonic.getClass());
+    assertEquals(3, mnemonic.getLineNumber());
+
+    mnemonic = MnemonicFactory.getMnemonic("nop", 4);
+    assertEquals(NOP.class, mnemonic.getClass());
+  }
+}

+ 64 - 0
spsassembler/src/test/java/de/mcs/tools/sps/mnemonic/TestPAGE.java

@@ -0,0 +1,64 @@
+package de.mcs.tools.sps.mnemonic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import de.mcs.tools.sps.exceptions.IllegalArgument;
+import de.mcs.tools.sps.exceptions.SyntaxError;
+
+class TestPAGE {
+
+  private static final int BYTE_VALUE = 0x80;
+  private final String MNEMONIC = "PAGE";
+
+  @Test
+  void testMnemonic() throws SyntaxError {
+    PAGE mnemonic = new PAGE(MNEMONIC + " 0x00");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE, mnemonic.getByte());
+    assertTrue(mnemonic.hasArgument());
+    assertFalse(mnemonic.isUsingPage());
+
+    mnemonic = new PAGE(MNEMONIC + " 0x01");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x01, mnemonic.getByte());
+
+    mnemonic = new PAGE(MNEMONIC + " 0x0f");
+    mnemonic.checkArgument();
+    assertEquals(BYTE_VALUE + 0x0f, mnemonic.getByte());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      PAGE mnemonic1 = new PAGE(MNEMONIC);
+      mnemonic1.checkArgument();
+    });
+
+    Assertions.assertThrows(IllegalArgument.class, () -> {
+      PAGE mnemonic1 = new PAGE(MNEMONIC + " 0x10");
+      mnemonic1.checkArgument();
+    });
+  }
+
+  @Test
+  void testMnemonicFactory() throws SyntaxError {
+    Mnemonic mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x00", 0);
+    assertEquals(PAGE.class, mnemonic.getClass());
+
+    Assertions.assertThrows(SyntaxError.class, () -> {
+      MnemonicFactory.getMnemonic(MNEMONIC, 0);
+    });
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0x02", 0);
+    assertEquals(BYTE_VALUE + 0x02, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 12", 0);
+    assertEquals(BYTE_VALUE + 12, mnemonic.getByte());
+
+    mnemonic = MnemonicFactory.getMnemonic(MNEMONIC + " 0b00001010", 0);
+    assertEquals(BYTE_VALUE + 10, mnemonic.getByte());
+
+  }
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff