Jelajahi Sumber

Bug in INtelHEX CRC calculation

- exporting to many lines if no DFSB mnemonic found.
Wilfried Klaas 6 tahun lalu
induk
melakukan
4739da5ab4

+ 514 - 518
src/main/java/de/mcs/tools/sps/SPSAssembler.java

@@ -71,522 +71,518 @@ import de.mcs.utils.jsap.SwitchOption;
 @Command(help = "SPS Assembler \r\n usage java -jar SPSEmulator-x.x.x.jar <options>")
 public class SPSAssembler {
 
-  private static final String INCLUDE_FILE = ".include";
-  private static final String END_MACRO_DEFINITION = ".endmacro";
-  private static final String START_MACRO_DEFINITION = ".macro";
-  private static final String DEFAULT_PACKAGE_FILTER = "de.mcs.tools.sps"; //$NON-NLS-1$
-  private static File source;
-  private static HARDWARE destination;
-  private static File destinationFile;
-  private static String outputFormat;
-  private static File includes;
-  private static Outputter outputter;
-  private static String destinationStr;
-
-  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;
-
-  @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 {
-      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.doWork(source);
-
-        List<Mnemonic> mnemonics = spsAssembler.getMnemonics();
-
-        outputMnemonics(mnemonics);
-
-        outputLabels(spsAssembler);
-
-        outputMacros(spsAssembler);
-
-        outputFile(mnemonics);
-
-        System.out.println("assembling succesfully");
-      } catch (SyntaxError e) {
-        e.printStackTrace();
-        System.err.println(e.getMessage());
-        System.exit(-1);
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-  }
-
-  /**
-   * @throws InstantiationException
-   * @throws IllegalAccessException
-   * @throws Exception
-   */
-  private static void registerAllOutputter() throws InstantiationException, IllegalAccessException, Exception {
-    Set<Class<?>> outputClasses = searchOutputter(SPSOutputter.class);
-    for (Class<?> outClass : outputClasses) {
-      SPSOutputter annotation = outClass.getAnnotation(SPSOutputter.class);
-      if (annotation.name().equalsIgnoreCase(outputFormat)) {
-        outputter = (Outputter) outClass.newInstance();
-      }
-    }
-
-    if (outputter == null) {
-      throw new Exception(String.format("can't find outputter for format: %s", outputFormat));
-    }
-  }
-
-  private static Set<Class<?>> searchOutputter(Class<? extends Annotation> annotationClass) {
-    ConfigurationBuilder builder = new ConfigurationBuilder();
-    builder.addUrls(ClasspathHelper.forPackage(DEFAULT_PACKAGE_FILTER));
-    Reflections reflections = new Reflections(builder);
-    Set<Class<?>> classes = new HashSet<Class<?>>();
-    classes.addAll(reflections.getTypesAnnotatedWith(annotationClass));
-    return classes;
-  }
-
-  /**
-   * @param mnemonics
-   * @throws IOException
-   * @throws FileNotFoundException
-   */
-  private static void outputFile(List<Mnemonic> mnemonics) throws IOException, FileNotFoundException {
-    try (FileOutputStream output = new FileOutputStream(destinationFile)) {
-      outputter.output(mnemonics, output);
-    } finally {
-    }
-  }
-
-  /**
-   * @param spsAssembler
-   */
-  private static void outputMacros(SPSAssembler spsAssembler) {
-    Map<String, Macro> macros = spsAssembler.getMacros();
-    if ((macros != null) && (macros.size() > 0)) {
-      System.out.println();
-      System.out.println("macros");
-      for (Macro macro : macros.values()) {
-        System.out.printf("%s: %s\r\n", macro.getName(), macro.toString());
-      }
-    }
-  }
-
-  /**
-   * @param spsAssembler
-   */
-  private static void outputLabels(SPSAssembler spsAssembler) {
-    Map<String, Integer> labels = spsAssembler.getLabels();
-    if ((labels != null) && (labels.size() > 0)) {
-      System.out.println();
-      System.out.println("labels");
-      for (Entry<String, Integer> entry : labels.entrySet()) {
-        System.out.printf("%s: 0x%03x\r\n", entry.getKey(), entry.getValue());
-      }
-    }
-  }
-
-  /**
-   * @param mnemonics
-   */
-  private static void outputMnemonics(List<Mnemonic> mnemonics) {
-    System.out.println("Mnemonics");
-    int pos = 0;
-    for (Iterator<Mnemonic> iterator = mnemonics.iterator(); iterator.hasNext();) {
-      Mnemonic mnemonic = iterator.next();
-      System.out.printf("0x%03x: %s\r\n", pos, mnemonic.toString());
-      pos++;
-    }
-  }
-
-  /**
-   * 
-   */
-  public SPSAssembler() {
-    lineNumber = 0;
-    // srcLineNumber = 0;
-    labels = new HashMap<>();
-    inBlockComment = false;
-    mnemonics = new ArrayList<>();
-    inMacro = false;
-    actualMacro = null;
-    macros = new HashMap<>();
-  }
-
-  private void doWork(File source) throws IOException, SyntaxError {
-    System.out.printf("start parsing file: %s\r\n", source.getName());
-
-    List<String> sourceFile = Files.readAllLines(source.toPath(), Charset.forName("UTF-8"));
-    for (int i = 0; i < sourceFile.size(); i++) {
-      String line = sourceFile.get(i);
-
-      List<String> linesToAdd;
-      do {
-        linesToAdd = parseLine(i, line);
-        if (linesToAdd != null) {
-          sourceFile.remove(i);
-          sourceFile.addAll(i, linesToAdd);
-          line = sourceFile.get(i);
-          linesToAdd = parseLine(i, line);
-        }
-      } while (linesToAdd != null);
-    }
-
-    // Checking destination
-    checkingHardware();
-
-    calculatingJumps();
-
-    paddingSubroutines();
-
-    // checking size of program
-    checkingProgramSize();
-  }
-
-  private void paddingSubroutines() throws SyntaxError {
-    // checking location of Subroutines
-    if (destination.equals(HARDWARE.ARDUINOSPS) || destination.equals(HARDWARE.TINYSPS)) {
-      int position = 0;
-      for (int i = 0; i < mnemonics.size(); i++) {
-        Mnemonic mnemonic = mnemonics.get(i);
-        if (mnemonic instanceof DFSB) {
-          break;
-        }
-        position++;
-      }
-      if (position < 256) {
-        int count = 256 - position;
-        for (int i = 0; i < count; i++) {
-          mnemonics.add(position, new NOP(""));
-        }
-      }
-    }
-  }
-
-  private void checkingProgramSize() throws HardwareException {
-    int prgSize = 0;
-    for (Mnemonic mnemonic : mnemonics) {
-      if (mnemonic instanceof DFSB) {
-        break;
-      }
-      prgSize++;
-    }
-    int maxSize = 128;
-    switch (destination) {
-    case ARDUINOSPS:
-    case TINYSPS:
-      maxSize = 256;
-      break;
-    default:
-      break;
-    }
-    if (prgSize > maxSize) {
-      throw new HardwareException(
-          String.format("Program exceeding size (%d) for hardeware %s (%d).", prgSize, destination, 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(destination)) {
-        throw new SyntaxError(mnemonic.getLineNumber(),
-            String.format("the mnemonic \"%s\" with the argument \"%s\" is not availble on the choosen hardware \"%s\"",
-                mnemonic.getName(), mnemonic.getArgument() == null ? "" : mnemonic.getArgument(), destination.name()));
-      }
-    }
-  }
-
-  private void processJMP(Mnemonic mnemonic, Mnemonic predecessor) throws SyntaxError {
-    String label = mnemonic.getArgument();
-    if (!labels.containsKey(label)) {
-      throw new SyntaxError(mnemonic.getLineNumber(),
-          String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
-    }
-    int lineNumber = (Integer) labels.get(label);
-    int page = lineNumber / 16;
-    lineNumber = lineNumber % 16;
-    mnemonic.setArgument(Integer.toString(lineNumber));
-    mnemonic.checkArgument();
-    if ((predecessor != null) && (predecessor instanceof PAGE)) {
-      PAGE pageMne = (PAGE) predecessor;
-      if (pageMne.isCalculate()) {
-        pageMne.setArgument(Integer.toString(page));
-        pageMne.checkArgument();
-      }
-    }
-  }
-
-  private void processRJMP(int address, Mnemonic mnemonic) throws SyntaxError {
-    String label = mnemonic.getArgument();
-    if (!labels.containsKey(label)) {
-      throw new SyntaxError(mnemonic.getLineNumber(),
-          String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
-    }
-    int lineNumber = (Integer) labels.get(label);
-    lineNumber = address - lineNumber;
-    if (lineNumber < 0) {
-      throw new SyntaxError(mnemonic.getLineNumber(),
-          String.format("label %s defined after mnemonic \"%s\". JMP can only step back!", label, mnemonic.getName()));
-    }
-    mnemonic.setArgument(Integer.toString(lineNumber));
-    mnemonic.checkArgument();
-  }
-
-  private List<String> parseLine(int srcLineNumber, String srcLine) throws SyntaxError, IOException {
-    String line = srcLine.trim();
-    if (line.startsWith(".")) {
-      // process pre compiler part
-      String label = getLabel(line);
-      return precompiler(srcLineNumber, label);
-    } else {
-      if (inMacro) {
-        actualMacro.addLine(line);
-      } else {
-        if (line.startsWith(":")) {
-          String label = getLabel(line);
-          if (labels.containsKey(label)) {
-            throw new SyntaxError(srcLineNumber,
-                String.format("Label \"%s\" already definied in line %d.", label, labels.get(label)));
-          }
-          labels.put(label, lineNumber);
-        } else {
-          Mnemonic mnemonic = getMnemonic(line, srcLineNumber);
-          if (mnemonic != null) {
-            lineNumber++;
-            mnemonics.add(mnemonic);
-          }
-        }
-      }
-    }
-    return null;
-  }
-
-  private List<String> precompiler(int srcLineNumber, String value) throws IOException, SyntaxError {
-    String label = value.trim().toLowerCase();
-    String name = label;
-    if (label.indexOf(" ") > 0) {
-      name = label.substring(0, label.indexOf(" "));
-    }
-    if (INCLUDE_FILE.equals(name)) {
-      includeFile(srcLineNumber, label);
-    } else if (START_MACRO_DEFINITION.equals(name)) {
-      startMacroDefinition(srcLineNumber, label);
-      inMacro = true;
-    } else if (END_MACRO_DEFINITION.equalsIgnoreCase(label)) {
-      // end of macro definition
-      inMacro = false;
-      macros.put(actualMacro.getName(), actualMacro);
-    } else {
-      // seems to be a macro usage
-      name = name.substring(1);
-      String[] arguments = label.split(" ");
-      if (arguments.length < 1) {
-        throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
-      }
-      String[] args = null;
-      if (arguments.length > 1) {
-        args = Arrays.copyOfRange(arguments, 1, arguments.length);
-      }
-      Macro macro = macros.get(name);
-      if (macro == null) {
-        throw new SyntaxError(srcLineNumber, String.format("macro with name %s not define.", name));
-      }
-      List<String> macroMnemonics = macro.processMacro(args, srcLineNumber);
-      return macroMnemonics;
-    }
-    return null;
-  }
-
-  /**
-   * @param srcLineNumber
-   * @param label
-   * @throws IllegalArgument
-   */
-  private void startMacroDefinition(int srcLineNumber, String label) throws IllegalArgument {
-    // macro definition
-    String[] arguments = label.split(" ");
-    if (arguments.length < 2) {
-      throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
-    }
-    String macroName = arguments[1];
-    String[] args = null;
-    if (arguments.length > 2) {
-      args = Arrays.copyOfRange(arguments, 2, arguments.length);
-    }
-    actualMacro = new Macro(macroName, args);
-  }
-
-  /**
-   * @param srcLineNumber
-   * @param label
-   * @throws IllegalArgument
-   * @throws IOException
-   * @throws SyntaxError
-   */
-  private void includeFile(int srcLineNumber, String label) throws IllegalArgument, IOException, SyntaxError {
-    String[] arguments = label.split(" ");
-    if (arguments.length < 2) {
-      throw new IllegalArgument(srcLineNumber, "Need at least one argument for the filename to include.");
-    }
-    File includeFile = new File(includes, String.format("%s.tps", arguments[1]));
-    if (!includeFile.exists()) {
-      throw new IllegalArgument(srcLineNumber, String.format("include file not found: %s", includeFile.getName()));
-    }
-    System.out.println("=====");
-    System.out.printf("include file: %s \r\n", includeFile.getName());
-    SPSAssembler spsAssembler = new SPSAssembler();
-    spsAssembler.doWork(includeFile);
-    outputMnemonics(spsAssembler.getMnemonics());
-    outputLabels(spsAssembler);
-    outputMacros(spsAssembler);
-    System.out.println("=====");
-  }
-
-  private Mnemonic getMnemonic(String line, int srcLineNumber) throws SyntaxError {
-    String newLine = stripComments(line);
-    if (StringUtils.isNotEmpty(newLine)) {
-      Mnemonic mnemonic = MnemonicFactory.getMnemonic(newLine, srcLineNumber);
-      return mnemonic;
-    }
-    return null;
-  }
-
-  private String stripComments(String line) {
-    if (line.indexOf("*/") >= 0) {
-      inBlockComment = false;
-      return null;
-    }
-    if (inBlockComment) {
-      return null;
-    }
-    if (line.indexOf(";") >= 0) {
-      line = line.substring(0, line.indexOf(";")).trim();
-      return line;
-    }
-    if (line.startsWith("/*")) {
-      inBlockComment = true;
-      return null;
-    }
-    return line.trim();
-  }
-
-  private String getLabel(String line) {
-    line = stripComments(line);
-    return line;
-  }
-
-  private List<Mnemonic> getMnemonics() {
-    return mnemonics;
-  }
-
-  private Map<String, Macro> getMacros() {
-    return macros;
-  }
-
-  private Map<String, Integer> getLabels() {
-    return labels;
-  }
+	private static final String INCLUDE_FILE = ".include";
+	private static final String END_MACRO_DEFINITION = ".endmacro";
+	private static final String START_MACRO_DEFINITION = ".macro";
+	private static final String DEFAULT_PACKAGE_FILTER = "de.mcs.tools.sps"; //$NON-NLS-1$
+	private static File source;
+	private static HARDWARE destination;
+	private static File destinationFile;
+	private static String outputFormat;
+	private static File includes;
+	private static Outputter outputter;
+	private static String destinationStr;
+
+	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;
+
+	@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 {
+			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.doWork(source);
+
+				List<Mnemonic> mnemonics = spsAssembler.getMnemonics();
+
+				outputMnemonics(mnemonics);
+
+				outputLabels(spsAssembler);
+
+				outputMacros(spsAssembler);
+
+				outputFile(mnemonics);
+
+				System.out.println("assembling succesfully");
+			} catch (SyntaxError e) {
+				e.printStackTrace();
+				System.err.println(e.getMessage());
+				System.exit(-1);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * @throws InstantiationException
+	 * @throws IllegalAccessException
+	 * @throws Exception
+	 */
+	private static void registerAllOutputter() throws InstantiationException, IllegalAccessException, Exception {
+		Set<Class<?>> outputClasses = searchOutputter(SPSOutputter.class);
+		for (Class<?> outClass : outputClasses) {
+			SPSOutputter annotation = outClass.getAnnotation(SPSOutputter.class);
+			if (annotation.name().equalsIgnoreCase(outputFormat)) {
+				outputter = (Outputter) outClass.newInstance();
+			}
+		}
+
+		if (outputter == null) {
+			throw new Exception(String.format("can't find outputter for format: %s", outputFormat));
+		}
+	}
+
+	private static Set<Class<?>> searchOutputter(Class<? extends Annotation> annotationClass) {
+		ConfigurationBuilder builder = new ConfigurationBuilder();
+		builder.addUrls(ClasspathHelper.forPackage(DEFAULT_PACKAGE_FILTER));
+		Reflections reflections = new Reflections(builder);
+		Set<Class<?>> classes = new HashSet<Class<?>>();
+		classes.addAll(reflections.getTypesAnnotatedWith(annotationClass));
+		return classes;
+	}
+
+	/**
+	 * @param mnemonics
+	 * @throws IOException
+	 * @throws FileNotFoundException
+	 */
+	private static void outputFile(List<Mnemonic> mnemonics) throws IOException, FileNotFoundException {
+		try (FileOutputStream output = new FileOutputStream(destinationFile)) {
+			outputter.output(mnemonics, output);
+		} finally {
+		}
+	}
+
+	/**
+	 * @param spsAssembler
+	 */
+	private static void outputMacros(SPSAssembler spsAssembler) {
+		Map<String, Macro> macros = spsAssembler.getMacros();
+		if ((macros != null) && (macros.size() > 0)) {
+			System.out.println();
+			System.out.println("macros");
+			for (Macro macro : macros.values()) {
+				System.out.printf("%s: %s\r\n", macro.getName(), macro.toString());
+			}
+		}
+	}
+
+	/**
+	 * @param spsAssembler
+	 */
+	private static void outputLabels(SPSAssembler spsAssembler) {
+		Map<String, Integer> labels = spsAssembler.getLabels();
+		if ((labels != null) && (labels.size() > 0)) {
+			System.out.println();
+			System.out.println("labels");
+			for (Entry<String, Integer> entry : labels.entrySet()) {
+				System.out.printf("%s: 0x%03x\r\n", entry.getKey(), entry.getValue());
+			}
+		}
+	}
+
+	/**
+	 * @param mnemonics
+	 */
+	private static void outputMnemonics(List<Mnemonic> mnemonics) {
+		System.out.println("Mnemonics");
+		int pos = 0;
+		for (Iterator<Mnemonic> iterator = mnemonics.iterator(); iterator.hasNext();) {
+			Mnemonic mnemonic = iterator.next();
+			System.out.printf("0x%03x: %s\r\n", pos, mnemonic.toString());
+			pos++;
+		}
+	}
+
+	/**
+	 * 
+	 */
+	public SPSAssembler() {
+		lineNumber = 0;
+		// srcLineNumber = 0;
+		labels = new HashMap<>();
+		inBlockComment = false;
+		mnemonics = new ArrayList<>();
+		inMacro = false;
+		actualMacro = null;
+		macros = new HashMap<>();
+	}
+
+	private void doWork(File source) throws IOException, SyntaxError {
+		System.out.printf("start parsing file: %s\r\n", source.getName());
+
+		List<String> sourceFile = Files.readAllLines(source.toPath(), Charset.forName("UTF-8"));
+		for (int i = 0; i < sourceFile.size(); i++) {
+			String line = sourceFile.get(i);
+
+			List<String> linesToAdd;
+			do {
+				linesToAdd = parseLine(i, line);
+				if (linesToAdd != null) {
+					sourceFile.remove(i);
+					sourceFile.addAll(i, linesToAdd);
+					line = sourceFile.get(i);
+					linesToAdd = parseLine(i, line);
+				}
+			} while (linesToAdd != null);
+		}
+
+		// Checking destination
+		checkingHardware();
+
+		calculatingJumps();
+
+		paddingSubroutines();
+
+		// checking size of program
+		checkingProgramSize();
+	}
+
+	private void paddingSubroutines() throws SyntaxError {
+		// checking location of Subroutines
+		if (destination.equals(HARDWARE.ARDUINOSPS) || destination.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(""));
+				}
+			}
+		}
+	}
+
+	private void checkingProgramSize() throws HardwareException {
+		int prgSize = 0;
+		for (Mnemonic mnemonic : mnemonics) {
+			if (mnemonic instanceof DFSB) {
+				break;
+			}
+			prgSize++;
+		}
+		int maxSize = 128;
+		switch (destination) {
+		case ARDUINOSPS:
+		case TINYSPS:
+			maxSize = 256;
+			break;
+		default:
+			break;
+		}
+		if (prgSize > maxSize) {
+			throw new HardwareException(String.format("Program exceeding size (%d) for hardeware %s (%d).", prgSize, destination, 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(destination)) {
+				throw new SyntaxError(mnemonic.getLineNumber(),
+						String.format("the mnemonic \"%s\" with the argument \"%s\" is not availble on the choosen hardware \"%s\"", mnemonic.getName(),
+								mnemonic.getArgument() == null ? "" : mnemonic.getArgument(), destination.name()));
+			}
+		}
+	}
+
+	private void processJMP(Mnemonic mnemonic, Mnemonic predecessor) throws SyntaxError {
+		String label = mnemonic.getArgument();
+		if (!labels.containsKey(label)) {
+			throw new SyntaxError(mnemonic.getLineNumber(), String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
+		}
+		int lineNumber = (Integer) labels.get(label);
+		int page = lineNumber / 16;
+		lineNumber = lineNumber % 16;
+		mnemonic.setArgument(Integer.toString(lineNumber));
+		mnemonic.checkArgument();
+		if ((predecessor != null) && (predecessor instanceof PAGE)) {
+			PAGE pageMne = (PAGE) predecessor;
+			if (pageMne.isCalculate()) {
+				pageMne.setArgument(Integer.toString(page));
+				pageMne.checkArgument();
+			}
+		}
+	}
+
+	private void processRJMP(int address, Mnemonic mnemonic) throws SyntaxError {
+		String label = mnemonic.getArgument();
+		if (!labels.containsKey(label)) {
+			throw new SyntaxError(mnemonic.getLineNumber(), String.format("used label %s on mnemonic \"%s\" is not defined.", label, mnemonic.getName()));
+		}
+		int lineNumber = (Integer) labels.get(label);
+		lineNumber = address - lineNumber;
+		if (lineNumber < 0) {
+			throw new SyntaxError(mnemonic.getLineNumber(),
+					String.format("label %s defined after mnemonic \"%s\". JMP can only step back!", label, mnemonic.getName()));
+		}
+		mnemonic.setArgument(Integer.toString(lineNumber));
+		mnemonic.checkArgument();
+	}
+
+	private List<String> parseLine(int srcLineNumber, String srcLine) throws SyntaxError, IOException {
+		String line = srcLine.trim();
+		if (line.startsWith(".")) {
+			// process pre compiler part
+			String label = getLabel(line);
+			return precompiler(srcLineNumber, label);
+		} else {
+			if (inMacro) {
+				actualMacro.addLine(line);
+			} else {
+				if (line.startsWith(":")) {
+					String label = getLabel(line);
+					if (labels.containsKey(label)) {
+						throw new SyntaxError(srcLineNumber, String.format("Label \"%s\" already definied in line %d.", label, labels.get(label)));
+					}
+					labels.put(label, lineNumber);
+				} else {
+					Mnemonic mnemonic = getMnemonic(line, srcLineNumber);
+					if (mnemonic != null) {
+						lineNumber++;
+						mnemonics.add(mnemonic);
+					}
+				}
+			}
+		}
+		return null;
+	}
+
+	private List<String> precompiler(int srcLineNumber, String value) throws IOException, SyntaxError {
+		String label = value.trim().toLowerCase();
+		String name = label;
+		if (label.indexOf(" ") > 0) {
+			name = label.substring(0, label.indexOf(" "));
+		}
+		if (INCLUDE_FILE.equals(name)) {
+			includeFile(srcLineNumber, label);
+		} else if (START_MACRO_DEFINITION.equals(name)) {
+			startMacroDefinition(srcLineNumber, label);
+			inMacro = true;
+		} else if (END_MACRO_DEFINITION.equalsIgnoreCase(label)) {
+			// end of macro definition
+			inMacro = false;
+			macros.put(actualMacro.getName(), actualMacro);
+		} else {
+			// seems to be a macro usage
+			name = name.substring(1);
+			String[] arguments = label.split(" ");
+			if (arguments.length < 1) {
+				throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
+			}
+			String[] args = null;
+			if (arguments.length > 1) {
+				args = Arrays.copyOfRange(arguments, 1, arguments.length);
+			}
+			Macro macro = macros.get(name);
+			if (macro == null) {
+				throw new SyntaxError(srcLineNumber, String.format("macro with name %s not define.", name));
+			}
+			List<String> macroMnemonics = macro.processMacro(args, srcLineNumber);
+			return macroMnemonics;
+		}
+		return null;
+	}
+
+	/**
+	 * @param srcLineNumber
+	 * @param label
+	 * @throws IllegalArgument
+	 */
+	private void startMacroDefinition(int srcLineNumber, String label) throws IllegalArgument {
+		// macro definition
+		String[] arguments = label.split(" ");
+		if (arguments.length < 2) {
+			throw new IllegalArgument(srcLineNumber, "Need at least one argument for the macro name.");
+		}
+		String macroName = arguments[1];
+		String[] args = null;
+		if (arguments.length > 2) {
+			args = Arrays.copyOfRange(arguments, 2, arguments.length);
+		}
+		actualMacro = new Macro(macroName, args);
+	}
+
+	/**
+	 * @param srcLineNumber
+	 * @param label
+	 * @throws IllegalArgument
+	 * @throws IOException
+	 * @throws SyntaxError
+	 */
+	private void includeFile(int srcLineNumber, String label) throws IllegalArgument, IOException, SyntaxError {
+		String[] arguments = label.split(" ");
+		if (arguments.length < 2) {
+			throw new IllegalArgument(srcLineNumber, "Need at least one argument for the filename to include.");
+		}
+		File includeFile = new File(includes, String.format("%s.tps", arguments[1]));
+		if (!includeFile.exists()) {
+			throw new IllegalArgument(srcLineNumber, String.format("include file not found: %s", includeFile.getName()));
+		}
+		System.out.println("=====");
+		System.out.printf("include file: %s \r\n", includeFile.getName());
+		SPSAssembler spsAssembler = new SPSAssembler();
+		spsAssembler.doWork(includeFile);
+		outputMnemonics(spsAssembler.getMnemonics());
+		outputLabels(spsAssembler);
+		outputMacros(spsAssembler);
+		System.out.println("=====");
+	}
+
+	private Mnemonic getMnemonic(String line, int srcLineNumber) throws SyntaxError {
+		String newLine = stripComments(line);
+		if (StringUtils.isNotEmpty(newLine)) {
+			Mnemonic mnemonic = MnemonicFactory.getMnemonic(newLine, srcLineNumber);
+			return mnemonic;
+		}
+		return null;
+	}
+
+	private String stripComments(String line) {
+		if (line.indexOf("*/") >= 0) {
+			inBlockComment = false;
+			return null;
+		}
+		if (inBlockComment) {
+			return null;
+		}
+		if (line.indexOf(";") >= 0) {
+			line = line.substring(0, line.indexOf(";")).trim();
+			return line;
+		}
+		if (line.startsWith("/*")) {
+			inBlockComment = true;
+			return null;
+		}
+		return line.trim();
+	}
+
+	private String getLabel(String line) {
+		line = stripComments(line);
+		return line;
+	}
+
+	private List<Mnemonic> getMnemonics() {
+		return mnemonics;
+	}
+
+	private Map<String, Macro> getMacros() {
+		return macros;
+	}
+
+	private Map<String, Integer> getLabels() {
+		return labels;
+	}
 }

+ 53 - 52
src/main/java/de/mcs/tools/sps/utils/IntelHex.java

@@ -32,60 +32,61 @@ import java.io.OutputStreamWriter;
  */
 public class IntelHex {
 
-  private int byteCount;
+	private int byteCount;
 
-  public IntelHex() {
-    byteCount = 8;
-  }
+	public IntelHex() {
+		byteCount = 8;
+	}
 
-  public void writeHexStream(OutputStream output, byte[] data) {
-    int address = 0;
-    int crc = 0;
-    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output))) {
-      for (int i = 0; i < data.length; i++) {
-        if ((i % byteCount) == 0) {
-          if (i > 0) {
-            crc = writeCRC(crc, writer);
-          }
-          crc = 0;
-          writer.append(':');
-          if ((data.length - i) > byteCount) {
-            writer.append(String.format("%02X", byteCount));
-            crc += byteCount;
-          } else {
-            writer.append(String.format("%02X", data.length - i));
-            crc += (data.length - i);
-          }
-          writer.append(String.format("%04X", address));
-          crc += address >> 8;
-          crc += address & 0x00FF;
-          writer.append("00");
-        }
-        writer.append(String.format("%02X", data[i]));
-        address++;
-      }
-      writeCRC(crc, writer);
-      crc = 0;
-      writer.append(':');
-      writer.append(String.format("%02X", 0));
-      crc += byteCount;
-      writer.append(String.format("%04X", 0));
-      crc += address >> 8;
-      crc += address & 0x00FF;
-      writer.append("01FF");
+	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++;
+			}
+			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();
-    }
-  }
+		} 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;
-  }
+	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;
+	}
 }

+ 18 - 9
src/test/java/de/mcs/tools/TestIntelHex.java

@@ -10,14 +10,23 @@ 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 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();
+	}
 }