Java tutorial
/** * 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)); } }