de.codesourcery.jasm16.parser.InstructionNodeTest.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.jasm16.parser.InstructionNodeTest.java

Source

/**
 * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.codesourcery.jasm16.parser;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.ArrayUtils;

import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.AddressingMode;
import de.codesourcery.jasm16.Register;
import de.codesourcery.jasm16.ast.AST;
import de.codesourcery.jasm16.ast.ASTNode;
import de.codesourcery.jasm16.ast.ASTUtils;
import de.codesourcery.jasm16.ast.InstructionNode;
import de.codesourcery.jasm16.ast.OperandNode;
import de.codesourcery.jasm16.ast.StatementNode;
import de.codesourcery.jasm16.ast.TermNode;
import de.codesourcery.jasm16.compiler.CompilationUnit;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ICompilationUnit;
import de.codesourcery.jasm16.compiler.io.IObjectCodeWriter;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.utils.Misc;

public class InstructionNodeTest extends TestHelper {

    public void testDetermineAddressingModeWithLabel() throws Exception {

        ICompilationUnit unit = compile("    SET a , label\n" + ":label .word 0x1234");

        assertFalse(unit.getAST().hasErrors());

        StatementNode stmt1 = (StatementNode) unit.getAST().child(0);
        InstructionNode instr = (InstructionNode) stmt1.child(0);

        assertEquals(AddressingMode.REGISTER, instr.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instr.getOperand(1).getAddressingMode());
    }

    public void testDetermineAddressingModeWithLabelExpression1() throws Exception {

        ICompilationUnit unit = compile("    SET a , label+10\n" + ":label .word 0x1234");

        assertFalse(unit.getAST().hasErrors());

        StatementNode stmt1 = (StatementNode) unit.getAST().child(0);
        InstructionNode instr = (InstructionNode) stmt1.child(0);

        assertEquals(AddressingMode.REGISTER, instr.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instr.getOperand(1).getAddressingMode());
    }

    public void testRegisterIndirectWithNegativeOffset() throws Exception {

        ICompilationUnit unit = compile("    SET a , [b-3]");

        assertFalse(unit.getAST().hasErrors());

        StatementNode stmt1 = (StatementNode) unit.getAST().child(0);
        InstructionNode instr = (InstructionNode) stmt1.child(0);

        assertEquals(AddressingMode.REGISTER, instr.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.INDIRECT_REGISTER_OFFSET, instr.getOperand(1).getAddressingMode());
    }

    public void testDetermineAddressingModeWithLabelExpression2() throws Exception {

        ICompilationUnit unit = compile("    SET PC , 10+label\n" + ":label .word 0x1234");

        assertFalse(unit.getAST().hasErrors());

        StatementNode stmt1 = (StatementNode) unit.getAST().child(0);
        InstructionNode instr = (InstructionNode) stmt1.child(0);

        assertEquals(AddressingMode.REGISTER, instr.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instr.getOperand(1).getAddressingMode());
    }

    public void testDetermineAddressingMode() throws ParseException {
        InstructionNode instruction = compileInstruction("SET PUSH , 10");
        assertEquals(AddressingMode.INDIRECT_REGISTER_PREDECREMENT, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET a , b");
        assertEquals(AddressingMode.REGISTER, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.REGISTER, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET [a] , 10");
        assertEquals(AddressingMode.INDIRECT_REGISTER, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET [sp++] , 10");
        assertEquals(AddressingMode.INDIRECT_REGISTER_POSTINCREMENT, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET [0x2000+a] , 10");
        assertEquals(AddressingMode.INDIRECT_REGISTER_OFFSET, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET [0x2000] , 10");
        assertEquals(AddressingMode.INDIRECT, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());

        instruction = compileInstruction("SET a, 10");
        assertEquals(AddressingMode.REGISTER, instruction.getOperand(0).getAddressingMode());
        assertEquals(AddressingMode.IMMEDIATE, instruction.getOperand(1).getAddressingMode());
    }

    private InstructionNode compileInstruction(String source) {
        ASTNode node = new InstructionNode().parse(createParseContext(source));

        if (node.hasErrors()) {
            ASTUtils.printAST(node);
            Misc.printCompilationErrors(CompilationUnit.createInstance("string", source), source, true);
        }
        assertFalse(node.hasErrors());
        assertEquals(InstructionNode.class, node.getClass());
        return (InstructionNode) node;
    }

    public void testTextRegion() throws Exception {
        final String source = "SET [0x2000+I], [A]";

        ICompilationUnit unit = compile(source);
        assertFalse(unit.hasErrors());

        AST ast = unit.getAST();

        final InstructionNode instruction = ((StatementNode) unit.getAST().child(0)).getInstructionNode();
        ASTUtils.printAST(instruction);

        assertSourceCode(source, instruction);

        // operand A checks
        ASTNode operandA = ast.getNodeInRange(4);
        assertNotNull(operandA);
        assertEquals(OperandNode.class, operandA.getClass());

        assertSourceCode("[0x2000+I]", source, operandA);

        // operand B checks
        ASTNode operandB = ast.getNodeInRange(16);
        assertNotNull(operandB);
        assertEquals(OperandNode.class, operandB.getClass());

        assertSourceCode("[A]", source, operandB);

        assertSourceCode(source, unit.getAST());
        assertSourceCode(source, instruction);
    }

    public void testConditionalExpressionsParsing() throws Exception {
        assertCompiles(":test SET A , 3 > 2");
        assertCompiles(":test SET [ 3 > 2 ] , 1 == 2");
        assertDoesNotCompile(":test SET [ a < 3 ] , 1 == 2");
    }

    public void testPushNotValidAsSourceOperand() throws Exception {
        assertDoesNotCompile(" SET A , PUSH ");
    }

    public void testPopNotValidAsSourceOperand() throws Exception {
        assertDoesNotCompile(" SET POP , 1 ");
    }

    public void testAddressingModesParsing() throws Exception {

        assertCompiles("SET PICK 3,a");
        assertCompiles("SET a,PICK 3");

        assertCompiles("SET [SP+3],a");
        assertCompiles("label: SET [SP+3+4+label],a");
        assertCompiles("label: SET PICK 3+4,a");
        assertCompiles("SET a,[SP+3]");

        assertCompiles(":test SET A, 2+test");

        assertDoesNotCompile(":sub SET a,1"); // sub is an opcode !
        assertDoesNotCompile(":x SET a,1"); // x is a register !        

        assertDoesNotCompile("SET [A] , [--SP]");

        assertCompiles("ifn A, \"=\"");
        assertDoesNotCompile("SET a , 1+a ");

        assertCompiles("SET [10+A],10");
        assertCompiles("SET [10+A+10],10");

        assertCompiles("SET [0x10],3");

        assertCompiles("SET A , 10 ");
        assertDoesNotCompile("SET [A++] , 10 ");
        assertDoesNotCompile("SET [B] , [A++]");

        assertDoesNotCompile("SET [--A] , 10 ");
        assertDoesNotCompile("SET [B] , [--A]");

        assertCompiles("SET PC,4");

        assertCompiles("SET [A] , 10 ");
        assertCompiles("SET [A+10] , 10 ");

        assertDoesNotCompile("SET [A+A],10)");
        assertDoesNotCompile("SET [1+A+2+A],10)");

        assertCompiles("SET [A+10],10");

        assertDoesNotCompile("SET 10,A");
        assertDoesNotCompile("SET 10,[A]");
        assertDoesNotCompile("SET 10,[A+10]");
        assertDoesNotCompile("SET 10,[10+A]");

        assertCompiles("SET [A] , [B] ");
        assertCompiles("SET [A+5] , [b+10] ");
        assertCompiles("SET [A+10] , [b+5] ");
        assertCompiles("SET [A+10] , [B+10] ");

        assertCompiles("SET [6+A] , [6+b] ");
        assertDoesNotCompile("SET [A++] , 1");
        assertDoesNotCompile("SET [--A] , 1");
        assertCompiles("SET [--SP] , 1");

        assertCompiles("SET PUSH, [A]"); // PUSH      
        assertDoesNotCompile("SET [A] , PUSH "); // PUSH       
        assertDoesNotCompile("SET [A] , [--SP] "); // PUSH

        assertCompiles("SET [A], PEEK "); // PEEK   
        assertCompiles("SET PEEK , [A] "); // PEEK       
        assertCompiles("SET [SP] , [A] "); // PEEK

        assertCompiles("SET PC,POP");
        assertCompiles("SET [A] , POP"); // POP           
        assertDoesNotCompile("SET POP , [A] "); // POP       
        assertDoesNotCompile("SET [SP++] , [A] "); // POP    
    }

    public void testPush() throws Exception {

        final String source = "SET PUSH,10";

        final IParseContext context = createParseContext(source);

        final ASTNode result = new InstructionNode().parse(context);
        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;

        assertEquals(Register.SP, instruction.getOperand(0).getRegister());
        assertEquals(AddressingMode.INDIRECT_REGISTER_PREDECREMENT, instruction.getOperand(0).getAddressingMode());

        assertSourceCode("SET PUSH,10", result);
    }

    public void testPop() throws Exception {

        final String source = "SET a,pop";

        final IParseContext context = createParseContext(source);

        final ASTNode result = new InstructionNode().parse(context);
        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;

        assertEquals(Register.SP, instruction.getOperand(1).getRegister());
        assertEquals(AddressingMode.INDIRECT_REGISTER_POSTINCREMENT, instruction.getOperand(1).getAddressingMode());

        assertSourceCode("SET a,pop", result);
    }

    public void testPop2() throws Exception {
        assertDoesNotCompile("SET POP,o");
    }

    public void testNoOperandInlining() throws Exception {

        final String source = "SET [0x1000], 0x20";

        ICompilationUnit unit = CompilationUnit.createInstance("string", source);
        final IParseContext context = createParseContext(unit);

        final ASTNode result = new InstructionNode().parse(context);
        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;
        resolveSymbols(unit, instruction);
        final int size = instruction.getSizeInBytes(0);
        assertEquals(6, size);
        assertSourceCode("SET [0x1000], 0x20", result);
    }

    public void testOperandInlining1() throws Exception {

        final String source = "SET [0x1000], 0x1e";

        ICompilationUnit unit = CompilationUnit.createInstance("string", source);
        final IParseContext context = createParseContext(unit);

        final ASTNode result = new InstructionNode().parse(context);

        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;
        resolveSymbols(unit, instruction);

        final int size = instruction.getSizeInBytes(0);
        assertEquals(4, size);
        assertSourceCode("SET [0x1000], 0x1f", result);
    }

    public void testOperandInlining2() throws Exception {

        final String source = "SET PC, 30";

        ICompilationUnit unit = CompilationUnit.createInstance("string", source);
        final IParseContext context = createParseContext(unit);

        final ASTNode result = new InstructionNode().parse(context);

        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;
        resolveSymbols(unit, instruction);

        final int size = instruction.getSizeInBytes(0);
        assertEquals(2, size);
        assertSourceCode("SET PC, 30", result);
    }

    public void testOperandInlining3() throws Exception {

        final String source = "SET PC, -1";

        ICompilationUnit unit = CompilationUnit.createInstance("string", source);
        final IParseContext context = createParseContext(unit);

        final ASTNode result = new InstructionNode().parse(context);

        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;
        resolveSymbols(unit, instruction);

        final int size = instruction.getSizeInBytes(0);
        assertEquals(2, size);
        assertSourceCode("SET PC, -1", result);
    }

    public void testExtendedInstructionParsing() throws Exception {

        final String source = "JSR 0x1000";

        ICompilationUnit unit = CompilationUnit.createInstance("string", source);
        final IParseContext context = createParseContext(unit);

        final ASTNode result = new InstructionNode().parse(context);

        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;
        resolveSymbols(unit, instruction);

        final int size = instruction.getSizeInBytes(0);
        assertEquals(4, size);
        assertSourceCode("JSR 0x1000", result);
    }

    public void testPeek() throws Exception {

        final String source = "SET a,peek";

        final IParseContext context = createParseContext(source);

        final ASTNode result = new InstructionNode().parse(context);
        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;

        assertEquals(Register.SP, instruction.getOperand(1).getRegister());
        assertEquals(AddressingMode.INDIRECT_REGISTER, instruction.getOperand(1).getAddressingMode());

        assertSourceCode("SET a,peek", result);
    }

    public void testParseNestedImmediateExpression() throws Exception {

        final String source = "SET I, 4+5*3";

        final ICompilationUnit unit = CompilationUnit.createInstance("string input", source);

        final IParseContext context = createParseContext(source);

        final ASTNode result = new InstructionNode().parse(context);
        assertFalse(getErrors(source, result), result.hasErrors());
        assertEquals(InstructionNode.class, result.getClass());

        final InstructionNode instruction = (InstructionNode) result;

        final AtomicReference<byte[]> objcode = new AtomicReference<byte[]>();

        final IObjectCodeWriter writer = new IObjectCodeWriter() {

            @Override
            public void close() throws IOException {
            }

            @Override
            public void writeObjectCode(byte[] data, int offset, int length) throws IOException {
                objcode.set(ArrayUtils.subarray(data, offset, offset + length));
            }

            @Override
            public void writeObjectCode(byte[] data) throws IOException {
                writeObjectCode(data, 0, data.length);
            }

            @Override
            public void deleteOutput() throws IOException {
                // TODO Auto-generated method stub

            }

            @Override
            public Address getCurrentWriteOffset() {
                return Address.ZERO;
            }

            @Override
            public Address getFirstWriteOffset() {
                return Address.ZERO;
            }

            @Override
            public void advanceToWriteOffset(Address offset) throws IOException {
                throw new UnsupportedOperationException("Not implemented");
            }
        };

        final ICompilationContext compContext = createCompilationContext(unit);

        final OperandNode operand = instruction.getOperand(1);
        final TermNode oldExpression = (TermNode) operand.child(0);
        final TermNode newExpression = oldExpression.reduce(compContext);
        if (newExpression != oldExpression) {
            operand.setChild(0, newExpression);
        }
        instruction.symbolsResolved(compContext);
        instruction.writeObjectCode(writer, compContext);
        assertNotNull(objcode.get());
        assertEquals(source, toSourceCode(result, source));
    }
}