/******************************************************************************* * openDLX - A DLX/MIPS processor simulator. * Copyright (C) 2013 The openDLX project, University of Augsburg, Germany * Project URL: <https://sourceforge.net/projects/opendlx> * Development branch: <https://github.com/smetzlaff/openDLX> * * * 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 * 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, see <LICENSE>. If not, see * <http://www.gnu.org/licenses/>. ******************************************************************************/ package openDLX.asm.parser; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import openDLX.asm.AssemblerException; import openDLX.asm.MemoryBuffer; import openDLX.asm.instruction.Instruction; import openDLX.asm.instruction.InstructionException; import openDLX.asm.instruction.Registers; import openDLX.asm.tokenizer.Token; import openDLX.asm.tokenizer.TokenType; import openDLX.asm.tokenizer.Tokenizer; import openDLX.asm.tokenizer.TokenizerException; public class Parser { private static final String INCOMPLETE_DIRECTIVE = "incomplete directive"; private static final String INCOMPLETE_INSTRUCTION = "incomplete instruction"; private static final String INSTRUCTION_EXCEPTION = "instruction: "; private static final String LABEL_ALREADY_EXISTS = "label already exists"; private static final String LABEL_DOES_NOT_EXISTS = "label does not exist"; private static final String LABEL_NOT_ALLOWED_HERE = "Label not allowed here"; private static final String MISSING_PARANTHESIS = "missing paranthesis"; private static final String MISSING_SEPARATOR = "missing separator before"; private static final String NO_NOP = "this is no nop instruction"; private static final String NOT_A_NUMBER = "expected number or label but got"; private static final String NOT_A_REGISTER = "expected register specifier but got"; private static final String NUMBER_NEGATIVE = "negative value not allowed here"; private static final String NUMBER_TOO_BIG = "number is too big or too small"; private static final String TEXT_OVERFLOW = "text segment overflow"; private static final String UNKNOWN_MNEMONIC = "unknown mnemonic"; private static final String UNKNOWN_MNEMONIC_TYPE = "unknown mnemonic type"; private static final String UNEXPECTED_LITERAL_END = "unexpected end of string literal"; private static final String UNEXPECTED_TOKEN = "unexpected token"; private static final String UNEXPECTED_TRASH = "unexpected trash at end of line"; private static final String UNKNOWN_DIRECTIVE = "unknown directive"; private static final String UNKNOWN_TRAP_ID = "unknown trap id"; /* * This parser runs up to two times over the code. * The first run tries to resolve all labels. * Instructions with labels that are unknown on the first run * are saved in unresolvedInstructions for the second run. */ private MemoryBuffer memory_; //where binary output is saved //however, there is no linker and hence no distinction between local and global labels private Hashtable<String, Integer> globalLabels_; private Tokenizer tokenizer_; private SegmentPointer dataPointer_; //data segment pointer private SegmentPointer textPointer_; //text segment pointer private SegmentPointer segmentPointer_; //active segment pointer private boolean stopOnUnresolvedLabel; //turned to true on second run private List<UnresolvedInstruction> unresolvedInstructions_; // private boolean hasGlobalMain; //workaround unnecessary when distinction between global and local labels public boolean hasGlobalMain() { return hasGlobalMain; } public Parser(int dataSegment, int textSegment) { tokenizer_ = new Tokenizer(); dataPointer_ = new SegmentPointer(dataSegment); textPointer_ = new SegmentPointer(textSegment); segmentPointer_ = textPointer_; } /* * ================================* Parse *================================ */ /** * another parse pass for jet unresolved labels, stop on any unresolved * label * * @param unresolvedInstructions * @param globalLabels * @param memory * @throws ParserException */ public void resolve(List<UnresolvedInstruction> unresolvedInstructions, Hashtable<String, Integer> globalLabels, MemoryBuffer memory) throws AssemblerException { stopOnUnresolvedLabel = true; unresolvedInstructions_ = unresolvedInstructions; globalLabels_ = globalLabels; memory_ = memory; for (UnresolvedInstruction instr : unresolvedInstructions_) { if (instr.inTextSegement) segmentPointer_ = textPointer_; else segmentPointer_ = dataPointer_; segmentPointer_.set(instr.position); parseLine(instr.tokens); } } /** * parse stream into memory, unresolved instructions are returned * * @param reader * @param globalLabels * @param memory * @return * @throws TokenizerException * @throws IOException * @throws ParserException */ public List<UnresolvedInstruction> parse(BufferedReader reader, Hashtable<String, Integer> globalLabels, MemoryBuffer memory) throws IOException, AssemblerException { stopOnUnresolvedLabel = false; globalLabels_ = globalLabels; tokenizer_.setReader(reader); memory_ = memory; unresolvedInstructions_ = new ArrayList<UnresolvedInstruction>(); Token[] tokens = tokenizer_.readLine(); while (tokens != null) { parseLine(tokens); tokens = tokenizer_.readLine(); } return unresolvedInstructions_; } /** * called from parse(...) and resolve(...) * * @param tokens * @throws ParserException */ private void parseLine(Token[] tokens) throws AssemblerException { if (tokens.length == 0) return; Token[] tmpTokens; try { switch (tokens[0].getTokenType()) { case Label: String label = tokens[0].getString(); addLabel(label.substring(0, label.length() - 1)); tmpTokens = new Token[tokens.length - 1]; System.arraycopy(tokens, 1, tmpTokens, 0, tokens.length - 1); parseLine(tmpTokens); break; case Mnemonic: parseMnemonic(tokens); break; case Directive: parseDirectives(tokens); break; default: throw new ParserException(UNEXPECTED_TOKEN, tokens[0]); } } catch (ParserException ex) { if (ex.getToken() == null) ex.setToken(tokens[0]); throw ex; } } /* * ===============================* Labels *=============================== */ /** * add label to labels if not exists * * @param label * @throws ParserException */ private void addLabel(String label) throws ParserException { if (globalLabels_.containsKey(label)) throw new ParserException(LABEL_ALREADY_EXISTS, null); globalLabels_.put(label, segmentPointer_.get()); } /** * replaces labels with their corresponding integer constant. If not * possible returns false * * @param tokens * @return */ private Token resolveLabels(Token[] tokens) { for (int i = 0; i < tokens.length; ++i) { Token t = tokens[i]; if (t.getTokenType() == TokenType.Identifier) { Integer position = globalLabels_.get(t.getString()); if (position == null) return t; tokens[i] = new Token(t.getPosition(), TokenType.IntegerConstant, position.toString()); } } return null; } /* * ==============================* Mnemonics *============================== */ /** * ALU_IMMEDIATE ALU_REGISTER BRANCH JUMP JUMP_REGISTER LOAD LOAD_IMMEDIATE * NOP SAVE SHIFT_IMMEDIATE TRAP * * @param tokens * @throws ParserException */ private void parseMnemonic(Token[] tokens) throws ParserException { Instruction instr = Instruction.fromMnemonic(tokens[0].getString()); //unknown mnemonic if (instr == null) throw new ParserException(UNKNOWN_MNEMONIC, tokens[0]); //unresolved labels Token t = resolveLabels(tokens); if (t != null) { if (stopOnUnresolvedLabel == true) throw new ParserException(LABEL_DOES_NOT_EXISTS, t); unresolvedInstructions_.add(new UnresolvedInstruction(tokens, segmentPointer_.get(), segmentPointer_ == textPointer_ ? true : false)); } else { switch (instr.calcInstType()) { case ALU_IMMEDIATE: aluImmediate(instr, tokens); break; case ALU_REGISTER: aluRegister(instr, tokens); break; case BRANCH: branch(instr, tokens); break; case JUMP: jump(instr, tokens); break; case JUMP_REGISTER: jumpRegister(instr, tokens); break; case LOAD: load(instr, tokens); break; case LOAD_IMMEDIATE: loadImmediate(instr, tokens); break; case NOP: nop(instr, tokens); break; case SAVE: save(instr, tokens); break; case SHIFT_REGISTER: shiftRegister(instr, tokens); break; case SHIFT_IMMEDIATE: shiftImmediate(instr, tokens); break; case TRAP: trap(instr, tokens); break; default: throw new ParserException(UNKNOWN_MNEMONIC_TYPE, tokens[0]); } if (memory_.getTextEnd() >= memory_.getDataBegin() && memory_.getTextEnd() <= memory_.getDataEnd()) { //TODO translate text data? throw exception? throw new ParserException(TEXT_OVERFLOW, tokens[0]); } memory_.writeWord(segmentPointer_.get(), instr.instrWord()); } segmentPointer_.add(4); memory_.setTextEnd(segmentPointer_.get()); } /** * e.g. addi r1,r0,200 * * @param instr * @param tokens * @return * @throws ParserException */ private void aluImmediate(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r0 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRs(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //+-200 ++i; boolean negative = false; if (tokens[i].getString().equals("+")) { negative = false; ++i; } else if (tokens[i].getString().equals("-")) { negative = true; ++i; } value = Integer.decode(tokens[i].getString()); if (negative) value = -value; instr.setOffset(value); if (i < tokens.length - 1) { throw new ParserException(UNEXPECTED_TRASH, tokens[++i]); } } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. add r1,r2,r3 * * @param instr * @param tokens * @return * @throws ParserException */ private void aluRegister(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRd(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r2 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRs(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r3 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } } /** * e.g. sll(v) r1,r2,r3 * * @param instr * @param tokens * @return * @throws ParserException */ private void shiftRegister(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRd(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r2 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r3 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRs(value); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } } /** * e.g. beqz r1,Label+4 * * @param instr * @param tokens * @return * @throws ParserException */ private void branch(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRs(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //Label+4 ++i; value = Integer.decode(tokens[i].getString()); value -= segmentPointer_.get() + 4; instr.setOffset(value >> 2); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. j Label+4 * * @param instr * @param tokens * @return * @throws ParserException */ private void jump(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { //Label+4 ++i; Integer value = Integer.decode(tokens[i].getString()); //i -= textSegment_ + 4; instr.setInstrIndex(value >> 2); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. jr r31 * * @param instr * @param tokens * @return * @throws ParserException */ private void jumpRegister(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { //r31 ++i; Integer value = Registers.instance().getInteger(tokens[i].getString()); instr.setRs(value); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } } /** * e.g. lb r1,Label+4(r2) * * @param instr * @param tokens * @return * @throws ParserException */ private void load(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //optional Label if (!tokens[i + 1].getString().equals("(")) { ++i; value = Integer.decode(tokens[i].getString()); instr.setOffset(value); } //TODO do it better //optional +4 ++i; if (tokens.length > i && tokens[i].getString().equals("+")) { ++i; value += Integer.decode(tokens[i].getString()); instr.setOffset(value); ++i; } if (tokens.length > i && tokens[i].getString().equals("-")) { ++i; value -= Integer.decode(tokens[i].getString()); instr.setOffset(value); ++i; } //optional (r2) if (tokens.length <= i) return; //( if (!tokens[i].getString().equals("(")) throw new ParserException(MISSING_PARANTHESIS, tokens[i]); //r2 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setBase(value); //) ++i; if (!tokens[i].getString().equals(")")) throw new ParserException(MISSING_PARANTHESIS, tokens[i]); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. lhi r1,Label+4 * * @param instr * @param tokens * @return * @throws ParserException */ private void loadImmediate(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //Label+4 ++i; boolean negative = false; if (tokens[i].getString().equals("+")) { negative = false; ++i; } else if (tokens[i].getString().equals("-")) { negative = true; ++i; } value = Integer.decode(tokens[i].getString()); if (negative) value = -value; instr.setOffset(value); if (i < tokens.length - 1) { throw new ParserException(UNEXPECTED_TRASH, tokens[++i]); } } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * nop * * @param instr * @param tokens * @return * @throws ParserException */ private void nop(Instruction instr, Token[] tokens) throws ParserException { //TODO: there must be a better solution for the same opcode problem with slli and nop if (tokens[0].getString().equalsIgnoreCase("slli")) { shiftImmediate(instr, tokens); return; } if (!tokens[0].getString().equalsIgnoreCase("nop")) throw new ParserException(NO_NOP, tokens[0]); if (tokens.length != 1) throw new ParserException(UNEXPECTED_TRASH, tokens[1]); } /** * e.g. sb Label+4(r0),r1 * * @param instr * @param tokens * @return * @throws ParserException */ private void save(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value = new Integer(0); //optional Label if (!tokens[i + 1].getString().equals("(")) { ++i; value = Integer.decode(tokens[i].getString()); instr.setOffset(value); } //TODO do it better //optional +4 ++i; if (tokens[i].getString().equals("+")) { ++i; value += Integer.decode(tokens[i].getString()); instr.setOffset(value); ++i; } if (tokens[i].getString().equals("-")) { ++i; value -= Integer.decode(tokens[i].getString()); instr.setOffset(value); ++i; } //optional (r0) if (tokens[i].getString().equals("(")) { //( if (!tokens[i].getString().equals("(")) throw new ParserException(MISSING_PARANTHESIS, tokens[i]); //r0 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setBase(value); //) ++i; if (!tokens[i].getString().equals(")")) throw new ParserException(MISSING_PARANTHESIS, tokens[i]); ++i; } //, if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. slli r2,r1,2 * * @param instr * @param tmpTokens * @return * @throws ParserException */ private void shiftImmediate(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { Integer value; //r2 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRd(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //r1 ++i; value = Registers.instance().getInteger(tokens[i].getString()); instr.setRt(value); //, ++i; if (!tokens[i].getString().equals(",")) throw new ParserException(MISSING_SEPARATOR, tokens[i]); //2 ++i; value = Integer.decode(tokens[i].getString()); instr.setSa(value); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NullPointerException ex) { throw new ParserException(NOT_A_REGISTER, tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /** * e.g. trap 0 * * @param instr * @param tokens * @return * @throws ParserException */ private void trap(Instruction instr, Token[] tokens) throws ParserException { int i = 0; try { ++i; int trapId = Integer.decode(tokens[i].getString()); if (trapId < 0 || trapId > 5) throw new ParserException(UNKNOWN_TRAP_ID, tokens[i]); instr.setRs(trapId); //if someone wants an explicit breakdown /*switch (trapId) { //terminate case 0: break; //open file case 1: break; //close file case 2: break; //read file case 3: break; //write file case 4: break; //formatted output to std out case 5: break; default: throw new ParserException("", null); }*/ } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } catch (InstructionException ex) { throw new ParserException(INSTRUCTION_EXCEPTION + ex.getMessage(), tokens[i]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i]); } } /* * =============================* Directives *============================= */ /** * .align .ascii .asciiz .byte .data .global .half .space .text .word * * @param tokens * @throws ParserException */ private void parseDirectives(Token[] tokens) throws ParserException { //unresolved labels Token t = resolveLabels(tokens); if (t != null) { if (stopOnUnresolvedLabel == true) throw new ParserException(LABEL_DOES_NOT_EXISTS, t); unresolvedInstructions_.add(new UnresolvedInstruction(tokens, segmentPointer_.get(), segmentPointer_ == textPointer_ ? true : false)); if (tokens[0].getString().equalsIgnoreCase(".word")) { segmentPointer_.add(4); } else if (tokens[0].getString().equalsIgnoreCase(".global")) { //do nothing } else { throw new ParserException(LABEL_NOT_ALLOWED_HERE, t); } return; } String name = tokens[0].getString(); if (name.equalsIgnoreCase(".align")) { align(tokens); } else if (name.equalsIgnoreCase(".ascii")) { ascii(tokens); } else if (name.equalsIgnoreCase(".asciiz")) { ascii(tokens); memory_.writeByte(segmentPointer_.get(), (byte) 0x0);//terminating null segmentPointer_.add(1); memory_.setDataEnd(segmentPointer_.get()); } else if (name.equalsIgnoreCase(".byte")) { byteDir(tokens); } else if (name.equalsIgnoreCase(".data")) { data(tokens); } else if (name.equalsIgnoreCase(".global")) { global(tokens); } else if (name.equalsIgnoreCase(".half")) { half(tokens); } else if (name.equalsIgnoreCase(".space")) { space(tokens); } else if (name.equalsIgnoreCase(".text")) { text(tokens); } else if (name.equalsIgnoreCase(".word")) { word(tokens); } else { throw new ParserException(UNKNOWN_DIRECTIVE, null); } } /** * e.g. .align 2 * * @param tokens */ private void align(Token[] tokens) throws ParserException { try { int align = Integer.decode(tokens[1].getString()); if (align > 0 && align < 32) align = 2 << align - 1; else if (align == 0) align = 1; else throw new ParserException(NUMBER_TOO_BIG, tokens[1]); while (segmentPointer_.get() % align != 0) segmentPointer_.add(1); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[1]); } } /** * e.g. .ascii "Foo\n" * * @param tokens * @throws ParserException */ private void ascii(Token[] tokens) throws ParserException { try { byte[] str = replaceEscapeSequences(tokens[1].getString()).getBytes(); for (int i = 0; i < str.length; ++i) { memory_.writeByte(segmentPointer_.get(), str[i]); segmentPointer_.add(1); memory_.setDataEnd(segmentPointer_.get()); } } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch(ParserException ex) { throw new ParserException(ex.getMessage(), tokens[1]); } } /** * e.g. .byte 1,2,3 * * @param tokens * @throws ParserException */ private void byteDir(Token[] tokens) throws ParserException { int i = 0; try { i = 1; do { int value = Integer.decode(tokens[i++].getString()); memory_.writeByte(segmentPointer_.get(), (byte) value); segmentPointer_.add(1); memory_.setDataEnd(segmentPointer_.get()); } while (i < tokens.length && tokens[i++].getString().equals(",")); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i - 1]); } } /** * e.g. .data 0x1000 * * @param tokens * @throws ParserException */ private void data(Token[] tokens) throws ParserException { segmentPointer_ = dataPointer_; if (tokens.length == 1) { return; } else if (tokens.length == 2) { try { int value = Integer.decode(tokens[1].getString()); if (value < 0) throw new ParserException(NUMBER_NEGATIVE, tokens[1]); dataPointer_.set(value); memory_.setDataBegin(value); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[1]); } } else { throw new ParserException(UNEXPECTED_TRASH, tokens[2]); } } /** * e.g. .global Label * * @param tokens * @throws ParserException */ private void global(Token[] tokens) throws ParserException { //TODO or not to do that's the question try { if (Integer.decode(tokens[1].getString()).intValue() == globalLabels_.get("main")) { hasGlobalMain = true; } } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_INSTRUCTION, tokens[0]); } } /** * e.g. .half 1,2,3 * * @param tokens * @throws ParserException */ private void half(Token[] tokens) throws ParserException { int i = 0; try { i = 1; do { int value = Integer.decode(tokens[i++].getString()); memory_.writeHalf(segmentPointer_.get(), (short) value); segmentPointer_.add(2); memory_.setDataEnd(segmentPointer_.get()); } while (i < tokens.length && tokens[i++].getString().equals(",")); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i - 1]); } } /** * e.g. .space 4,2,2 * * @param tokens * @throws ParserException */ private void space(Token[] tokens) throws ParserException { int i = 0; try { i = 1; do { int value = Integer.decode(tokens[i++].getString()); segmentPointer_.add(value); } while (i < tokens.length && tokens[i++].getString().equals(",")); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i - 1]); } } /** * e.g. .text 0x100 * * @param tokens * @throws ParserException */ private void text(Token[] tokens) throws ParserException { segmentPointer_ = textPointer_; if (tokens.length == 1) { return; } else if (tokens.length == 2) { try { int value = Integer.decode(tokens[1].getString()); if (value < 0) throw new ParserException(NUMBER_NEGATIVE, tokens[1]); textPointer_.set(value); // TODO what happens when multiple .text directives are found? memory_.setTextBegin(value); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[1]); } } else { throw new ParserException(UNEXPECTED_TRASH, tokens[2]); } } /** * e.g. .word 1,2,3 * * @param tokens * @throws ParserException */ private void word(Token[] tokens) throws ParserException { int i = 0; try { i = 1; do { int value = Integer.decode(tokens[i++].getString()); memory_.writeWord(segmentPointer_.get(), value); segmentPointer_.add(4); memory_.setDataEnd(segmentPointer_.get()); } while (i < tokens.length && tokens[i++].getString().equals(",")); } catch (ArrayIndexOutOfBoundsException ex) { throw new ParserException(INCOMPLETE_DIRECTIVE, tokens[0]); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, tokens[i - 1]); } } /* * ======================================================================== */ private String replaceEscapeSequences(String str) throws ParserException { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < str.length(); ++i) { char c = str.charAt(i); if (c != '\\') buffer.append(c); else { try { ++i; switch (str.charAt(i)) { case '0': buffer.append('\0'); break; case 'n': buffer.append('\n'); break; case 't': buffer.append('\t'); break; case 'f': buffer.append('\f'); break; case 'b': buffer.append('\b'); break; case '\\': buffer.append('\\'); break; case '\0': buffer.append('\0'); break; case '\'': buffer.append('\''); break; case '\"': buffer.append('\"'); break; case 'x': ++i; char x1 = str.charAt(i); ++i; char x2 = str.charAt(i); byte hex = Byte.decode("0x" + x1 + x2); buffer.append((char) hex); break; default: buffer.append(c); buffer.append(str.charAt(i)); } } catch (IndexOutOfBoundsException ex) { throw new ParserException(UNEXPECTED_LITERAL_END, null); } catch (NumberFormatException ex) { throw new ParserException(NOT_A_NUMBER, null); } } } return buffer.toString(); } }