/**
* 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;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import de.codesourcery.jasm16.ast.InstructionNode;
import de.codesourcery.jasm16.ast.ObjectCodeOutputNode;
import de.codesourcery.jasm16.ast.OperandNode;
import de.codesourcery.jasm16.ast.OperandNode.OperandPosition;
import de.codesourcery.jasm16.compiler.ICompilationContext;
import de.codesourcery.jasm16.compiler.ICompiler.CompilerOption;
import de.codesourcery.jasm16.compiler.ISymbolTable;
/**
* Enumeration of all DCPU-16 instructions / op codes.
*
* <p>This class also implements object code generation
* for each of the instructions.</p>
*
* @author tobias.gierke@code-sourcery.de
*/
public enum OpCode
{
/*
* === Basic opcodes (5 bits) ===
* |
* |C | VAL | NAME | DESCRIPTION
* +--+------+----------+---------------------------------------------------------
* |- | 0x00 | n/a | special instruction - see below
* |1 | 0x01 | SET b, a | sets b to a
* |2 | 0x02 | ADD b, a | sets b to b+a, sets EX to 0x0001 if there's an overflow, 0x0 otherwise
* |2 | 0x03 | SUB b, a | sets b to b-a, sets EX to 0xffff if there's an underflow, 0x0 otherwise
* |2 | 0x04 | MUL b, a | sets b to b*a, sets EX to ((b*a)>>16)&0xffff (treats b, a as unsigned)
* |2 | 0x05 | MLI b, a | like MUL, but treat b, a as signed
* |3 | 0x06 | DIV b, a | sets b to b/a, sets EX to ((b<<16)/a)&0xffff. if a==0, sets b and EX to 0 instead. (treats b, a as unsigned)
* |3 | 0x07 | DVI b, a | like DIV, but treat b, a as signed. Rounds towards 0
* |3 | 0x08 | MOD b, a | sets b to b%a. if a==0, sets b to 0 instead.
* |3 | 0x09 | MDI b, a | like MOD, but treat b, a as signed. Rounds towards 0
*
* |1 | 0x0a | AND b, a | sets b to b&a
* |1 | 0x0b | BOR b, a | sets b to b|a
* |1 | 0x0c | XOR b, a | sets b to b^a
*
* |2 | 0x0d | SHR b, a | sets b to b>>>a, sets EX to ((b<<16)>>a)&0xffff (logical shift)
* |2 | 0x0e | ASR b, a | sets b to b>>a, sets EX to ((b<<16)>>>a)&0xffff (arithmetic shift) (treats b as signed)
* |2 | 0x0f | SHL b, a | sets b to b<<a, sets EX to ((b<<a)>>16)&0xffff
*
* |2+| 0x10 | IFB b, a | performs next instruction only if (b&a)!=0
* |2+| 0x11 | IFC b, a | performs next instruction only if (b&a)==0
* |2+| 0x12 | IFE b, a | performs next instruction only if b==a
* |2+| 0x13 | IFN b, a | performs next instruction only if b!=a
* |2+| 0x14 | IFG b, a | performs next instruction only if b>a
* |2+| 0x15 | IFA b, a | performs next instruction only if b>a (signed)
* |2+| 0x16 | IFL b, a | performs next instruction only if b<a
* |2+| 0x17 | IFU b, a | performs next instruction only if b<a (signed)
* |- | 0x18 | - |
* |- | 0x19 | - |
* |3 | 0x1a | ADX b, a | sets b to b+a+EX, sets EX to 0x0001 if there is an over-
* | | | | flow, 0x0 otherwise
* |3 | 0x1b | SBX b, a | sets b to b-a+EX, sets EX to 0xFFFF if there is an under-
* | | | | flow, 0x0 otherwise
* |- | 0x1c | - |
* |- | 0x1d | - |
* |2 | 0x1e | STI b, a | sets b to a, then increases I and J by 1
* |2 | 0x1f | STD b, a | sets b to a, then decreases I and J by 1
* +--+------+----------+---------------------------------------------------------
*
*
* === Special opcodes: (5 bits) ===
*
*
* | C | VAL | NAME | DESCRIPTION
* +---+------+-------+-------------------------------------------------------------
* | - | 0x00 | n/a | reserved for future expansion
* | 3 | 0x01 | JSR a | pushes the address of the next instruction to the stack,
* | | | | then sets PC to a
* | - | 0x02 | - |
* | - | 0x03 | - |
* | - | 0x04 | - |
* | - | 0x05 | - |
* | - | 0x06 | - |
* | 9 | 0x07 | HCF a | use sparingly
* | 4 | 0x08 | INT a | triggers a software interrupt with message a
* | 1 | 0x09 | IAG a | sets a to IA
* | 1 | 0x0a | IAS a | sets IA to a
* | 3 | 0x0b | IAP a | if IA is 0, does nothing, otherwise pushes IA to the stack,
* | | | | then sets IA to a
* | 2 | 0x0c | IAQ a | if a is nonzero, interrupts will be added to the queue
* | | | | instead of triggered. if a is zero, interrupts will be
* | | | | triggered as normal again
* | - | 0x0d | - |
* | - | 0x0e | - |
* | - | 0x0f | - |
* | 2 | 0x10 | HWN a | sets a to number of connected hardware devices
* | 4 | 0x11 | HWQ a | sets A, B, C, X, Y registers to information about hardware a
* | | | | A+(B<<16) is a 32 bit word identifying the hardware id
* | | | | C is the hardware version
* | | | | X+(Y<<16) is a 32 bit word identifying the manufacturer
* | 4+| 0x12 | HWI a | sends an interrupt to hardware a
* | - | 0x13 | - |
* | - | 0x14 | - |
* | - | 0x15 | - |
* | - | 0x16 | - |
* | - | 0x17 | - |
* | - | 0x18 | - |
* | - | 0x19 | - |
* | - | 0x1a | - |
* | - | 0x1b | - |
* | - | 0x1c | - |
* | - | 0x1d | - |
* | - | 0x1e | - |
* | - | 0x1f | - |
* +---+------+-------+-------------------------------------------------------------
*/
// general
SET("set",2,0x01) // sets b to a
{
@Override
public boolean isValidAddressingMode(OperandPosition position , AddressingMode type)
{
if ( position == OperandPosition.TARGET_OPERAND )
{
if ( type.equals( AddressingMode.IMMEDIATE ) ) {
return false;
}
}
return true;
}
},
// arithmetics
ADD("add",2,0x02),
SUB("sub",2,0x03),
MUL("mul",2,0x04),
MLI("mli",2,0x05),
DIV("div",2,0x06),
DVI("dvi",2,0x07),
MOD("mod",2,0x08),
MDI("mdi",2,0x09),
ADX("adx",2,0x1a),
SBX("sbx",2,0x1b),
STI("sti",2,0x1e),
STD("std",2,0x1f),
// EXTENDED opcodes / control flow
JSR( "jsr",1,0x01)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// operand no. 0 is always considered to be the TARGET operand
// while operand no. 1 is always considered to be the SOURCE operand.
// This breaks stuff in case of JSR which only takes a single operand
// that needs to be treated as SOURCE (because it needs to provide the jump target)
// Thus we'll just hard-code the operand position to always be SOURCE before
// actually doing the check
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
/**
* Officially no longer supported.
*
* @see #isHaltInstruction(int)
*/
HCF( "hcf",1,0x07)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
INT( "int",1,0x08)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
IAG( "iag",1,0x09)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
return super.isOperandValidInPosition( OperandPosition.TARGET_OPERAND , mode , register ); // IAG assigns IA to a so cannot have a literal value as a target
}
},
IAS( "ias",1,0x0a)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
RFI( "rfi",1,0x0b)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
IAQ( "iaq",1,0x0c)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
HWN( "hwn",1,0x10)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
HWQ( "hwq",1,0x11)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
HWI( "hwi",1,0x12)
{
public boolean isBasicOpCode() { return false; };
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register)
{
// use hard-coded operand position here, see in-line comment in JSR enum constant for reason
return super.isOperandValidInPosition( OperandPosition.SOURCE_OPERAND , mode , register );
}
},
// conditions
IFB("ifb",2,0x10) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFC("ifc",2,0x11) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFE("ife",2,0x12) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFN("ifn",2,0x13) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFG("ifg",2,0x14) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFA("ifa",2,0x15) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFL("ifl",2,0x16) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
IFU("ifu",2,0x17) {
@Override
protected boolean isConditionalBranchOpCode() { return true; }
},
// bit-shifting
SHR("shr",2,0x0d),
ASR("asr",2,0x0e),
SHL("shl",2,0x0f),
// boolean operations
AND("and",2,0x0a),
OR("bor",2 ,0x0b),
XOR("xor",2,0x0c);
private final int opCodeBits;
private final String identifier;
private final int operandCount;
private static volatile Map<String,OpCode> LOWER_CASE_MAP;
private static volatile Map<String,OpCode> UPPER_CASE_MAP;
private final Logger LOG = Logger.getLogger( OpCode.class );
private OpCode(String identifier,int operandCount,int opCodeBits) {
this.identifier = identifier;
this.operandCount=operandCount;
this.opCodeBits = opCodeBits;
}
protected static final class OperandDesc
{
public final int operandBits;
public final boolean appendAsWord;
protected OperandDesc(int operandBits) {
this( operandBits, false);
}
protected OperandDesc(int operandBits,boolean appendAsWord) {
this.operandBits=operandBits;
this.appendAsWord=appendAsWord;
}
}
private OperandDesc operandDesc(int operandBits) {
return new OperandDesc(operandBits);
}
private OperandDesc operandDesc(int operandBits,boolean appendAsWord) {
return new OperandDesc(operandBits,appendAsWord);
}
public int getOperandCount()
{
return operandCount;
}
public String getIdentifier() {
return identifier.toUpperCase();
}
public boolean isValidAddressingMode(OperandPosition position, AddressingMode type) {
return true;
}
private static Map<String,OpCode> getLowerCaseMap() {
if ( LOWER_CASE_MAP == null ) {
LOWER_CASE_MAP = new HashMap<String,OpCode>();
for ( OpCode code : values() ) {
LOWER_CASE_MAP.put( code.identifier.toLowerCase() , code );
}
}
return LOWER_CASE_MAP;
}
/**
* Returns whether this is a basic (two-operand) opcode.
*
* @return
* @see #isSpecialOpCode()
*/
public boolean isBasicOpCode() {
return true;
}
/**
* Non-basic opcodes always have their lower four bits unset, have one value and a six bit opcode.
*
* In binary, they have the format: aaaaaaoooooo0000
* The value (a) is in the same six bit format as defined earlier.
* @return
* @see #isBasicOpCode()
*/
public final boolean isSpecialOpCode() {
return ! isBasicOpCode();
}
private static Map<String,OpCode> getUpperCaseMap() {
if ( UPPER_CASE_MAP == null ) {
UPPER_CASE_MAP = new HashMap<String,OpCode>();
for ( OpCode code : values() ) {
UPPER_CASE_MAP.put( code.identifier.toUpperCase() , code );
}
}
return UPPER_CASE_MAP;
}
public static OpCode fromIdentifier(String identifier)
{
OpCode result = getLowerCaseMap().get( identifier );
if ( result == null ) {
result = getUpperCaseMap().get( identifier );
}
return result;
}
public int calculateSizeInBytes(ICompilationContext context , InstructionNode instruction)
{
final OperandNode operandA = instruction.getOperand( 0, false );
final OperandNode operandB = instruction.getOperand( 1, false );
return getInstructionSizeInBytes( context , operandA , operandB );
}
private int getInstructionSizeInBytes(ICompilationContext context, OperandNode operandA, OperandNode operandB)
{
final byte[] buffer = new byte[6];
final int calculatedSize = writeInstruction( context , operandA , operandB , buffer );
if ( calculatedSize == InstructionNode.UNKNOWN_SIZE ) {
return getMaximumInstructionSizeInBytes(); // assume worst-case scenario
}
return calculatedSize;
}
protected int getMaximumInstructionSizeInBytes() {
return 6;
}
/**
* Generates object code for a given instruction.
*
* @param context
* @param instruction
* @return object code or <code>null</code> if no object code could be generated because
* this method was unable to determine the operands sizes (probably because symbols could not be resolved)
*/
public byte[] generateObjectCode(ICompilationContext context , InstructionNode instruction)
{
final byte[] buffer = new byte[6]; // max. instruction length: three words (3*2 bytes)
final OperandNode targetOperand;
if ( instruction.getOperandCount() > 0 ) {
targetOperand = instruction.getOperand( 0 );
} else {
targetOperand = null;
}
final OperandNode sourceOperand;
if ( instruction.getOperandCount() > 1 ) {
sourceOperand = instruction.getOperand( 1 );
} else {
sourceOperand = null;
}
final int bytesToWrite = writeInstruction( context , targetOperand, sourceOperand , buffer );
if ( bytesToWrite != ObjectCodeOutputNode.UNKNOWN_SIZE )
{
final byte[] result = new byte[ bytesToWrite ];
System.arraycopy( buffer , 0 , result , 0 , bytesToWrite );
return result;
}
return null;
}
private int writeInstruction(ICompilationContext context,OperandNode targetOperand , OperandNode sourceOperand, byte[] buffer)
{
/*
* Instructions are 1-3 words long and are fully defined by the first word.
* In a basic instruction, the lower FIVE bits of the first word of the instruction
* are the opcode, and the remaining ELEVEN bits are split into a FIVE bit value b
* and a SIX bit value a.
*
* b is always handled by the processor after a, and is the LOWER FIVE bits.
*
* In bits (in LSB-0 format), a basic instruction has the format:
*
* aaaaaabbbbbooooo
*
* NON-BASIC opcodes always have their lower five bits unset, have one value and a
* five bit opcode.
*
* In binary, they have the format:
*
* aaaaaaooooo00000
*
* The value (a) is in the same six bit format as defined earlier.
*/
final ISymbolTable symbolTable = context.getSymbolTable();
int opcode = 0;
OperandDesc descTarget=null;
OperandDesc descSource=null;
final int OPCODE_BITS = 5;
final boolean inlineLiterals = ! context.hasCompilerOption(CompilerOption.DISABLE_INLINING ) &&
! context.hasCompilerOption( CompilerOption.GENERATE_RELOCATION_INFORMATION );
if ( isSpecialOpCode() )
{
if ( targetOperand == null ) {
throw new RuntimeException("Extended instruction "+this+" requires a single operand, got none");
}
if ( sourceOperand != null ) {
throw new RuntimeException("Extended instruction "+this+" requires a single operand, got two ?");
}
opcode |= ( opCodeBits << OPCODE_BITS );
descTarget = getOperandBits( symbolTable , OperandPosition.TARGET_OPERAND , targetOperand ,sourceOperand , inlineLiterals );
if ( descTarget == null ) {
return ObjectCodeOutputNode.UNKNOWN_SIZE;
}
opcode |= ( descTarget.operandBits << 10 );
}
else
{
// handle basic opcode
opcode |= ( opCodeBits & 0x1f); // 5-bit opcodes = 0x1F
if ( targetOperand != null )
{
descTarget = getOperandBits( symbolTable , OperandPosition.TARGET_OPERAND , targetOperand ,sourceOperand , inlineLiterals);
if ( descTarget == null ) {
return ObjectCodeOutputNode.UNKNOWN_SIZE;
}
opcode |= ( descTarget.operandBits << OPCODE_BITS );
}
if ( sourceOperand != null ) {
descSource = getOperandBits( symbolTable , OperandPosition.SOURCE_OPERAND , targetOperand ,sourceOperand , inlineLiterals );
if ( descSource == null ) {
return ObjectCodeOutputNode.UNKNOWN_SIZE;
}
opcode |= ( descSource.operandBits << 10 );
}
}
// write instruction word
int idx = 0;
buffer[idx++] = (byte) ( ( opcode >> 8 ) & 0xff );
buffer[idx++] = (byte) ( opcode & 0xff );
// write operand A
if ( descSource != null && descSource.appendAsWord )
{
Long literalValue = sourceOperand.getLiteralValue( symbolTable );
if ( literalValue == null ) {
return ObjectCodeOutputNode.UNKNOWN_SIZE;
}
final long value = literalValue;
buffer[idx++] = (byte) ( ( value >> 8 ) & 0xff );
buffer[idx++] = (byte) ( value & 0x00ff );
}
// write operand B
if ( descTarget != null && descTarget.appendAsWord )
{
Long literalValue = targetOperand.getLiteralValue( symbolTable );
if ( literalValue == null ) {
return ObjectCodeOutputNode.UNKNOWN_SIZE;
}
final long value = literalValue;
buffer[idx++] = (byte) ( ( value >> 8 ) & 0xff );
buffer[idx++] = (byte) ( value & 0xff );
}
return idx;
}
private OperandDesc getOperandBits(ISymbolTable symbolTable,
OperandPosition operandToHandle,
OperandNode targetOperand,
OperandNode sourceOperand,boolean inlineLiterals)
{
final OperandNode operand = operandToHandle == OperandPosition.TARGET_OPERAND ? targetOperand : sourceOperand;
/*
* SET TARGET (b) ,SOURCE (a)
*
* aaaaaabbbbbooooo
*
* - 5 bits for the TARGET operand (b)
* - 6 bits for the SOURCE operand (a)
*
* --- Values: (5/6 bits) ---------------------------------------------------------
* C | VALUE | DESCRIPTION
* ---+-----------+----------------------------------------------------------------
* 0 | 0x00-0x07 | register (A, B, C, X, Y, Z, I or J, in that order)
* 0 | 0x08-0x0f | [register]
* 1 | 0x10-0x17 | [register + next word]
* 0 | 0x18 | (PUSH / [--SP]) if in b, or (POP / [SP++]) if in a
* 0 | 0x19 | [SP] / PEEK
* 1 | 0x1a | [SP + next word] / PICK n
* 0 | 0x1b | SP
* 0 | 0x1c | PC
* 0 | 0x1d | EX
* 1 | 0x1e | [next word]
* 1 | 0x1f | next word (literal)
* 0 | 0x20-0x3f | literal value 0xffff-0x1e (-1..30) (literal) (only for a)
* --+-----------+----------------------------------------------------------------
*
* - "next word" means "[PC++]". Increases the word length of the instruction by 1.
* - By using 0x18, 0x19, 0x1a as PEEK, POP/PUSH, and PICK there's a reverse stack
* starting at memory location 0xffff. Example: "SET PUSH, 10", "SET X, POP"
*
*/
switch ( operand.getAddressingMode() )
{
case IMMEDIATE:
Long value2 = operand.getLiteralValue( symbolTable );
if ( value2 == null ) {
return null; // => ObjectCodeOutputNode#UNKNOWN_SIZE
}
long value = value2;
// inlining is ONLY supported for source operand (a) OR the only operand in single-operand opcodes
if ( inlineLiterals && ( operandToHandle == OperandPosition.SOURCE_OPERAND || getOperandCount() == 1 ) &&
value >= -1 && value <= 30 )
{
value+=1; // shift because -1 = 0x20 , 0 = 0x21 , +++
return operandDesc( 0x20 + (int) value );
}
return operandDesc( 0x1f , true ); // value too large (for this operand position), cannot be inlined
case INDIRECT:
return operandDesc( 0x1e , true );
case INDIRECT_REGISTER:
Register register = operand.getRegister();
if ( register == Register.SP )
{
return operandDesc( 0x19 );
} else if ( register == Register.PC ||register == Register.EX ) {
throw new RuntimeException("Internal error, register "+register+" must not be used with addressing mode INDIRECT_REGISTER");
}
return operandDesc( getRegisterBitmask( register , 0x08 ) );
case INDIRECT_REGISTER_POSTINCREMENT:
register = operand.getRegister();
if ( register != Register.SP )
{
throw new RuntimeException("Internal error, register "+register+" must not be used with addressing mode INDIRECT_REGISTER_POSTINCREMENT");
}
// SET b,[SP++] => VALID
// SET [SP++],a => INVALID because there's no opcode for it
if ( operandToHandle == OperandPosition.TARGET_OPERAND ) {
LOG.warn("POP/[SP++] cannot be used as TARGET operand");
return null;
}
return operandDesc( 0x18 ); // (PUSH / [--SP]) if in b, or (POP / [SP++]) if in a
case INDIRECT_REGISTER_PREDECREMENT:
register = operand.getRegister();
if ( register != Register.SP )
{
throw new RuntimeException("Internal error, register "+register+" must not be used with addressing mode INDIRECT_REGISTER_PREDECREMENT");
}
// SET [--SP],a => VALID
// SET b,[--SP] => INVALID because there's no opcode for it
if ( operandToHandle == OperandPosition.SOURCE_OPERAND ) {
LOG.warn("PUSH/[--SP] cannot be used as SOURCE operand");
return null;
}
return operandDesc( 0x18 ); // (PUSH / [--SP]) if in b, or (POP / [SP++]) if in a
case INDIRECT_REGISTER_OFFSET:
value2 = operand.getLiteralValue(symbolTable);
if ( value2 == null ) {
return null; // => ObjectCodeOutputNode#UNKNOWN_SIZE
}
value = value2;
register = operand.getRegister();
if ( register == Register.SP ) {
return operandDesc( 0x1a , true ); // new since DCPU 1.1: [SP + next word] / PICK n
}
if ( register == Register.PC || register == Register.EX ) {
throw new RuntimeException("Internal error, register "+register+" must not be used with addressing mode INDIRECT_REGISTER_OFFSET");
}
return operandDesc( getRegisterBitmask( register , 0x10 ) , true );
case REGISTER:
register = operand.getRegister();
if ( register == Register.SP ) {
return operandDesc( 0x1b );
} else if ( register == Register.PC ) {
return operandDesc( 0x1c );
} else if ( register == Register.EX ) {
return operandDesc( 0x1d );
}
return operandDesc( getRegisterBitmask( register , 0x00 ) );
}
throw new RuntimeException("Unhandled addressing mode: "+operand.getAddressingMode()+" , operand: "+operand);
}
private int getRegisterBitmask(Register register , int offset) {
/*
* 0x00-0x07: register (A, B, C, X, Y, Z, I or J, in that order)
* 0x08-0x0f: [register]
* 0x10-0x17: [next word + register]
*/
switch( register ) {
case A:
return 0+offset;
case B:
return 1+offset;
case C:
return 2+offset;
case X:
return 3+offset;
case Y:
return 4+offset;
case Z:
return 5+offset;
case I:
return 6+offset;
case J:
return 7+offset;
}
throw new RuntimeException("Cannot handle register: "+register);
}
public boolean isOperandValidInPosition(OperandPosition pos,AddressingMode mode, Register register) {
switch( getValidOperandPositions( mode, register ) )
{
case SOURCE_OPERAND:
return pos == OperandPosition.SOURCE_OPERAND;
case SOURCE_OR_TARGET:
return pos == OperandPosition.SOURCE_OPERAND ||
pos == OperandPosition.TARGET_OPERAND ;
case TARGET_OPERAND:
return pos == OperandPosition.TARGET_OPERAND ;
case NOT_POSSIBLE:
return false;
}
throw new RuntimeException("Unreachable code reached");
}
private OperandPosition getValidOperandPositions(AddressingMode addressingMode, Register register)
{
switch( addressingMode )
{
case REGISTER: // SET a,10
return OperandPosition.SOURCE_OR_TARGET;
case INDIRECT_REGISTER:
return OperandPosition.SOURCE_OR_TARGET;
case INDIRECT_REGISTER_POSTINCREMENT:
if ( register == Register.SP ) {
return OperandPosition.SOURCE_OR_TARGET; // POP / [SP++]
}
return OperandPosition.NOT_POSSIBLE;
case INDIRECT_REGISTER_PREDECREMENT:
if ( register == Register.SP ) {
return OperandPosition.SOURCE_OR_TARGET; // PUSH / [--SP]
}
return OperandPosition.NOT_POSSIBLE;
case INDIRECT_REGISTER_OFFSET:
return OperandPosition.SOURCE_OR_TARGET;
case INDIRECT:
return OperandPosition.SOURCE_OR_TARGET;
case IMMEDIATE:
if ( isConditionalBranchOpCode() ) {
return OperandPosition.SOURCE_OR_TARGET;
}
return OperandPosition.SOURCE_OPERAND;
}
throw new RuntimeException("Internal error, unreachable code " +
"reached (addressing mode: "+addressingMode+")");
}
protected boolean isConditionalBranchOpCode() {
return false;
}
public static boolean isHaltInstruction(int instructionWord) {
return 0x84e0 == instructionWord;
}
}