mirror of
				https://github.com/simon987/Much-Assembly-Required.git
				synced 2025-11-04 10:06:54 +00:00 
			
		
		
		
	DW and relative addressing constant folding support.
This commit is contained in:
		
							parent
							
								
									74ab5b6d88
								
							
						
					
					
						commit
						292f737971
					
				@ -79,7 +79,7 @@ public class Assembler {
 | 
			
		||||
        String[] tokens = line.trim().split("\\s+");
 | 
			
		||||
        String mnemonic = tokens[0];
 | 
			
		||||
 | 
			
		||||
        if (mnemonic.toUpperCase().equals("ORG")) {
 | 
			
		||||
        if (mnemonic.equalsIgnoreCase("ORG")) {
 | 
			
		||||
            if (tokens.length > 1) {
 | 
			
		||||
                try {
 | 
			
		||||
                    result.origin = (Integer.decode(tokens[1]));
 | 
			
		||||
@ -124,6 +124,9 @@ public class Assembler {
 | 
			
		||||
        return line.replaceAll("\\s+", "").isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static final Pattern DUP_PATTERN = Pattern.compile("(.+)\\s+DUP(.+)");
 | 
			
		||||
    private static final Pattern STRING_PATTERN = Pattern.compile("\"(.*)\"$");
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse the DW instruction (Define word). Handles DUP operator
 | 
			
		||||
     *
 | 
			
		||||
@ -132,7 +135,7 @@ public class Assembler {
 | 
			
		||||
     * @param labels      Map of labels
 | 
			
		||||
     * @return Encoded instruction, null if the line is not a DW instruction
 | 
			
		||||
     */
 | 
			
		||||
    private static byte[] parseDWInstruction(String line, HashMap<String, Character> labels, int currentLine)
 | 
			
		||||
    private byte[] parseDWInstruction(String line, HashMap<String, Character> labels, int currentLine)
 | 
			
		||||
            throws InvalidOperandException {
 | 
			
		||||
 | 
			
		||||
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
 | 
			
		||||
@ -140,7 +143,7 @@ public class Assembler {
 | 
			
		||||
 | 
			
		||||
        //System.out.println(line);
 | 
			
		||||
 | 
			
		||||
        if (line.length() >= 2 && line.substring(0, 2).toUpperCase().equals("DW")) {
 | 
			
		||||
        if (line.length() >= 2 && line.substring(0, 2).equalsIgnoreCase("DW")) {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
 | 
			
		||||
@ -150,18 +153,10 @@ public class Assembler {
 | 
			
		||||
                for (String value : values) {
 | 
			
		||||
 | 
			
		||||
                    value = value.trim();
 | 
			
		||||
 | 
			
		||||
                    String[] valueTokens = value.split("\\s+");
 | 
			
		||||
 | 
			
		||||
                    //Handle DUP operator
 | 
			
		||||
                    if (valueTokens.length == 2 && valueTokens[1].toUpperCase().contains("DUP(")) {
 | 
			
		||||
                        out.write(parseDUPOperator16(valueTokens, labels, currentLine));
 | 
			
		||||
                    } else if (value.startsWith("\"") && value.endsWith("\"")) {
 | 
			
		||||
                        //Handle string
 | 
			
		||||
 | 
			
		||||
                        //Unescape the string
 | 
			
		||||
                        String string = value.substring(1, value.length() - 1);
 | 
			
		||||
 | 
			
		||||
                    Matcher m = STRING_PATTERN.matcher(value);
 | 
			
		||||
                    if (m.lookingAt()) {
 | 
			
		||||
                        // Parse string
 | 
			
		||||
                        String string = m.group(1);
 | 
			
		||||
                        try {
 | 
			
		||||
                            string = StringEscapeUtils.unescapeJava(string);
 | 
			
		||||
                        } catch (IllegalArgumentException e) {
 | 
			
		||||
@ -169,39 +164,39 @@ public class Assembler {
 | 
			
		||||
                                    "Invalid string operand \"" + string + "\": " + e.getMessage(),
 | 
			
		||||
                                    currentLine);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        out.write(string.getBytes(StandardCharsets.UTF_16BE));
 | 
			
		||||
                    } else if (labels != null && labels.containsKey(value)) {
 | 
			
		||||
                        //Handle label
 | 
			
		||||
                        out.writeChar(labels.get(value));
 | 
			
		||||
 | 
			
		||||
                    } else {
 | 
			
		||||
                        //Handle integer value
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    int factor;
 | 
			
		||||
                    if (m.usePattern(DUP_PATTERN).lookingAt()) {
 | 
			
		||||
                        // Get DUP factor
 | 
			
		||||
                        TokenParser parser = new TokenParser(m.group(1), currentLine, new HashMap<>());
 | 
			
		||||
                        try {
 | 
			
		||||
                            out.writeChar(Integer.decode(value));
 | 
			
		||||
 | 
			
		||||
                        } catch (NumberFormatException e) {
 | 
			
		||||
                            //Handle assumed label
 | 
			
		||||
                            if (labels == null) {
 | 
			
		||||
 | 
			
		||||
                                // Write placeholder word
 | 
			
		||||
                                out.writeChar(0);
 | 
			
		||||
 | 
			
		||||
                            } else {
 | 
			
		||||
 | 
			
		||||
                                //Integer.decode failed, try binary
 | 
			
		||||
                                if (value.startsWith("0b")) {
 | 
			
		||||
                                    try {
 | 
			
		||||
                                        out.writeChar(Integer.parseInt(value.substring(2), 2));
 | 
			
		||||
                                    } catch (NumberFormatException e2) {
 | 
			
		||||
                                        throw new InvalidOperandException("Invalid operand \"" + value + '"', currentLine);
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    throw new InvalidOperandException("Invalid operand \"" + value + '"', currentLine);
 | 
			
		||||
 | 
			
		||||
                                }
 | 
			
		||||
                            if (TokenParser.TokenType.Constant !=
 | 
			
		||||
                                    parser.getNextToken(true, TokenParser.ParseContext.Value)) {
 | 
			
		||||
                                throw new InvalidOperandException("Invalid DUP factor " + m.group(1), currentLine);
 | 
			
		||||
                            }
 | 
			
		||||
                        } catch (AssemblyException ae) {
 | 
			
		||||
                            throw new InvalidOperandException("Couldn't parse DUP factor " + m.group(1), currentLine);
 | 
			
		||||
                        }
 | 
			
		||||
                        factor = parser.lastInt;
 | 
			
		||||
                        if (factor > MEM_SIZE) {
 | 
			
		||||
                            throw new InvalidOperandException(
 | 
			
		||||
                                    "Factor '" + factor + "' exceeds total memory size", currentLine);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        value = m.group(2);
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        // Parse as single number
 | 
			
		||||
                        factor = 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Operand operand = new Operand(value, labels, registerSet, currentLine);
 | 
			
		||||
                    char s = (char)operand.getData();
 | 
			
		||||
                    for (int i = 0; i < factor; i++) {
 | 
			
		||||
                        out.write(Util.getHigherByte(s));
 | 
			
		||||
                        out.write(Util.getLowerByte(s));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -224,61 +219,10 @@ public class Assembler {
 | 
			
		||||
     * @param currentLine Current line number
 | 
			
		||||
     * @return Encoded instruction, null if the line is not a DW instruction
 | 
			
		||||
     */
 | 
			
		||||
    private static byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
 | 
			
		||||
    private byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
 | 
			
		||||
        return parseDWInstruction(line, null, currentLine);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse the dup operator
 | 
			
		||||
     *
 | 
			
		||||
     * @param valueTokens Value tokens e.g. {"8", "DUP(12)"}
 | 
			
		||||
     * @param labels      Map of labels
 | 
			
		||||
     * @return The encoded instruction
 | 
			
		||||
     */
 | 
			
		||||
    private static byte[] parseDUPOperator16(String[] valueTokens, HashMap<String, Character> labels, int currentLine)
 | 
			
		||||
            throws InvalidOperandException {
 | 
			
		||||
 | 
			
		||||
        ByteArrayOutputStream out = new ByteArrayOutputStream();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            int factor = Integer.decode(valueTokens[0]);
 | 
			
		||||
 | 
			
		||||
            if (factor > MEM_SIZE) {
 | 
			
		||||
                throw new InvalidOperandException("Factor '" + factor + "' exceeds total memory size", currentLine);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            String value = valueTokens[1].substring(4, valueTokens[1].lastIndexOf(')'));
 | 
			
		||||
 | 
			
		||||
            //Handle label
 | 
			
		||||
            if (labels != null && labels.containsKey(value)) {
 | 
			
		||||
                //Label value is casted to byte
 | 
			
		||||
                for (int i = 0; i < factor; i++) {
 | 
			
		||||
                    char s = labels.get(value);
 | 
			
		||||
 | 
			
		||||
                    out.write(Util.getHigherByte(s));
 | 
			
		||||
                    out.write(Util.getLowerByte(s));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else {
 | 
			
		||||
                //Handle integer value
 | 
			
		||||
                char s = (char) (int) Integer.decode(value);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < factor; i++) {
 | 
			
		||||
                    out.write(Util.getHigherByte(s));
 | 
			
		||||
                    out.write(Util.getLowerByte(s));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            throw new InvalidOperandException("Usage: <factor> DUP(<value>)", currentLine);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return out.toByteArray();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check for and handle section declarations (.text and .data)
 | 
			
		||||
     *
 | 
			
		||||
@ -289,13 +233,13 @@ public class Assembler {
 | 
			
		||||
 | 
			
		||||
        String[] tokens = line.split("\\s+");
 | 
			
		||||
 | 
			
		||||
        if (tokens[0].toUpperCase().equals(".TEXT")) {
 | 
			
		||||
        if (tokens[0].equalsIgnoreCase(".TEXT")) {
 | 
			
		||||
 | 
			
		||||
            result.defineSection(Section.TEXT, currentLine, currentOffset);
 | 
			
		||||
            result.disassemblyLines.add(".text");
 | 
			
		||||
            throw new PseudoInstructionException(currentLine);
 | 
			
		||||
 | 
			
		||||
        } else if (tokens[0].toUpperCase().equals(".DATA")) {
 | 
			
		||||
        } else if (tokens[0].equalsIgnoreCase(".DATA")) {
 | 
			
		||||
 | 
			
		||||
            LogManager.LOGGER.fine("DEBUG: .data @" + currentLine);
 | 
			
		||||
 | 
			
		||||
@ -304,9 +248,8 @@ public class Assembler {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final Pattern EQU_PATTERN = Pattern.compile("([\\w]+)[^\\S\\n]+EQU[^\\S\\n]+(.+)",
 | 
			
		||||
                Pattern.CASE_INSENSITIVE
 | 
			
		||||
            );
 | 
			
		||||
    private static final Pattern EQU_PATTERN = Pattern.compile("([\\w]+)\\s+EQU\\s+(.+)",
 | 
			
		||||
                Pattern.CASE_INSENSITIVE);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Check for and handle the EQU instruction
 | 
			
		||||
@ -316,7 +259,7 @@ public class Assembler {
 | 
			
		||||
     * @param currentLine Current line number
 | 
			
		||||
     */
 | 
			
		||||
    private static void checkForEQUInstruction(String line, HashMap<String, Character> labels,
 | 
			
		||||
                                               int currentLine, RegisterSet registers)
 | 
			
		||||
                                               int currentLine)
 | 
			
		||||
            throws AssemblyException {
 | 
			
		||||
        /*  the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++
 | 
			
		||||
         *  usage: constant_name EQU <immediate_value>
 | 
			
		||||
@ -347,9 +290,6 @@ public class Assembler {
 | 
			
		||||
     * the errors, if any.
 | 
			
		||||
     */
 | 
			
		||||
    public AssemblyResult parse(String text) {
 | 
			
		||||
 | 
			
		||||
        int currentLine;
 | 
			
		||||
 | 
			
		||||
        //Split in lines
 | 
			
		||||
        AssemblyResult result = new AssemblyResult(config);
 | 
			
		||||
        String[] lines = text.split("\n");
 | 
			
		||||
@ -451,7 +391,7 @@ public class Assembler {
 | 
			
		||||
 | 
			
		||||
                //Check for pseudo instructions
 | 
			
		||||
                checkForSectionDeclaration(line, result, currentLine, currentOffset);
 | 
			
		||||
                checkForEQUInstruction(line, result.labels, currentLine, registerSet);
 | 
			
		||||
                checkForEQUInstruction(line, result.labels, currentLine);
 | 
			
		||||
                checkForORGInstruction(line, result, currentLine);
 | 
			
		||||
 | 
			
		||||
                for (String label : result.labels.keySet()) {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@ import net.simon987.mar.server.assembly.exception.AssemblyException;
 | 
			
		||||
import net.simon987.mar.server.assembly.exception.InvalidOperandException;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Stack;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents an operand of an instruction. An operand can refer to a
 | 
			
		||||
@ -36,14 +35,14 @@ public class Operand {
 | 
			
		||||
     * Data of the operand. This will be appended after the instruction.
 | 
			
		||||
     * For example, "[AX+3]" value={index of AX] + {number of registers}, Data=3
 | 
			
		||||
     */
 | 
			
		||||
    private int data = 0;
 | 
			
		||||
    private char data = 0;
 | 
			
		||||
 | 
			
		||||
    public Operand(OperandType type, int value) {
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        this.value = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Operand(OperandType type, int value, int data) {
 | 
			
		||||
    public Operand(OperandType type, int value, char data) {
 | 
			
		||||
        this(type, value);
 | 
			
		||||
        this.data = data;
 | 
			
		||||
    }
 | 
			
		||||
@ -90,7 +89,7 @@ public class Operand {
 | 
			
		||||
                value = Operand.IMMEDIATE_VALUE_MEM;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!parseRegExpr(registerSet, labels)) {
 | 
			
		||||
            if (!parseRegExpr(registerSet, labels, line)) {
 | 
			
		||||
                if (labels == null) {
 | 
			
		||||
                    type = OperandType.MEMORY_IMM16;
 | 
			
		||||
                    data = 0;
 | 
			
		||||
@ -127,206 +126,6 @@ public class Operand {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Attempt to parse an integer
 | 
			
		||||
     *
 | 
			
		||||
     * @param text Text to parse, can be a label or immediate value (hex or dec)
 | 
			
		||||
     * @return true if successful, false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    private boolean parseImmediate(String text) {
 | 
			
		||||
 | 
			
		||||
        text = text.trim();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            //Try IMM
 | 
			
		||||
            type = OperandType.IMMEDIATE16;
 | 
			
		||||
            data = Integer.decode(text);
 | 
			
		||||
            value = IMMEDIATE_VALUE;
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
 | 
			
		||||
            //Try Binary number (format 0bXXXX)
 | 
			
		||||
            if (text.startsWith("0b")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = Integer.parseInt(text.substring(2), 2);
 | 
			
		||||
                    value = IMMEDIATE_VALUE;
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (text.startsWith("0o")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = Integer.parseInt(text.substring(2), 8);
 | 
			
		||||
                    value = IMMEDIATE_VALUE;
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Attempt to parse a user-defined label
 | 
			
		||||
     *
 | 
			
		||||
     * @param text   Text to parse
 | 
			
		||||
     * @param labels Map of labels
 | 
			
		||||
     * @return true if parsing is successful, false otherwise
 | 
			
		||||
     */
 | 
			
		||||
    private boolean parseLabel(String text, HashMap<String, Character> labels) {
 | 
			
		||||
 | 
			
		||||
        text = text.trim();
 | 
			
		||||
 | 
			
		||||
        if (labels == null) {
 | 
			
		||||
            return false;
 | 
			
		||||
        } else if (labels.containsKey(text)) {
 | 
			
		||||
            type = OperandType.IMMEDIATE16;
 | 
			
		||||
            data = labels.get(text);
 | 
			
		||||
            value = IMMEDIATE_VALUE;
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Interface allowing parse states to be manipulated, evaluated, and stacked.
 | 
			
		||||
     */
 | 
			
		||||
    private static class ParseOperator {
 | 
			
		||||
        public int getPrecedence() {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        public int apply(int other) {
 | 
			
		||||
            return other;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public final int closeExpect;
 | 
			
		||||
 | 
			
		||||
        public ParseOperator(int closeExpect) {
 | 
			
		||||
            this.closeExpect = closeExpect;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class ParseOperatorUnary extends ParseOperator {
 | 
			
		||||
        TokenParser.UnaryOperatorType op;
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getPrecedence() {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int apply(int other) {
 | 
			
		||||
            return op.apply(other);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ParseOperatorUnary(int closeExpect, TokenParser.UnaryOperatorType op) {
 | 
			
		||||
            super(closeExpect);
 | 
			
		||||
            this.op = op;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class ParseOperatorBinary extends ParseOperator {
 | 
			
		||||
        private final TokenParser.BinaryOperatorType op;
 | 
			
		||||
        private final int value;
 | 
			
		||||
        @Override
 | 
			
		||||
        public int getPrecedence() {
 | 
			
		||||
            return op.precedence;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public int apply(int other) {
 | 
			
		||||
            return op.apply(value, other);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ParseOperatorBinary(int closeExpect, TokenParser.BinaryOperatorType op, int value) {
 | 
			
		||||
            super(closeExpect);
 | 
			
		||||
            this.op = op;
 | 
			
		||||
            this.value = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static int parseConstExpression(String text, int line, HashMap<String, Character> labels)
 | 
			
		||||
            throws AssemblyException {
 | 
			
		||||
        TokenParser parser = new TokenParser(text, line, labels);
 | 
			
		||||
        Stack<ParseOperator> parseOps = new Stack<>();
 | 
			
		||||
        int closeExpect = -1; // No closing parenthesis expected
 | 
			
		||||
        TokenParser.ParseContext context = TokenParser.ParseContext.Value;
 | 
			
		||||
        int lastValue = 0;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            TokenParser.TokenType ty = parser.getNextToken(true, context);
 | 
			
		||||
            if (context == TokenParser.ParseContext.Value) {
 | 
			
		||||
                // Parse value
 | 
			
		||||
                if (ty == TokenParser.TokenType.UnaryOperator) {
 | 
			
		||||
                    parseOps.push(new ParseOperatorUnary(closeExpect, parser.lastUnary));
 | 
			
		||||
                    closeExpect = -1;
 | 
			
		||||
                }
 | 
			
		||||
                else if (ty == TokenParser.TokenType.GroupOperator) {
 | 
			
		||||
                    if (parser.lastGroup.end) throw new AssemblyException("Unexpected group close", line);
 | 
			
		||||
                    if (closeExpect != -1) parseOps.push(new ParseOperator(closeExpect));
 | 
			
		||||
                    closeExpect = parser.lastGroup.groupType;
 | 
			
		||||
                } else if (ty == TokenParser.TokenType.Constant) {
 | 
			
		||||
                    lastValue = parser.lastInt;
 | 
			
		||||
                    context = TokenParser.ParseContext.TackOn;
 | 
			
		||||
                } else throw new AssemblyException("Value not found", line);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Parse modifier
 | 
			
		||||
                if (ty == TokenParser.TokenType.EOF || ty == TokenParser.TokenType.GroupOperator) {
 | 
			
		||||
                    if (ty == TokenParser.TokenType.GroupOperator && !parser.lastGroup.end)
 | 
			
		||||
                        throw new AssemblyException("Unexpected group open", line);
 | 
			
		||||
                    if (closeExpect != -1) {
 | 
			
		||||
                        if (ty == TokenParser.TokenType.EOF)
 | 
			
		||||
                            throw new AssemblyException("Unclosed group", line);
 | 
			
		||||
                        else if (closeExpect != parser.lastGroup.groupType)
 | 
			
		||||
                            throw new AssemblyException("Unmatched group ends", line);
 | 
			
		||||
                        else {
 | 
			
		||||
                            closeExpect = -1;
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    boolean completed = false;
 | 
			
		||||
 | 
			
		||||
                    //Evaluation chain
 | 
			
		||||
                    while (!parseOps.isEmpty()) {
 | 
			
		||||
                        ParseOperator op = parseOps.pop();
 | 
			
		||||
                        if (op.closeExpect != -1) {
 | 
			
		||||
                            if (ty == TokenParser.TokenType.EOF) throw new AssemblyException("Unclosed group", line);
 | 
			
		||||
                            else if (op.closeExpect != parser.lastGroup.groupType)
 | 
			
		||||
                                throw new AssemblyException("Unmatched group ends", line);
 | 
			
		||||
                            lastValue = op.apply(lastValue);
 | 
			
		||||
                            completed = true;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        lastValue = op.apply(lastValue);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!completed) {
 | 
			
		||||
                        if (ty == TokenParser.TokenType.EOF) return lastValue;
 | 
			
		||||
                        else if (parser.lastGroup.groupType != -1)
 | 
			
		||||
                            throw new AssemblyException("Unexpected group close", line);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else if (ty == TokenParser.TokenType.BinaryOperator) {
 | 
			
		||||
                    TokenParser.BinaryOperatorType bop = parser.lastBinary;
 | 
			
		||||
                    while (closeExpect == -1 && !parseOps.empty()) {
 | 
			
		||||
                        ParseOperator op = parseOps.peek();
 | 
			
		||||
                        if (bop.precedence <= op.getPrecedence()) break;
 | 
			
		||||
                        lastValue = op.apply(lastValue);
 | 
			
		||||
                        closeExpect = op.closeExpect;
 | 
			
		||||
                        parseOps.pop();
 | 
			
		||||
                    }
 | 
			
		||||
                    parseOps.push(new ParseOperatorBinary(closeExpect, bop, lastValue));
 | 
			
		||||
                    closeExpect = -1;
 | 
			
		||||
                    context = TokenParser.ParseContext.Value;
 | 
			
		||||
                }
 | 
			
		||||
                else throw new AssemblyException("Modifier or end not found", line);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Attempt to parse a register
 | 
			
		||||
@ -354,7 +153,7 @@ public class Operand {
 | 
			
		||||
     *
 | 
			
		||||
     * @return true if successful
 | 
			
		||||
     */
 | 
			
		||||
    private boolean parseRegExpr(RegisterSet registerSet, HashMap<String, Character> labels) {
 | 
			
		||||
    private boolean parseRegExpr(RegisterSet registerSet, HashMap<String, Character> labels, int line) {
 | 
			
		||||
 | 
			
		||||
        String expr;
 | 
			
		||||
 | 
			
		||||
@ -374,7 +173,10 @@ public class Operand {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (expr.replaceAll("\\s+", "").isEmpty()) {
 | 
			
		||||
        //Remove white space
 | 
			
		||||
        expr = expr.replaceAll("\\s+", "");
 | 
			
		||||
 | 
			
		||||
        if (expr.isEmpty()) {
 | 
			
		||||
            //No data specified
 | 
			
		||||
            type = OperandType.MEMORY_REG16;
 | 
			
		||||
            value += registerSet.size(); //refers to memory.
 | 
			
		||||
@ -382,64 +184,22 @@ public class Operand {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Remove white space
 | 
			
		||||
        expr = expr.replaceAll("\\s+", "");
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            type = OperandType.MEMORY_REG_DISP16;
 | 
			
		||||
 | 
			
		||||
            if (labels != null) {
 | 
			
		||||
 | 
			
		||||
                Character address = labels.get(expr.replaceAll("[^A-Za-z0-9_]", ""));
 | 
			
		||||
                if (address != null) {
 | 
			
		||||
                    data = (expr.startsWith("-")) ? -address : address;
 | 
			
		||||
                    value += registerSet.size() * 2;//refers to memory with disp
 | 
			
		||||
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //label is invalid
 | 
			
		||||
            data = Integer.decode(expr);
 | 
			
		||||
            value += registerSet.size() * 2; //refers to memory with disp
 | 
			
		||||
        if (!expr.startsWith("-") && !expr.startsWith("+")) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        type = OperandType.MEMORY_REG_DISP16;
 | 
			
		||||
        value += registerSet.size() * 2;
 | 
			
		||||
        if (labels == null) {
 | 
			
		||||
            data = 0;
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
 | 
			
		||||
            //Integer.decode failed, try binary
 | 
			
		||||
            if (expr.startsWith("+0b")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = Integer.parseInt(expr.substring(3), 2);
 | 
			
		||||
                    value += registerSet.size() * 2; //refers to memory with disp
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (expr.startsWith("-0b")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = -Integer.parseInt(expr.substring(3), 2);
 | 
			
		||||
                    value += registerSet.size() * 2; //refers to memory with disp
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (expr.startsWith("+0o")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = Integer.parseInt(expr.substring(3), 8);
 | 
			
		||||
                    value += registerSet.size() * 2; //refers to memory with disp
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (expr.startsWith("-0o")) {
 | 
			
		||||
                try {
 | 
			
		||||
                    data = -Integer.parseInt(expr.substring(3), 8);
 | 
			
		||||
                    value += registerSet.size() * 2; //refers to memory with disp
 | 
			
		||||
                    return true;
 | 
			
		||||
                } catch (NumberFormatException e2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        expr = "0" + expr;
 | 
			
		||||
        TokenParser parser = new TokenParser(expr, line, labels);
 | 
			
		||||
        try {
 | 
			
		||||
            data = parser.parseConstExpression();
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (AssemblyException ex) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -452,7 +212,7 @@ public class Operand {
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getData() {
 | 
			
		||||
    public char getData() {
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@ import java.util.regex.Matcher;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import net.simon987.mar.server.assembly.exception.AssemblyException;
 | 
			
		||||
import org.apache.velocity.runtime.directive.Parse;
 | 
			
		||||
 | 
			
		||||
public class TokenParser {
 | 
			
		||||
 | 
			
		||||
@ -360,7 +359,12 @@ public class TokenParser {
 | 
			
		||||
        throw new AssemblyException("Invalid token found", line);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int parseConstExpression()
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses and evaluates a constant expression from the input.
 | 
			
		||||
     * @return the value of the constant expression
 | 
			
		||||
     * @throws AssemblyException when a constant expression cannot be parsed
 | 
			
		||||
     */
 | 
			
		||||
    public char parseConstExpression()
 | 
			
		||||
            throws AssemblyException {
 | 
			
		||||
        Stack<ParseOperator> parseOps = new Stack<>();
 | 
			
		||||
        int closeExpect = -1; // No closing parenthesis expected
 | 
			
		||||
@ -423,7 +427,7 @@ public class TokenParser {
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!completed) {
 | 
			
		||||
                        if (ty == TokenParser.TokenType.EOF) {
 | 
			
		||||
                            return lastValue;
 | 
			
		||||
                            return (char)lastValue;
 | 
			
		||||
                        } else if (lastGroup.groupType != -1) {
 | 
			
		||||
                            throw new AssemblyException("Unexpected group close", line);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
@ -59,4 +59,14 @@ public class DwDirectiveTest {
 | 
			
		||||
        assertTrue(res.ar.exceptions.isEmpty());
 | 
			
		||||
        assertEquals('"' ,res.memValue(res.ar.origin));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void dupTest() {
 | 
			
		||||
        String code = "DW 10 DUP(4)";
 | 
			
		||||
 | 
			
		||||
        TestExecutionResult res = TestHelper.executeCode(code);
 | 
			
		||||
 | 
			
		||||
        assertTrue(res.ar.exceptions.isEmpty());
 | 
			
		||||
        assertEquals(4 ,res.memValue(res.ar.origin));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -54,7 +54,7 @@ public class OperandTest {
 | 
			
		||||
            Operand imm2 = new Operand(" -12", labels, registerSet, 0);
 | 
			
		||||
            assertEquals(OperandType.IMMEDIATE16, imm2.getType());
 | 
			
		||||
            assertEquals(Operand.IMMEDIATE_VALUE, imm2.getValue());
 | 
			
		||||
            assertEquals(-12, imm2.getData());
 | 
			
		||||
            assertEquals((char)-12, imm2.getData());
 | 
			
		||||
 | 
			
		||||
            Operand imm3 = new Operand(" 0xABCD", labels, registerSet, 0);
 | 
			
		||||
            assertEquals(OperandType.IMMEDIATE16, imm3.getType());
 | 
			
		||||
@ -75,7 +75,7 @@ public class OperandTest {
 | 
			
		||||
            Operand mem2 = new Operand("[-12    ]", labels, registerSet, 0);
 | 
			
		||||
            assertEquals(OperandType.MEMORY_IMM16, mem2.getType());
 | 
			
		||||
            assertEquals(Operand.IMMEDIATE_VALUE_MEM, mem2.getValue());
 | 
			
		||||
            assertEquals(-12, mem2.getData());
 | 
			
		||||
            assertEquals((char)-12, mem2.getData());
 | 
			
		||||
 | 
			
		||||
            Operand mem3 = new Operand(" [ 0xABCD]", labels, registerSet, 0);
 | 
			
		||||
            assertEquals(OperandType.MEMORY_IMM16, mem3.getType());
 | 
			
		||||
@ -117,7 +117,7 @@ public class OperandTest {
 | 
			
		||||
            Operand mem10 = new Operand("[   B -    label1     ]", labels, registerSet, 0);
 | 
			
		||||
            assertEquals(OperandType.MEMORY_REG_DISP16, mem10.getType());
 | 
			
		||||
            assertEquals(2 + 2 * registerSet.size(), mem10.getValue());
 | 
			
		||||
            assertEquals(-10, mem10.getData());
 | 
			
		||||
            assertEquals((char)-10, mem10.getData());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        } catch (InvalidOperandException e) {
 | 
			
		||||
@ -201,11 +201,13 @@ public class OperandTest {
 | 
			
		||||
            fail();
 | 
			
		||||
        } catch (InvalidOperandException ignored) {
 | 
			
		||||
        }
 | 
			
		||||
        /* This should most likely be allowed under constant folding.
 | 
			
		||||
        try {
 | 
			
		||||
            new Operand("[A + -1]", labels, registerSet, 0);
 | 
			
		||||
            fail();
 | 
			
		||||
        } catch (InvalidOperandException ignored) {
 | 
			
		||||
        }
 | 
			
		||||
        */
 | 
			
		||||
        try {
 | 
			
		||||
            new Operand("[A + ]", labels, registerSet, 0);
 | 
			
		||||
            fail();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user