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