mirror of
https://github.com/Qortal/AT.git
synced 2025-01-30 02:42:14 +00:00
New OpCodes:
SLP_DAT addr: fixed to work as advertised SLP_VAL value: sleeps for <value> blocks SHL_VAL addr value: left-shifts the contents of addr by value bits SHR_VAL addr value: right-shifts the contents of addr by value bits EXT_FUN_VAL func value: pass a value to a function instead of fetching via addr This allows new functions GET_A_DAT to take data segment address, instead of convoluted GET_A_IND which requires an address stored in the data segment (like a pointer). New FunctionCodes: GET_A_DAT, GET_B_DAT, SET_A_DAT, SET_B_DAT (see above) UNSIGNED_COMPARE_A_WITH_B, SIGNED_COMPARE_A_WITH_B Add branch offset bounds checking to OpCode.calcOffset method (throws if out of bounds). Added test to cover. Tidied up JavaDoc, which is now also generated by pom.xml! Added JavaDoc comments to OpCodeParam enum entries. Corrected some inaccurate descriptions. Converted obsolete <tt> tags to <code>. Fixed some invalid HTML, converted raw characters like &, <, >, etc. to HTML entites like &, > and → Bumped version to 1.4.0
This commit is contained in:
parent
2b116f41ce
commit
0a16696352
31
Java/pom.xml
31
Java/pom.xml
@ -4,10 +4,16 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.ciyam</groupId>
|
||||
<artifactId>AT</artifactId>
|
||||
<version>1.3.8</version>
|
||||
<version>1.4.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<skipTests>false</skipTests>
|
||||
|
||||
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
|
||||
<maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
|
||||
<maven-surefire-plugin.version>3.0.0-M4</maven-surefire-plugin.version>
|
||||
|
||||
<bouncycastle.version>1.64</bouncycastle.version>
|
||||
</properties>
|
||||
<build>
|
||||
@ -15,20 +21,35 @@
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<release>11</release>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
<version>${maven-surefire-plugin.version}</version>
|
||||
<configuration>
|
||||
<skipTests>${skipTests}</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${maven-javadoc-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadoc</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
|
@ -76,8 +76,8 @@ public abstract class API {
|
||||
* <p>
|
||||
* AT should sleep so it can use next block as source of entropy.
|
||||
* <p>
|
||||
* Set <tt>state.isSleeping = true</tt> before exit on first call.<br>
|
||||
* <tt>state.steps</tt> will be zero on second call after wake-up.
|
||||
* Set <code>state.isSleeping = true</code> before exit on first call.<br>
|
||||
* <code>state.steps</code> will be zero on second call after wake-up.
|
||||
* <p>
|
||||
* Returns 0xffffffffffffffff if A not valid transaction.
|
||||
*/
|
||||
@ -102,9 +102,9 @@ public abstract class API {
|
||||
public abstract void messageAToB(MachineState state);
|
||||
|
||||
/**
|
||||
* Returns <tt>minutes</tt> of blocks added to 'timestamp'
|
||||
* Returns <code>minutes</code> of blocks added to 'timestamp'
|
||||
* <p>
|
||||
* <tt>minutes</tt> is converted to rough number of blocks and added to 'timestamp' to create return value.
|
||||
* <code>minutes</code> is converted to rough number of blocks and added to 'timestamp' to create return value.
|
||||
*/
|
||||
public abstract long addMinutesToTimestamp(Timestamp timestamp, long minutes, MachineState state);
|
||||
|
||||
@ -113,7 +113,7 @@ public abstract class API {
|
||||
*
|
||||
* @param amount
|
||||
* - final balance to be returned to creator
|
||||
* @param state
|
||||
* @param state AT machine state
|
||||
*/
|
||||
public abstract void onFinished(long amount, MachineState state);
|
||||
|
||||
|
@ -12,19 +12,19 @@ import java.util.stream.Collectors;
|
||||
* <p>
|
||||
* Function codes are represented by a short. Functions can take 0 to 2 additional long values and optionally return a value too.
|
||||
* <p>
|
||||
* FunctionCode instances can be obtained via the default <tt>FunctionCode.valueOf(String)</tt> or the additional <tt>FunctionCode.valueOf(int)</tt>.
|
||||
* FunctionCode instances can be obtained via the default <code>FunctionCode.valueOf(String)</code> or the additional <code>FunctionCode.valueOf(int)</code>.
|
||||
* <p>
|
||||
* Use the <tt>FunctionCode.execute</tt> method to perform the operation.
|
||||
* Use the <code>FunctionCode.execute</code> method to perform the operation.
|
||||
* <p>
|
||||
* For more details, view the <a href="http://ciyam.org/at/at_api.html">API Specification</a>.
|
||||
*
|
||||
* @see FunctionCode#valueOf(int)
|
||||
* @see FunctionCode#execute(FunctionData, MachineState)
|
||||
* @see FunctionCode#execute(FunctionData, MachineState, short)
|
||||
*/
|
||||
public enum FunctionCode {
|
||||
/**
|
||||
* <b>ECHO</b> value to logger<br>
|
||||
* <tt>0x0001 value</tt>
|
||||
* <code>0x0001 value</code>
|
||||
*/
|
||||
ECHO(0x0001, 1, false) {
|
||||
@Override
|
||||
@ -34,7 +34,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0100</tt><br>
|
||||
* <code>0x0100</code><br>
|
||||
* Returns A1 value
|
||||
*/
|
||||
GET_A1(0x0100, 0, true) {
|
||||
@ -44,7 +44,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0101</tt><br>
|
||||
* <code>0x0101</code><br>
|
||||
* Returns A2 value
|
||||
*/
|
||||
GET_A2(0x0101, 0, true) {
|
||||
@ -54,7 +54,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0102</tt><br>
|
||||
* <code>0x0102</code><br>
|
||||
* Returns A3 value
|
||||
*/
|
||||
GET_A3(0x0102, 0, true) {
|
||||
@ -64,7 +64,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0103</tt><br>
|
||||
* <code>0x0103</code><br>
|
||||
* Returns A4 value
|
||||
*/
|
||||
GET_A4(0x0103, 0, true) {
|
||||
@ -74,7 +74,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0104</tt><br>
|
||||
* <code>0x0104</code><br>
|
||||
* Returns B1 value
|
||||
*/
|
||||
GET_B1(0x0104, 0, true) {
|
||||
@ -84,7 +84,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0105</tt><br>
|
||||
* <code>0x0105</code><br>
|
||||
* Returns B2 value
|
||||
*/
|
||||
GET_B2(0x0105, 0, true) {
|
||||
@ -94,7 +94,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0106</tt><br>
|
||||
* <code>0x0106</code><br>
|
||||
* Returns B3 value
|
||||
*/
|
||||
GET_B3(0x0106, 0, true) {
|
||||
@ -104,7 +104,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0107</tt><br>
|
||||
* <code>0x0107</code><br>
|
||||
* Returns B4 value
|
||||
*/
|
||||
GET_B4(0x0107, 0, true) {
|
||||
@ -115,7 +115,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copies A into addr to addr+3<br>
|
||||
* <tt>0x0108 addr</tt>
|
||||
* <code>0x0108 addr</code>
|
||||
*/
|
||||
GET_A_IND(0x0108, 1, false) {
|
||||
@Override
|
||||
@ -133,7 +133,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copies B into addr to addr+3<br>
|
||||
* <tt>0x0109 addr</tt>
|
||||
* <code>0x0109 addr</code>
|
||||
*/
|
||||
GET_B_IND(0x0109, 1, false) {
|
||||
@Override
|
||||
@ -149,9 +149,47 @@ public enum FunctionCode {
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b4);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copies A into addr to addr+3<br>
|
||||
* <code>0x010a addr</code><br>
|
||||
* To be used by calling EXT_FUN_VAL, passing destination address as <b>value</b>
|
||||
*/
|
||||
GET_A_DAT(0x010a, 1, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a1);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a2);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a3);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.a4);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copies B into addr to addr+3<br>
|
||||
* <code>0x010b addr</code><br>
|
||||
* To be used by calling EXT_FUN_VAL, passing destination address as <b>value</b>
|
||||
*/
|
||||
GET_B_DAT(0x010b, 1, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b1);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b2);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b3);
|
||||
state.dataByteBuffer.putLong(dataIndex++ * MachineState.VALUE_SIZE, state.b4);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Set A1<br>
|
||||
* <tt>0x0110 value</tt>
|
||||
* <code>0x0110 value</code>
|
||||
*/
|
||||
SET_A1(0x0110, 1, false) {
|
||||
@Override
|
||||
@ -161,7 +199,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set A2<br>
|
||||
* <tt>0x0111 value</tt>
|
||||
* <code>0x0111 value</code>
|
||||
*/
|
||||
SET_A2(0x0111, 1, false) {
|
||||
@Override
|
||||
@ -171,7 +209,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set A3<br>
|
||||
* <tt>0x0112 value</tt>
|
||||
* <code>0x0112 value</code>
|
||||
*/
|
||||
SET_A3(0x0112, 1, false) {
|
||||
@Override
|
||||
@ -181,7 +219,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set A4<br>
|
||||
* <tt>0x0113 value</tt>
|
||||
* <code>0x0113 value</code>
|
||||
*/
|
||||
SET_A4(0x0113, 1, false) {
|
||||
@Override
|
||||
@ -191,7 +229,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set A1 and A2<br>
|
||||
* <tt>0x0114 value value</tt>
|
||||
* <code>0x0114 value value</code>
|
||||
*/
|
||||
SET_A1_A2(0x0114, 2, false) {
|
||||
@Override
|
||||
@ -202,7 +240,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set A3 and A4<br>
|
||||
* <tt>0x0115 value value</tt>
|
||||
* <code>0x0115 value value</code>
|
||||
*/
|
||||
SET_A3_A4(0x0115, 2, false) {
|
||||
@Override
|
||||
@ -213,7 +251,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B1<br>
|
||||
* <tt>0x0116 value</tt>
|
||||
* <code>0x0116 value</code>
|
||||
*/
|
||||
SET_B1(0x0116, 1, false) {
|
||||
@Override
|
||||
@ -223,7 +261,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B2<br>
|
||||
* <tt>0x0117 value</tt>
|
||||
* <code>0x0117 value</code>
|
||||
*/
|
||||
SET_B2(0x0117, 1, false) {
|
||||
@Override
|
||||
@ -233,7 +271,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B3<br>
|
||||
* <tt>0x0118 value</tt>
|
||||
* <code>0x0118 value</code>
|
||||
*/
|
||||
SET_B3(0x0118, 1, false) {
|
||||
@Override
|
||||
@ -243,7 +281,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B4<br>
|
||||
* <tt>0x0119 value</tt>
|
||||
* <code>0x0119 value</code>
|
||||
*/
|
||||
SET_B4(0x0119, 1, false) {
|
||||
@Override
|
||||
@ -253,7 +291,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B1 and B2<br>
|
||||
* <tt>0x011a value value</tt>
|
||||
* <code>0x011a value value</code>
|
||||
*/
|
||||
SET_B1_B2(0x011a, 2, false) {
|
||||
@Override
|
||||
@ -264,7 +302,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Set B3 and B4<br>
|
||||
* <tt>0x011b value value</tt>
|
||||
* <code>0x011b value value</code>
|
||||
*/
|
||||
SET_B3_B4(0x011b, 2, false) {
|
||||
@Override
|
||||
@ -275,7 +313,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copies addr to addr+3 into A<br>
|
||||
* <tt>0x011c addr</tt>
|
||||
* <code>0x011c addr</code>
|
||||
*/
|
||||
SET_A_IND(0x011c, 1, false) {
|
||||
@Override
|
||||
@ -293,7 +331,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copies addr to addr+3 into B<br>
|
||||
* <tt>0x011d addr</tt>
|
||||
* <code>0x011d addr</code>
|
||||
*/
|
||||
SET_B_IND(0x011d, 1, false) {
|
||||
@Override
|
||||
@ -309,9 +347,47 @@ public enum FunctionCode {
|
||||
state.b4 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copies addr to addr+3 into A<br>
|
||||
* <code>0x011e addr</code><br>
|
||||
* To be used by calling EXT_FUN_VAL, passing source address as <b>value</b>
|
||||
*/
|
||||
SET_A_DAT(0x011e, 1, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.a1 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.a2 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.a3 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.a4 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copies addr to addr+3 into B<br>
|
||||
* <code>0x011f addr</code><br>
|
||||
* To be used by calling EXT_FUN_VAL, passing source address as <b>value</b>
|
||||
*/
|
||||
SET_B_DAT(0x011f, 1, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
// Validate data offset in arg1
|
||||
checkDataAddress(state, functionData.value1, 4);
|
||||
|
||||
int dataIndex = (int) (functionData.value1 & Integer.MAX_VALUE);
|
||||
|
||||
state.b1 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.b2 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.b3 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
state.b4 = state.dataByteBuffer.getLong(dataIndex++ * MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Clear A<br>
|
||||
* <tt>0x0120</tt>
|
||||
* <code>0x0120</code>
|
||||
*/
|
||||
CLEAR_A(0x0120, 0, false) {
|
||||
@Override
|
||||
@ -324,7 +400,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Clear B<br>
|
||||
* <tt>0x0121</tt>
|
||||
* <code>0x0121</code>
|
||||
*/
|
||||
CLEAR_B(0x0121, 0, false) {
|
||||
@Override
|
||||
@ -337,7 +413,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Clear A and B<br>
|
||||
* <tt>0x0122</tt>
|
||||
* <code>0x0122</code>
|
||||
*/
|
||||
CLEAR_A_AND_B(0x0122, 0, false) {
|
||||
@Override
|
||||
@ -354,7 +430,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copy A from B<br>
|
||||
* <tt>0x0123</tt>
|
||||
* <code>0x0123</code>
|
||||
*/
|
||||
COPY_A_FROM_B(0x0123, 0, false) {
|
||||
@Override
|
||||
@ -367,7 +443,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Copy B from A<br>
|
||||
* <tt>0x0124</tt>
|
||||
* <code>0x0124</code>
|
||||
*/
|
||||
COPY_B_FROM_A(0x0124, 0, false) {
|
||||
@Override
|
||||
@ -380,7 +456,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check A is zero<br>
|
||||
* <tt>0x0125</tt><br>
|
||||
* <code>0x0125</code><br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
CHECK_A_IS_ZERO(0x0125, 0, true) {
|
||||
@ -394,7 +470,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check B is zero<br>
|
||||
* <tt>0x0126</tt><br>
|
||||
* <code>0x0126</code><br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
CHECK_B_IS_ZERO(0x0126, 0, true) {
|
||||
@ -408,7 +484,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check A equals B<br>
|
||||
* <tt>0x0127</tt><br>
|
||||
* <code>0x0127</code><br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
CHECK_A_EQUALS_B(0x0127, 0, true) {
|
||||
@ -422,7 +498,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Swap A with B<br>
|
||||
* <tt>0x0128</tt>
|
||||
* <code>0x0128</code>
|
||||
*/
|
||||
SWAP_A_AND_B(0x0128, 0, false) {
|
||||
@Override
|
||||
@ -445,7 +521,8 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Bitwise-OR A with B<br>
|
||||
* <tt>0x0129</tt>
|
||||
* <code>0x0129</code><br>
|
||||
* A = A | B
|
||||
*/
|
||||
OR_A_WITH_B(0x0129, 0, false) {
|
||||
@Override
|
||||
@ -458,20 +535,22 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Bitwise-OR B with A<br>
|
||||
* <tt>0x012a</tt>
|
||||
* <code>0x012a</code><br>
|
||||
* B = B | A
|
||||
*/
|
||||
OR_B_WITH_A(0x012a, 0, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
state.b1 = state.a1 | state.b1;
|
||||
state.b2 = state.a2 | state.b2;
|
||||
state.b3 = state.a3 | state.b3;
|
||||
state.b4 = state.a4 | state.b4;
|
||||
state.b1 = state.b1 | state.a1;
|
||||
state.b2 = state.b2 | state.a2;
|
||||
state.b3 = state.b3 | state.a3;
|
||||
state.b4 = state.b4 | state.a4;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Bitwise-AND A with B<br>
|
||||
* <tt>0x012b</tt>
|
||||
* <code>0x012b</code><br>
|
||||
* A = A & B
|
||||
*/
|
||||
AND_A_WITH_B(0x012b, 0, false) {
|
||||
@Override
|
||||
@ -484,20 +563,22 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Bitwise-AND B with A<br>
|
||||
* <tt>0x012c</tt>
|
||||
* <code>0x012c</code><br>
|
||||
* B = B & A
|
||||
*/
|
||||
AND_B_WITH_A(0x012c, 0, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
state.b1 = state.a1 & state.b1;
|
||||
state.b2 = state.a2 & state.b2;
|
||||
state.b3 = state.a3 & state.b3;
|
||||
state.b4 = state.a4 & state.b4;
|
||||
state.b1 = state.b1 & state.a1;
|
||||
state.b2 = state.b2 & state.a2;
|
||||
state.b3 = state.b3 & state.a3;
|
||||
state.b4 = state.b4 & state.a4;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Bitwise-XOR A with B<br>
|
||||
* <tt>0x012d</tt>
|
||||
* <code>0x012d</code><br>
|
||||
* A = A ^ B
|
||||
*/
|
||||
XOR_A_WITH_B(0x012d, 0, false) {
|
||||
@Override
|
||||
@ -510,20 +591,65 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Bitwise-XOR B with A<br>
|
||||
* <tt>0x012e</tt>
|
||||
* <code>0x012e</code><br>
|
||||
* B = B ^ A
|
||||
*/
|
||||
XOR_B_WITH_A(0x012e, 0, false) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
state.b1 = state.a1 ^ state.b1;
|
||||
state.b2 = state.a2 ^ state.b2;
|
||||
state.b3 = state.a3 ^ state.b3;
|
||||
state.b4 = state.a4 ^ state.b4;
|
||||
state.b1 = state.b1 ^ state.a1;
|
||||
state.b2 = state.b2 ^ state.a2;
|
||||
state.b3 = state.b3 ^ state.a3;
|
||||
state.b4 = state.b4 ^ state.a4;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Unsigned compare A with B<br>
|
||||
* <code>0x0130</code><br>
|
||||
* Returns -1, 0 or 1 depending on whether A is less than, equal or greater than B.
|
||||
*/
|
||||
UNSIGNED_COMPARE_A_WITH_B(0x0130, 0, true) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
int result = Long.compareUnsigned(state.a1, state.b1);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compareUnsigned(state.a2, state.b2);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compareUnsigned(state.a3, state.a3);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compareUnsigned(state.a4, state.a4);
|
||||
|
||||
functionData.returnValue = Long.valueOf(result);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Signed compare A with B<br>
|
||||
* <code>0x0131</code><br>
|
||||
* Returns -1, 0 or 1 depending on whether A is less than, equal or greater than B.
|
||||
*/
|
||||
SIGNED_COMPARE_A_WITH_B(0x0131, 0, true) {
|
||||
@Override
|
||||
protected void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
int result = Long.compare(state.a1, state.b1);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compare(state.a2, state.b2);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compare(state.a3, state.a3);
|
||||
|
||||
if (result == 0)
|
||||
result = Long.compare(state.a4, state.a4);
|
||||
|
||||
functionData.returnValue = Long.valueOf(result);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* MD5 data into B<br>
|
||||
* <tt>0x0200 start-addr byte-length</tt><br>
|
||||
* <code>0x0200 start-addr byte-length</code><br>
|
||||
* MD5 hash stored in B1 and B2. B3 and B4 are zeroed.
|
||||
*/
|
||||
MD5_INTO_B(0x0200, 2, false) {
|
||||
@ -548,7 +674,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check MD5 of data matches B<br>
|
||||
* <tt>0x0201 start-addr byte-length</tt><br>
|
||||
* <code>0x0201 start-addr byte-length</code><br>
|
||||
* Other MD5 hash is in B1 and B2. B3 and B4 are ignored.<br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
@ -579,7 +705,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* RIPE-MD160 data into B<br>
|
||||
* <tt>0x0202 start-addr byte-length</tt><br>
|
||||
* <code>0x0202 start-addr byte-length</code><br>
|
||||
* RIPE-MD160 hash stored in LSB of B1 and all of B2 and B3. B4 is zeroed.
|
||||
*/
|
||||
RMD160_INTO_B(0x0202, 2, false) {
|
||||
@ -604,7 +730,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check RIPE-MD160 of data matches B<br>
|
||||
* <tt>0x0203 start-addr byte-length</tt><br>
|
||||
* <code>0x0203 start-addr byte-length</code><br>
|
||||
* Other RIPE-MD160 hash is in LSB of B1 and all of B2 and B3. B4 is ignored.<br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
@ -637,7 +763,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* SHA256 data into B<br>
|
||||
* <tt>0x0204 start-addr byte-length</tt><br>
|
||||
* <code>0x0204 start-addr byte-length</code><br>
|
||||
* SHA256 hash is stored in B1 through B4.
|
||||
*/
|
||||
SHA256_INTO_B(0x0204, 2, false) {
|
||||
@ -662,7 +788,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check SHA256 of data matches B<br>
|
||||
* <tt>0x0205 start-addr byte-length</tt><br>
|
||||
* <code>0x0205 start-addr byte-length</code><br>
|
||||
* Other SHA256 hash is in B1 through B4.<br>
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
@ -695,7 +821,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* HASH160 data into B<br>
|
||||
* <tt>0x0206 start-addr byte-length</tt><br>
|
||||
* <code>0x0206 start-addr byte-length</code><br>
|
||||
* Bitcoin's HASH160 hash is equivalent to RMD160(SHA256(data)).<br>
|
||||
* HASH160 hash stored in LSB of B1 and all of B2 and B3. B4 is zeroed.
|
||||
*/
|
||||
@ -724,7 +850,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Check HASH160 of data matches B<br>
|
||||
* <tt>0x0207 start-addr byte-length</tt><br>
|
||||
* <code>0x0207 start-addr byte-length</code><br>
|
||||
* Other HASH160 hash is in B1, B2 and LSB of B3. B4 is ignored.
|
||||
* Returns 1 if true, 0 if false
|
||||
*/
|
||||
@ -759,7 +885,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0300</tt><br>
|
||||
* <code>0x0300</code><br>
|
||||
* Returns current block's "timestamp"
|
||||
*/
|
||||
GET_BLOCK_TIMESTAMP(0x0300, 0, true) {
|
||||
@ -769,7 +895,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0301</tt><br>
|
||||
* <code>0x0301</code><br>
|
||||
* Returns AT's creation block's "timestamp"
|
||||
*/
|
||||
GET_CREATION_TIMESTAMP(0x0301, 0, true) {
|
||||
@ -779,7 +905,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0302</tt><br>
|
||||
* <code>0x0302</code><br>
|
||||
* Returns previous block's "timestamp"
|
||||
*/
|
||||
GET_PREVIOUS_BLOCK_TIMESTAMP(0x0302, 0, true) {
|
||||
@ -790,7 +916,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Put previous block's hash in A<br>
|
||||
* <tt>0x0303</tt>
|
||||
* <code>0x0303</code>
|
||||
*/
|
||||
PUT_PREVIOUS_BLOCK_HASH_INTO_A(0x0303, 0, false) {
|
||||
@Override
|
||||
@ -800,7 +926,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Put transaction (to this AT) after timestamp in A, or zero if none<br>
|
||||
* <tt>0x0304 timestamp</tt><br>
|
||||
* <code>0x0304 timestamp</code><br>
|
||||
* a-k-a "A_To_Tx_After_Timestamp"
|
||||
*/
|
||||
PUT_TX_AFTER_TIMESTAMP_INTO_A(0x0304, 1, false) {
|
||||
@ -810,7 +936,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0305</tt><br>
|
||||
* <code>0x0305</code><br>
|
||||
* Return transaction type from transaction in A<br>
|
||||
* Returns 0xffffffffffffffff in A not valid transaction
|
||||
*/
|
||||
@ -821,7 +947,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0306</tt><br>
|
||||
* <code>0x0306</code><br>
|
||||
* Return transaction amount from transaction in A<br>
|
||||
* Returns 0xffffffffffffffff in A not valid transaction
|
||||
*/
|
||||
@ -832,7 +958,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0307</tt><br>
|
||||
* <code>0x0307</code><br>
|
||||
* Return transaction timestamp from transaction in A<br>
|
||||
* Returns 0xffffffffffffffff in A not valid transaction
|
||||
*/
|
||||
@ -844,7 +970,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Generate random number using transaction in A<br>
|
||||
* <tt>0x0308</tt><br>
|
||||
* <code>0x0308</code><br>
|
||||
* Returns 0xffffffffffffffff in A not valid transaction<br>
|
||||
* Can sleep to use next block as source of entropy
|
||||
*/
|
||||
@ -866,7 +992,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Put 'message' from transaction in A into B<br>
|
||||
* <tt>0x0309</tt><br>
|
||||
* <code>0x0309</code><br>
|
||||
* If transaction has no 'message' then zero B<br>
|
||||
* Example 'message' could be 256-bit shared secret
|
||||
*/
|
||||
@ -878,7 +1004,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Put sender/creator address from transaction in A into B<br>
|
||||
* <tt>0x030a</tt>
|
||||
* <code>0x030a</code>
|
||||
*/
|
||||
PUT_ADDRESS_FROM_TX_IN_A_INTO_B(0x030a, 0, false) {
|
||||
@Override
|
||||
@ -888,7 +1014,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Put AT's creator's address into B<br>
|
||||
* <tt>0x030b</tt>
|
||||
* <code>0x030b</code>
|
||||
*/
|
||||
PUT_CREATOR_INTO_B(0x030b, 0, false) {
|
||||
@Override
|
||||
@ -897,7 +1023,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0400</tt><br>
|
||||
* <code>0x0400</code><br>
|
||||
* Returns AT's current balance
|
||||
*/
|
||||
GET_CURRENT_BALANCE(0x0400, 0, true) {
|
||||
@ -907,7 +1033,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0401</tt><br>
|
||||
* <code>0x0401</code><br>
|
||||
* Returns AT's previous balance at end of last execution round<br>
|
||||
* Does not include any amounts sent to AT since
|
||||
*/
|
||||
@ -919,7 +1045,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Pay fee-inclusive amount to account address in B<br>
|
||||
* <tt>0x0402 amount</tt><br>
|
||||
* <code>0x0402 amount</code><br>
|
||||
* Reduces amount to current balance rather than failing due to insufficient funds
|
||||
*/
|
||||
PAY_TO_ADDRESS_IN_B(0x0402, 1, false) {
|
||||
@ -941,7 +1067,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Pay all remaining funds to account address in B<br>
|
||||
* <tt>0x0403</tt>
|
||||
* <code>0x0403</code>
|
||||
*/
|
||||
PAY_ALL_TO_ADDRESS_IN_B(0x0403, 0, false) {
|
||||
@Override
|
||||
@ -955,7 +1081,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Pay previous balance to account address in B<br>
|
||||
* <tt>0x0404</tt><br>
|
||||
* <code>0x0404</code><br>
|
||||
* Reduces amount to current balance rather than failing due to insufficient funds
|
||||
*/
|
||||
PAY_PREVIOUS_TO_ADDRESS_IN_B(0x0404, 0, false) {
|
||||
@ -977,7 +1103,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Send A as a message to address in B<br>
|
||||
* <tt>0x0405</tt>
|
||||
* <code>0x0405</code>
|
||||
*/
|
||||
MESSAGE_A_TO_ADDRESS_IN_B(0x0405, 0, false) {
|
||||
@Override
|
||||
@ -987,7 +1113,7 @@ public enum FunctionCode {
|
||||
},
|
||||
/**
|
||||
* Add minutes to timestamp<br>
|
||||
* <tt>0x0406 timestamp minutes</tt><br>
|
||||
* <code>0x0406 timestamp minutes</code><br>
|
||||
* Return 'timestamp' based on passed 'timestamp' plus minutes
|
||||
*/
|
||||
ADD_MINUTES_TO_TIMESTAMP(0x0406, 2, true) {
|
||||
@ -997,7 +1123,7 @@ public enum FunctionCode {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <tt>0x0500 - 0x06ff</tt><br>
|
||||
* <code>0x0500 - 0x06ff</code><br>
|
||||
* Platform-specific functions.<br>
|
||||
* These are passed through to the API
|
||||
*/
|
||||
@ -1050,12 +1176,10 @@ public enum FunctionCode {
|
||||
/**
|
||||
* Execute Function
|
||||
* <p>
|
||||
* Can modify various fields of <tt>state</tt>, including <tt>programCounter</tt>.
|
||||
* Can modify various fields of <code>state</code>, including <code>programCounter</code>.
|
||||
* <p>
|
||||
* Throws a subclass of <tt>ExecutionException</tt> on error, e.g. <tt>InvalidAddressException</tt>.
|
||||
* Throws a subclass of <code>ExecutionException</code> on error, e.g. <code>InvalidAddressException</code>.
|
||||
*
|
||||
* @param functionData
|
||||
* @param state
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
public void execute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException {
|
||||
@ -1073,7 +1197,7 @@ public enum FunctionCode {
|
||||
postCheckExecute(functionData, state, rawFunctionCode);
|
||||
}
|
||||
|
||||
/** Actually execute function */
|
||||
/** Actually execute function after checks have completed. */
|
||||
protected abstract void postCheckExecute(FunctionData functionData, MachineState state, short rawFunctionCode) throws ExecutionException;
|
||||
|
||||
// TODO: public abstract String disassemble();
|
||||
|
@ -13,19 +13,19 @@ import java.util.stream.Collectors;
|
||||
* <p>
|
||||
* Op codes are represented by a single byte and maybe be followed by additional arguments like data addresses, offset, immediate values, etc.
|
||||
* <p>
|
||||
* OpCode instances can be obtained via the default <tt>OpCode.valueOf(String)</tt> or the additional <tt>OpCode.valueOf(int)</tt>.
|
||||
* OpCode instances can be obtained via the default <code>OpCode.valueOf(String)</code> or the additional <code>OpCode.valueOf(int)</code>.
|
||||
* <p>
|
||||
* Use the <tt>OpCode.execute</tt> method to perform the operation.
|
||||
* Use the <code>OpCode.execute</code> method to perform the operation.
|
||||
* <p>
|
||||
* In the documentation for each OpCode:
|
||||
* <p>
|
||||
* <tt>@addr</tt> means "store at <tt>addr</tt>"
|
||||
* <code>@addr</code> means "store at <code>addr</code>"
|
||||
* <p>
|
||||
* <tt>$addr</tt> means "fetch from <tt>addr</tt>"
|
||||
* <code>$addr</code> means "fetch from <code>addr</code>"
|
||||
* <p>
|
||||
* <tt>@($addr)</tt> means "store at address fetched from <tt>addr</tt>", i.e. indirect
|
||||
* <code>@($addr)</code> means "store at address fetched from <code>addr</code>", i.e. indirect
|
||||
* <p>
|
||||
* <tt>$($addr1 + $addr2)</tt> means "fetch from address fetched from <tt>addr1</tt> plus offset fetched from <tt>addr2</tt>", i.e. indirect indexed
|
||||
* <code>$($addr1 + $addr2)</code> means "fetch from address fetched from <code>addr1</code> plus offset fetched from <code>addr2</code>", i.e. indirect indexed
|
||||
*
|
||||
* @see OpCode#valueOf(int)
|
||||
* @see OpCode#executeWithParams(MachineState, Object...)
|
||||
@ -34,7 +34,7 @@ public enum OpCode {
|
||||
|
||||
/**
|
||||
* <b>N</b>o <b>OP</b>eration<br>
|
||||
* <tt>0x7f</tt><br>
|
||||
* <code>0x7f</code><br>
|
||||
* (Does nothing)
|
||||
*/
|
||||
NOP(0x7f) {
|
||||
@ -45,8 +45,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SET</b> <b>VAL</b>ue<br>
|
||||
* <tt>0x01 addr value</tt><br>
|
||||
* <tt>@addr = value</tt>
|
||||
* <code>0x01 addr value</code><br>
|
||||
* <code>@addr = value</code>
|
||||
*/
|
||||
SET_VAL(0x01, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
@ -59,8 +59,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SET</b> <b>DAT</b>a<br>
|
||||
* <tt>0x02 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 = $addr2</tt>
|
||||
* <code>0x02 addr1 addr2</code><br>
|
||||
* <code>@addr1 = $addr2</code>
|
||||
*/
|
||||
SET_DAT(0x02, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -74,8 +74,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>CL</b>ea<b>R</b> <b>DAT</b>a<br>
|
||||
* <tt>0x03 addr</tt><br>
|
||||
* <tt>@addr = 0</tt>
|
||||
* <code>0x03 addr</code><br>
|
||||
* <code>@addr = 0</code>
|
||||
*/
|
||||
CLR_DAT(0x03, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -87,8 +87,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>INC</b>rement <b>DAT</b>a<br>
|
||||
* <tt>0x04 addr</tt><br>
|
||||
* <tt>@addr += 1</tt>
|
||||
* <code>0x04 addr</code><br>
|
||||
* <code>@addr += 1</code>
|
||||
*/
|
||||
INC_DAT(0x04, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -101,8 +101,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>DEC</b>rement <b>DAT</b>a<br>
|
||||
* <tt>0x05 addr</tt><br>
|
||||
* <tt>@addr -= 1</tt>
|
||||
* <code>0x05 addr</code><br>
|
||||
* <code>@addr -= 1</code>
|
||||
*/
|
||||
DEC_DAT(0x05, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -115,8 +115,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>ADD</b> <b>DAT</b>a<br>
|
||||
* <tt>0x06 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 += $addr2</tt>
|
||||
* <code>0x06 addr1 addr2</code><br>
|
||||
* <code>@addr1 += $addr2</code>
|
||||
*/
|
||||
ADD_DAT(0x06, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -126,8 +126,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SUB</b>tract <b>DAT</b>a<br>
|
||||
* <tt>0x07 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 -= $addr2</tt>
|
||||
* <code>0x07 addr1 addr2</code><br>
|
||||
* <code>@addr1 -= $addr2</code>
|
||||
*/
|
||||
SUB_DAT(0x07, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -137,8 +137,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>MUL</b>tiply <b>DAT</b>a<br>
|
||||
* <tt>0x08 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 *= $addr2</tt>
|
||||
* <code>0x08 addr1 addr2</code><br>
|
||||
* <code>@addr1 *= $addr2</code>
|
||||
*/
|
||||
MUL_DAT(0x08, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -148,9 +148,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>DIV</b>ide <b>DAT</b>a<br>
|
||||
* <tt>0x09 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 /= $addr2</tt><br>
|
||||
* Can also throw <tt>IllegealOperationException</tt> if divide-by-zero attempted.
|
||||
* <code>0x09 addr1 addr2</code><br>
|
||||
* <code>@addr1 /= $addr2</code><br>
|
||||
* Can also throw <code>IllegalOperationException</code> if divide-by-zero attempted.
|
||||
*/
|
||||
DIV_DAT(0x09, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -164,8 +164,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>inary-<b>OR</b> <b>DAT</b>a<br>
|
||||
* <tt>0x0a addr1 addr2</tt><br>
|
||||
* <tt>@addr1 |= $addr2</tt>
|
||||
* <code>0x0a addr1 addr2</code><br>
|
||||
* <code>@addr1 |= $addr2</code>
|
||||
*/
|
||||
BOR_DAT(0x0a, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -175,8 +175,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Binary-<b>AND</b> <b>DAT</b>a<br>
|
||||
* <tt>0x0b addr1 addr2</tt><br>
|
||||
* <tt>@addr1 &= $addr2</tt>
|
||||
* <code>0x0b addr1 addr2</code><br>
|
||||
* <code>@addr1 &= $addr2</code>
|
||||
*/
|
||||
AND_DAT(0x0b, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -186,8 +186,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* E<b>X</b>clusive <b>OR</b> <b>DAT</b>a<br>
|
||||
* <tt>0x0c addr1 addr2</tt><br>
|
||||
* <tt>@addr1 ^= $addr2</tt>
|
||||
* <code>0x0c addr1 addr2</code><br>
|
||||
* <code>@addr1 ^= $addr2</code>
|
||||
*/
|
||||
XOR_DAT(0x0c, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -197,8 +197,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Bitwise-<b>NOT</b> <b>DAT</b>a<br>
|
||||
* <tt>0x0d addr</tt><br>
|
||||
* <tt>@addr = ~$addr</tt>
|
||||
* <code>0x0d addr</code><br>
|
||||
* <code>@addr = ~$addr</code>
|
||||
*/
|
||||
NOT_DAT(0x0d, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -211,8 +211,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SET</b> using <b>IND</b>irect data<br>
|
||||
* <tt>0x0e addr1 addr2</tt><br>
|
||||
* <tt>@addr1 = $($addr2)</tt>
|
||||
* <code>0x0e addr1 addr2</code><br>
|
||||
* <code>@addr1 = $($addr2)</code>
|
||||
*/
|
||||
SET_IND(0x0e, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR) {
|
||||
@Override
|
||||
@ -231,8 +231,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SET</b> using indirect <b>I</b>n<b>D</b>e<b>X</b>ed data<br>
|
||||
* <tt>0x0f addr1 addr2 addr3</tt><br>
|
||||
* <tt>@addr1 = $($addr2 + $addr3)</tt>
|
||||
* <code>0x0f addr1 addr2 addr3</code><br>
|
||||
* <code>@addr1 = $($addr2 + $addr3)</code>
|
||||
*/
|
||||
SET_IDX(0x0f, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR_WITH_INDEX, OpCodeParam.INDEX) {
|
||||
@Override
|
||||
@ -255,9 +255,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>P</b>u<b>SH</b> <b>DAT</b>a onto user stack<br>
|
||||
* <tt>0x10 addr</tt><br>
|
||||
* <tt>@--user_stack = $addr</tt><br>
|
||||
* Can also throw <tt>StackBoundsException</tt> if user stack exhausted.
|
||||
* <code>0x10 addr</code><br>
|
||||
* <code>@--user_stack = $addr</code><br>
|
||||
* Can also throw <code>StackBoundsException</code> if user stack exhausted.
|
||||
*/
|
||||
PSH_DAT(0x10, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -278,9 +278,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>POP</b> <b>DAT</b>a from user stack<br>
|
||||
* <tt>0x11 addr</tt><br>
|
||||
* <tt>@addr = $user_stack++</tt><br>
|
||||
* Can also throw <tt>StackBoundsException</tt> if user stack empty.
|
||||
* <code>0x11 addr</code><br>
|
||||
* <code>@addr = $user_stack++</code><br>
|
||||
* Can also throw <code>StackBoundsException</code> if user stack empty.
|
||||
*/
|
||||
POP_DAT(0x11, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -302,9 +302,10 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>J</b>u<b>MP</b> into <b>SUB</b>routine<br>
|
||||
* <tt>0x12 addr</tt><br>
|
||||
* <tt>@--call_stack = PC after opcode & args</tt>, <tt>PC = addr</tt><br>
|
||||
* Can also throw <tt>StackBoundsException</tt> if call stack exhausted.
|
||||
* <code>0x12 addr</code><br>
|
||||
* <code>@--call_stack = PC after opcode and args</code>,<br>
|
||||
* <code>PC = addr</code><br>
|
||||
* Can also throw <code>StackBoundsException</code> if call stack exhausted.
|
||||
*/
|
||||
JMP_SUB(0x12, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
@ -325,9 +326,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>RET</b>urn from <b>SUB</b>routine<br>
|
||||
* <tt>0x13<br>
|
||||
* <tt>PC = $call_stack++</tt><br>
|
||||
* Can also throw <tt>StackBoundsException</tt> if call stack empty.
|
||||
* <code>0x13</code><br>
|
||||
* <code>PC = $call_stack++</code><br>
|
||||
* Can also throw <code>StackBoundsException</code> if call stack empty.
|
||||
*/
|
||||
RET_SUB(0x13) {
|
||||
@Override
|
||||
@ -346,8 +347,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Store <b>IND</b>irect <b>DAT</b>a<br>
|
||||
* <tt>0x14 addr1 addr2<br>
|
||||
* <tt>@($addr1) = $addr2</tt>
|
||||
* <code>0x14 addr1 addr2</code><br>
|
||||
* <code>@($addr1) = $addr2</code>
|
||||
*/
|
||||
IND_DAT(0x14, OpCodeParam.INDIRECT_DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -366,8 +367,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Store indirect <b>I</b>n<b>D</b>e<b>X</b>ed <b>DAT</b>a<br>
|
||||
* <tt>0x15 addr1 addr2<br>
|
||||
* <tt>@($addr1 + $addr2) = $addr3</tt>
|
||||
* <code>0x15 addr1 addr2</code><br>
|
||||
* <code>@($addr1 + $addr2) = $addr3</code>
|
||||
*/
|
||||
IDX_DAT(0x15, OpCodeParam.INDIRECT_DEST_ADDR_WITH_INDEX, OpCodeParam.INDEX, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -390,8 +391,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>MOD</b>ulo <b>DAT</b>a<br>
|
||||
* <tt>0x16 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 %= $addr2</tt>
|
||||
* <code>0x16 addr1 addr2</code><br>
|
||||
* <code>@addr1 %= $addr2</code>
|
||||
*/
|
||||
MOD_DAT(0x16, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -405,8 +406,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SH</b>ift <b>L</b>eft <b>DAT</b>a<br>
|
||||
* <tt>0x17 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 <<= $addr2</tt>
|
||||
* <code>0x17 addr1 addr2</code><br>
|
||||
* <code>@addr1 <<= $addr2</code>
|
||||
*/
|
||||
SHL_DAT(0x17, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
|
||||
@ -419,8 +420,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SH</b>ift <b>R</b>ight <b>DAT</b>a<br>
|
||||
* <tt>0x18 addr1 addr2</tt><br>
|
||||
* <tt>@addr1 >>= $addr2</tt><br>
|
||||
* <code>0x18 addr1 addr2</code><br>
|
||||
* <code>@addr1 >>= $addr2</code><br>
|
||||
* Note: new MSB bit will be zero
|
||||
*/
|
||||
SHR_DAT(0x18, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@ -434,8 +435,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>J</b>u<b>MP</b> to <b>AD</b>d<b>R</b>ess<br>
|
||||
* <tt>0x1a addr</tt><br>
|
||||
* <tt>PC = addr</tt>
|
||||
* <code>0x1a addr</code><br>
|
||||
* <code>PC = addr</code>
|
||||
*/
|
||||
JMP_ADR(0x1a, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
@ -447,9 +448,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>Z</b>e<b>R</b>o<br>
|
||||
* <tt>0x1b addr offset</tt><br>
|
||||
* <tt>if ($addr == 0) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x1b addr offset</code><br>
|
||||
* <code>if ($addr == 0) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BZR_DAT(0x1b, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -467,9 +468,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>N</b>ot <b>Z</b>ero<br>
|
||||
* <tt>0x1e addr offset</tt><br>
|
||||
* <tt>if ($addr != 0) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x1e addr offset</code><br>
|
||||
* <code>if ($addr != 0) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BNZ_DAT(0x1e, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -487,9 +488,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>G</b>reater-<b>T</b>han <b>DAT</b>a<br>
|
||||
* <tt>0x1f addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 > $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x1f addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 > $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BGT_DAT(0x1f, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -499,9 +500,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>L</b>ess-<b>T</b>han <b>DAT</b>a<br>
|
||||
* <tt>0x20 addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 < $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x20 addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 < $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BLT_DAT(0x20, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -511,9 +512,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>G</b>reater-or-<b>E</b>qual <b>DAT</b>a<br>
|
||||
* <tt>0x21 addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 >= $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x21 addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 >= $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BGE_DAT(0x21, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -523,9 +524,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>L</b>ess-or-<b>E</b>qual <b>DAT</b>a<br>
|
||||
* <tt>0x22 addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 <= $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x22 addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 <= $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BLE_DAT(0x22, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -535,9 +536,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>EQ</b>ual <b>DAT</b>a<br>
|
||||
* <tt>0x23 addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 == $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x23 addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 == $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BEQ_DAT(0x23, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -547,9 +548,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>B</b>ranch if <b>N</b>ot-<b>E</b>qual <b>DAT</b>a<br>
|
||||
* <tt>0x24 addr1 addr2 offset</tt><br>
|
||||
* <tt>if ($addr1 != $addr2) PC += offset</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately before opcode byte.
|
||||
* <code>0x24 addr1 addr2 offset</code><br>
|
||||
* <code>if ($addr1 != $addr2) PC += offset</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.
|
||||
*/
|
||||
BNE_DAT(0x24, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) {
|
||||
@Override
|
||||
@ -559,16 +560,16 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SL</b>ee<b>P</b> until <b>DAT</b>a<br>
|
||||
* <tt>0x25 addr</tt><br>
|
||||
* <tt>sleep until $addr, then carry on from current PC</tt><br>
|
||||
* Note: The value from <tt>$addr</tt> is considered to be a block height.
|
||||
* <code>0x25 addr</code><br>
|
||||
* <code>sleep until $addr, then carry on from current PC</code><br>
|
||||
* Note: The value from <code>$addr</code> is considered to be a block height.
|
||||
*/
|
||||
SLP_DAT(0x25, OpCodeParam.BLOCK_HEIGHT) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
int address = (int) args[0];
|
||||
|
||||
long value = state.codeByteBuffer.getLong(address);
|
||||
long value = state.dataByteBuffer.getLong(address);
|
||||
|
||||
state.setSleepUntilHeight((int) value);
|
||||
state.setIsSleeping(true);
|
||||
@ -576,8 +577,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>FI</b>nish if <b>Z</b>ero <b>DAT</b>a<br>
|
||||
* <tt>0x26 addr</tt><br>
|
||||
* <tt>if ($addr == 0) permanently stop</tt>
|
||||
* <code>0x26 addr</code><br>
|
||||
* <code>if ($addr == 0) permanently stop</code>
|
||||
*/
|
||||
FIZ_DAT(0x26, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -592,8 +593,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>ST</b>op if <b>Z</b>ero <b>DAT</b>a<br>
|
||||
* <tt>0x27 addr</tt><br>
|
||||
* <tt>if ($addr == 0) PC = PCS and stop</tt>
|
||||
* <code>0x27 addr</code><br>
|
||||
* <code>if ($addr == 0) PC = PCS and stop</code>
|
||||
*/
|
||||
STZ_DAT(0x27, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -610,8 +611,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>FIN</b>ish <b>IM</b>me<b>D</b>iately<br>
|
||||
* <tt>0x28</tt><br>
|
||||
* <tt>permanently stop</tt>
|
||||
* <code>0x28</code><br>
|
||||
* <code>permanently stop</code>
|
||||
*/
|
||||
FIN_IMD(0x28) {
|
||||
@Override
|
||||
@ -621,8 +622,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>ST</b>o<b>P</b> <b>IM</b>me<b>D</b>iately<br>
|
||||
* <tt>0x29</tt><br>
|
||||
* <tt>stop</tt>
|
||||
* <code>0x29</code><br>
|
||||
* <code>stop</code>
|
||||
*/
|
||||
STP_IMD(0x29) {
|
||||
@Override
|
||||
@ -632,8 +633,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SL</b>ee<b>P</b> <b>IM</b>me<b>D</b>iately<br>
|
||||
* <tt>0x2a</tt><br>
|
||||
* <tt>sleep until next block, then carry on from current PC</tt>
|
||||
* <code>0x2a</code><br>
|
||||
* <code>sleep until next block, then carry on from current PC</code>
|
||||
*/
|
||||
SLP_IMD(0x2a) {
|
||||
@Override
|
||||
@ -644,8 +645,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Set <b>ERR</b>or <b>AD</b>d<b>R</b>ess<br>
|
||||
* <tt>0x2b addr</tt><br>
|
||||
* <tt>PCE = addr</tt>
|
||||
* <code>0x2b addr</code><br>
|
||||
* <code>PCE = addr</code>
|
||||
*/
|
||||
ERR_ADR(0x2b, OpCodeParam.CODE_ADDR) {
|
||||
@Override
|
||||
@ -655,11 +656,26 @@ public enum OpCode {
|
||||
state.setOnErrorAddress(address);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>SL</b>ee<b>P</b> for <b>VAL</b>ue blocks<br>
|
||||
* <code>0x2c value</code><br>
|
||||
* <code>sleep until $addr, then carry on from current PC</code><br>
|
||||
* Note: The value from <code>$addr</code> is considered to be a block height.
|
||||
*/
|
||||
SLP_VAL(0x2c, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
long value = (long) args[0];
|
||||
|
||||
state.setSleepUntilHeight(state.getCurrentBlockHeight() + (int) value);
|
||||
state.setIsSleeping(true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>SET</b> <b>PCS</b> (stop address)<br>
|
||||
* <tt>0x30</tt><br>
|
||||
* <tt>PCS = PC</tt><br>
|
||||
* Note: <tt>PC</tt> is considered to be immediately after this opcode byte.
|
||||
* <code>0x30</code><br>
|
||||
* <code>PCS = PC</code><br>
|
||||
* Note: <code>PC</code> is considered to be immediately after this opcode byte.
|
||||
*/
|
||||
SET_PCS(0x30) {
|
||||
@Override
|
||||
@ -669,8 +685,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction<br>
|
||||
* <tt>0x32 func</tt><br>
|
||||
* <tt>func()</tt>
|
||||
* <code>0x32 func</code><br>
|
||||
* <code>func()</code>
|
||||
*/
|
||||
EXT_FUN(0x32, OpCodeParam.FUNC) {
|
||||
@Override
|
||||
@ -699,8 +715,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction with <b>DAT</b>a<br>
|
||||
* <tt>0x33 func addr</tt><br>
|
||||
* <tt>func($addr)</tt>
|
||||
* <code>0x33 func addr</code><br>
|
||||
* <code>func($addr)</code>
|
||||
*/
|
||||
EXT_FUN_DAT(0x33, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -731,8 +747,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction with <b>DAT</b>a x<b>2</b><br>
|
||||
* <tt>0x34 func addr1 addr2</tt><br>
|
||||
* <tt>func($addr1, $addr2)</tt>
|
||||
* <code>0x34 func addr1 addr2</code><br>
|
||||
* <code>func($addr1, $addr2)</code>
|
||||
*/
|
||||
EXT_FUN_DAT_2(0x34, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -765,8 +781,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction expecting <b>RET</b>urn value<br>
|
||||
* <tt>0x35 func addr</tt><br>
|
||||
* <tt>@addr = func()</tt>
|
||||
* <code>0x35 func addr</code><br>
|
||||
* <code>@addr = func()</code>
|
||||
*/
|
||||
EXT_FUN_RET(0x35, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR) {
|
||||
@Override
|
||||
@ -801,8 +817,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction expecting <b>RET</b>urn value with <b>DAT</b>a<br>
|
||||
* <tt>0x36 func addr1 addr2</tt><br>
|
||||
* <tt>@addr1 = func($addr2)</tt>
|
||||
* <code>0x36 func addr1 addr2</code><br>
|
||||
* <code>@addr1 = func($addr2)</code>
|
||||
*/
|
||||
EXT_FUN_RET_DAT(0x36, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -839,8 +855,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction expecting <b>RET</b>urn value with <b>DAT</b>a x<b>2</b><br>
|
||||
* <tt>0x37 func addr1 addr2 addr3</tt><br>
|
||||
* <tt>@addr1 = func($addr2, $addr3)</tt>
|
||||
* <code>0x37 func addr1 addr2 addr3</code><br>
|
||||
* <code>@addr1 = func($addr2, $addr3)</code>
|
||||
*/
|
||||
EXT_FUN_RET_DAT_2(0x37, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) {
|
||||
@Override
|
||||
@ -877,10 +893,41 @@ public enum OpCode {
|
||||
state.dataByteBuffer.putLong(address1, functionData.returnValue);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Call <b>EXT</b>ernal <b>FUN</b>ction with <b>VAL</b>ue<br>
|
||||
* <code>0x38 func value</code><br>
|
||||
* <code>func(value)</code>
|
||||
*/
|
||||
EXT_FUN_VAL(0x38, OpCodeParam.FUNC, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void preExecuteCheck(Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
|
||||
FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode);
|
||||
|
||||
if (functionCode == null)
|
||||
throw new IllegalFunctionCodeException(String.format("Unknown function code 0x%04x encountered at %s",
|
||||
rawFunctionCode, this.name()));
|
||||
|
||||
functionCode.preExecuteCheck(1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
short rawFunctionCode = (short) args[0];
|
||||
long value = (long) args[1];
|
||||
|
||||
FunctionCode functionCode = FunctionCode.valueOf(rawFunctionCode);
|
||||
|
||||
FunctionData functionData = new FunctionData(value, false);
|
||||
|
||||
functionCode.execute(functionData, state, rawFunctionCode);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>ADD</b> <b>VAL</b>ue<br>
|
||||
* <tt>0x46 addr1 value</tt><br>
|
||||
* <tt>@addr1 += value</tt>
|
||||
* <code>0x46 addr1 value</code><br>
|
||||
* <code>@addr1 += value</code>
|
||||
*/
|
||||
ADD_VAL(0x46, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
@ -890,8 +937,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>SUB</b>tract <b>VAL</b>ue<br>
|
||||
* <tt>0x07 addr1 value</tt><br>
|
||||
* <tt>@addr1 -= value</tt>
|
||||
* <code>0x47 addr1 value</code><br>
|
||||
* <code>@addr1 -= value</code>
|
||||
*/
|
||||
SUB_VAL(0x47, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
@ -901,8 +948,8 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>MUL</b>tiply <b>VAL</b>ue<br>
|
||||
* <tt>0x08 addr1 value</tt><br>
|
||||
* <tt>@addr1 *= value</tt>
|
||||
* <code>0x48 addr1 value</code><br>
|
||||
* <code>@addr1 *= value</code>
|
||||
*/
|
||||
MUL_VAL(0x48, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
@ -912,9 +959,9 @@ public enum OpCode {
|
||||
},
|
||||
/**
|
||||
* <b>DIV</b>ide <b>VAL</b>ue<br>
|
||||
* <tt>0x09 addr1 value</tt><br>
|
||||
* <tt>@addr1 /= value</tt>
|
||||
* Can also throw <tt>IllegealOperationException</tt> if divide-by-zero attempted.
|
||||
* <code>0x49 addr1 value</code><br>
|
||||
* <code>@addr1 /= value</code>
|
||||
* Can also throw <code>IllegalOperationException</code> if divide-by-zero attempted.
|
||||
*/
|
||||
DIV_VAL(0x49, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
@ -925,6 +972,35 @@ public enum OpCode {
|
||||
throw new IllegalOperationException("Divide by zero", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>SH</b>ift <b>L</b>eft <b>VAL</b>ue<br>
|
||||
* <code>0x4a addr1 value</code><br>
|
||||
* <code>@addr1 <<= value</code>
|
||||
*/
|
||||
SHL_VAL(0x4a, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
|
||||
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
// If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence
|
||||
executeValueOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a << b, args);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>SH</b>ift <b>R</b>ight <b>VAL</b>ue<br>
|
||||
* <code>0x4b addr1 value</code><br>
|
||||
* <code>@addr1 >>= value</code><br>
|
||||
* Note: new MSB bit will be zero
|
||||
*/
|
||||
SHR_VAL(0x4b, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
private static final long MAX_SHIFT = MachineState.VALUE_SIZE * 8L;
|
||||
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
// If 2nd arg is more than value size (in bits) then return 0 to simulate all bits being shifted out of existence
|
||||
executeValueOperation(state, (a, b) -> b >= MAX_SHIFT ? 0 : a >>> b, args);
|
||||
}
|
||||
};
|
||||
|
||||
public final byte value;
|
||||
@ -945,14 +1021,14 @@ public enum OpCode {
|
||||
/**
|
||||
* Execute OpCode with args fetched from code bytes
|
||||
* <p>
|
||||
* Assumes <tt>codeByteBuffer.position()</tt> is already placed immediately after opcode and params.<br>
|
||||
* <tt>state.getProgramCounter()</tt> is available to return position immediately before opcode and params.
|
||||
* Assumes <code>codeByteBuffer.position()</code> is already placed immediately after opcode and params.<br>
|
||||
* <code>state.getProgramCounter()</code> is available to return position immediately before opcode and params.
|
||||
* <p>
|
||||
* OpCode execution can modify <tt>codeByteBuffer.position()</tt> in cases like jumps, branches, etc.
|
||||
* OpCode execution can modify <code>codeByteBuffer.position()</code> in cases like jumps, branches, etc.
|
||||
* <p>
|
||||
* Can also modify <tt>userStackByteBuffer</tt> and various fields of <tt>state</tt>.
|
||||
* Can also modify <code>userStackByteBuffer</code> and various fields of <code>state</code>.
|
||||
* <p>
|
||||
* Throws a subclass of <tt>ExecutionException</tt> on error, e.g. <tt>InvalidAddressException</tt>.
|
||||
* Throws a subclass of <code>ExecutionException</code> on error, e.g. <code>InvalidAddressException</code>.
|
||||
*
|
||||
* @param state
|
||||
* @param args
|
||||
@ -978,11 +1054,18 @@ public enum OpCode {
|
||||
this.executeWithParams(state, argsArray);
|
||||
}
|
||||
|
||||
public static int calcOffset(ByteBuffer byteBuffer, Integer branchTarget) {
|
||||
public static int calcOffset(ByteBuffer byteBuffer, Integer branchTarget) throws CompilationException {
|
||||
// First-pass of compilation where we don't know branchTarget yet
|
||||
if (branchTarget == null)
|
||||
return 0;
|
||||
|
||||
return branchTarget - byteBuffer.position();
|
||||
int offset = branchTarget - byteBuffer.position();
|
||||
|
||||
// Bounds checking
|
||||
if (offset < Byte.MIN_VALUE || offset > Byte.MAX_VALUE)
|
||||
throw new CompilationException(String.format("Branch offset %02x (from PC %04x) is wider than a byte", offset, byteBuffer.position()));
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
public byte[] compile(Object... args) throws CompilationException {
|
||||
@ -1032,10 +1115,10 @@ public enum OpCode {
|
||||
/**
|
||||
* Common code for ADD_DAT/SUB_DAT/MUL_DAT/DIV_DAT/MOD_DAT/SHL_DAT/SHR_DAT
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @param dataByteBuffer
|
||||
* @param state
|
||||
* @param operator
|
||||
* - typically a lambda operating on two <tt>long</tt> params, e.g. <tt>(a, b) -> a + b</tt>
|
||||
* - typically a lambda operating on two <code>long</code> params, e.g. <code>(a, b) → a + b</code>
|
||||
* @param args
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
protected void executeDataOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException {
|
||||
@ -1052,11 +1135,11 @@ public enum OpCode {
|
||||
|
||||
/**
|
||||
* Common code for ADD_VAL/SUB_VAL/MUL_VAL/DIV_VAL/MOD_VAL/SHL_VAL/SHR_VAL
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @param dataByteBuffer
|
||||
*
|
||||
* @param state
|
||||
* @param operator
|
||||
* - typically a lambda operating on two <tt>long</tt> params, e.g. <tt>(a, b) -> a + b</tt>
|
||||
* - typically a lambda operating on two <code>long</code> params, e.g. <code>(a, b) → a + b</code>
|
||||
* @param args
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
protected void executeValueOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException {
|
||||
@ -1073,11 +1156,10 @@ public enum OpCode {
|
||||
/**
|
||||
* Common code for BGT/BLT/BGE/BLE/BEQ/BNE
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @param dataByteBuffer
|
||||
* @param state
|
||||
* @param comparator
|
||||
* - typically a lambda comparing two <tt>long</tt> params, e.g. <tt>(a, b) -> a == b</tt>
|
||||
* - typically a lambda comparing two <code>long</code> params, e.g. <code>(a, b) → a == b</code>
|
||||
* @param args
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
protected void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException {
|
||||
|
@ -4,6 +4,15 @@ import java.nio.ByteBuffer;
|
||||
|
||||
enum OpCodeParam {
|
||||
|
||||
/**
|
||||
* Literal <b>64-bit long</b> value supplied from <b>code</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_VAL DEST_ADDR <b>VALUE</b></code><br>
|
||||
* Example: <code>SET_VAL 3 <b>12345</b></code><br>
|
||||
* Data segment address 3 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
VALUE(OpCodeParam::compileLong) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -15,6 +24,15 @@ enum OpCodeParam {
|
||||
return String.format("#%016x", (Long) value);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Destination address in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_VAL <b>DEST_ADDR</b> VALUE</code><br>
|
||||
* Example: <code>SET_VAL <b>3</b> 12345</code><br>
|
||||
* Data segment address 3 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
DEST_ADDR(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -26,6 +44,18 @@ enum OpCodeParam {
|
||||
return String.format("@%08x", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indirect destination address in <b>data</b> segment,<br>
|
||||
* using value extracted from supplied address, also in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>IND_DAT <b>INDIRECT_DEST_ADDR</b> SRC_ADDR</code><br>
|
||||
* Example: <code>IND_DAT <b>3</b> 4</code><br>
|
||||
* If data segment address 3 contains the value 7,<br>
|
||||
* and data segment address 4 contains the value 12345,<br>
|
||||
* then data segment address 7 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
INDIRECT_DEST_ADDR(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -37,6 +67,21 @@ enum OpCodeParam {
|
||||
return String.format("@($%08x)", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indirect destination address in <b>data</b> segment,<br>
|
||||
* using value extracted from supplied address, also in <b>data</b> segment,<br>
|
||||
* and then offset by the value extracted from 2nd 'index' address, also in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>IDX_DAT <b>INDIRECT_DEST_ADDR_WITH_INDEX</b> INDEX SRC_ADDR</code><br>
|
||||
* Example: <code>IDX_DAT <b>3</b> 4 20</code><br>
|
||||
* If data segment address 3 contains the value 7,<br>
|
||||
* and data segment address 4 contains the value 2,<br>
|
||||
* and data segment address 20 contains the value 12345,<br>
|
||||
* then data segment address 9 (7 + 2) will be set to the value 12345.
|
||||
* </p>
|
||||
* @see OpCodeParam#INDEX
|
||||
*/
|
||||
INDIRECT_DEST_ADDR_WITH_INDEX(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -48,6 +93,16 @@ enum OpCodeParam {
|
||||
return String.format("@($%08x", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Source address in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_DAT DEST_ADDR <b>SRC_ADDR</b></code><br>
|
||||
* Example: <code>SET_DAT 2 <b>3</b></code><br>
|
||||
* If data segment address 3 contains the value 12345,<br>
|
||||
* then data segment address 2 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
SRC_ADDR(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -59,6 +114,18 @@ enum OpCodeParam {
|
||||
return String.format("$%08x", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indirect source address in <b>data</b> segment,<br>
|
||||
* using value extracted from supplied address, also in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_IND DEST_ADDR <b>INDIRECT_SRC_ADDR</b></code><br>
|
||||
* Example: <code>SET_IND 3 <b>4</b></code><br>
|
||||
* If data segment address 4 contains the value 7,<br>
|
||||
* and data segment address 7 contains the value 12345,<br>
|
||||
* then data segment address 3 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
INDIRECT_SRC_ADDR(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -70,6 +137,20 @@ enum OpCodeParam {
|
||||
return String.format("$($%08x)", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Indirect source address in <b>data</b> segment,<br>
|
||||
* using value extracted from supplied address, also in <b>data</b> segment,<br>
|
||||
* and then offset by the value extracted from 2nd 'index' address, also in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_IDX DEST_ADDR <b>INDIRECT_SRC_ADDR</b> INDEX</code><br>
|
||||
* Example: <code>SET_IDX 3 <b>4</b> 5</code><br>
|
||||
* If data segment address 4 contains the value 7,<br>
|
||||
* and data segment address 5 contains the value 2,<br>
|
||||
* and data segment address 9 (7 + 2) contains the value 12345,<br>
|
||||
* then data segment address 3 will be set to the value 12345.
|
||||
* </p>
|
||||
*/
|
||||
INDIRECT_SRC_ADDR_WITH_INDEX(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -81,6 +162,21 @@ enum OpCodeParam {
|
||||
return String.format("$($%08x", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Offset value extracted from address in <b>data</b> segment.<br>
|
||||
* Used with {@link OpCodeParam#INDIRECT_DEST_ADDR_WITH_INDEX} and {@link OpCodeParam#INDIRECT_DEST_ADDR_WITH_INDEX}
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SET_IDX DEST_ADDR INDIRECT_SRC_ADDR <b>INDEX</b></code><br>
|
||||
* Example: <code>SET_IDX 3 4 <b>5</b></code><br>
|
||||
* If data segment address 4 contains the value 7,<br>
|
||||
* and data segment address 5 contains the value 2,<br>
|
||||
* and data segment address 9 (7 + 2) contains the value 12345,<br>
|
||||
* then data segment address 3 will be set to the value 12345.
|
||||
* </p>
|
||||
* @see OpCodeParam#INDIRECT_DEST_ADDR_WITH_INDEX
|
||||
* @see OpCodeParam#INDIRECT_SRC_ADDR_WITH_INDEX
|
||||
*/
|
||||
INDEX(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -92,6 +188,15 @@ enum OpCodeParam {
|
||||
return String.format("+ $%08x)", ((Integer) value) / MachineState.VALUE_SIZE);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Literal program address in <b>code</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>JMP_ADR <b>CODE_ADDR</b></code><br>
|
||||
* Example: <code>JMP_ADR <b>123</b></code><br>
|
||||
* Jump (set PC) to code address 123.
|
||||
* </p>
|
||||
*/
|
||||
CODE_ADDR(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -103,6 +208,21 @@ enum OpCodeParam {
|
||||
return String.format("[%04x]", (Integer) value);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>Byte</b> offset from current program counter, in <b>code</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>BZR_DAT SRC_ADDR <b>OFFSET</b></code><br>
|
||||
* Example: <code>BZR_DAT 4 <b>123</b></code><br>
|
||||
* If data segment address 4 contains the value 0,<br>
|
||||
* then add 123 to program counter (PC).
|
||||
* </p>
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Note: <code>PC</code> is considered to be immediately before opcode byte.<br>
|
||||
* Because this value is only a signed byte, maximum offsets are -128 and +127!
|
||||
* </p>
|
||||
*/
|
||||
OFFSET(OpCodeParam::compileByte) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -114,6 +234,17 @@ enum OpCodeParam {
|
||||
return String.format("PC+%02x=[%04x]", (int) ((Byte) value), postOpcodeProgramCounter - 1 + (Byte) value);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Literal <b>16-bit short</b> function code supplied from <b>code</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>EXT_FUN <b>FUNC</b></code><br>
|
||||
* Example: <code>EXT_FUN <b>0x0001</b></code><br>
|
||||
* Calls function 0x0001 (ECHO).
|
||||
* </p>
|
||||
* @see FunctionCode#ECHO
|
||||
* @see FunctionCode
|
||||
*/
|
||||
FUNC(OpCodeParam::compileFunc) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
@ -135,10 +266,20 @@ enum OpCodeParam {
|
||||
return "\"" + functionCode.name() + "\"" + String.format("{%04x}", (Short) value);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Block height extracted via address in <b>data</b> segment.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* Example: <code>SLP_DAT <b>BLOCK_HEIGHT</b></code><br>
|
||||
* Example: <code>SLP_DAT <b>3</b></code><br>
|
||||
* If data segment address 3 contains the value 12345,<br>
|
||||
* then the AT will sleep until block height reaches 12345.
|
||||
* </p>
|
||||
*/
|
||||
BLOCK_HEIGHT(OpCodeParam::compileInt) {
|
||||
@Override
|
||||
public Object fetch(ByteBuffer codeByteBuffer, ByteBuffer dataByteBuffer) throws ExecutionException {
|
||||
return Integer.valueOf(codeByteBuffer.getInt());
|
||||
return Integer.valueOf(Utils.getDataAddress(codeByteBuffer, dataByteBuffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,16 +4,16 @@ package org.ciyam.at;
|
||||
* CIYAM-AT "Timestamp"
|
||||
* <p>
|
||||
* With CIYAM-ATs, "timestamp" does not mean a real timestamp but instead is an artificial timestamp that includes three parts:
|
||||
* <p>
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>block height (32 bits)</li>
|
||||
* <li>blockchain ID (8 bits)</li>
|
||||
* <li>intra-block transaction sequence (24 bits)</li>
|
||||
* </ul>
|
||||
* This allows up to 256 different blockchains and up to ~16million transactions per block.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* A blockchain ID of zero is assumed to be the 'native' blockchain.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* Timestamp values are not directly manipulated by AT OpCodes so endianness isn't important here.
|
||||
*
|
||||
* @see Timestamp#Timestamp(int, int, int)
|
||||
|
@ -8,7 +8,7 @@ interface Utils {
|
||||
/**
|
||||
* Returns immediate function code enum from code bytes at current position.
|
||||
* <p>
|
||||
* Initial position is <tt>codeByteBuffer.position()</tt> but on return is incremented by 2.
|
||||
* Initial position is <code>codeByteBuffer.position()</code> but on return is incremented by 2.
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @return FunctionCode
|
||||
@ -33,9 +33,9 @@ interface Utils {
|
||||
/**
|
||||
* Returns code address from code bytes at current position.
|
||||
* <p>
|
||||
* Initial position is <tt>codeByteBuffer.position()</tt> but on return is incremented by 4.
|
||||
* Initial position is <code>codeByteBuffer.position()</code> but on return is incremented by 4.
|
||||
* <p>
|
||||
* <b>Note:</b> address is not scaled by <tt>Constants.VALUE_SIZE</tt> unlike other methods in this class.
|
||||
* <b>Note:</b> address is not scaled by <code>Constants.VALUE_SIZE</code> unlike other methods in this class.
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @return int address into code segment
|
||||
@ -58,9 +58,9 @@ interface Utils {
|
||||
/**
|
||||
* Returns data address from code bytes at current position.
|
||||
* <p>
|
||||
* Initial position is <tt>codeByteBuffer.position()</tt> but on return is incremented by 4.
|
||||
* Initial position is <code>codeByteBuffer.position()</code> but on return is incremented by 4.
|
||||
* <p>
|
||||
* <b>Note:</b> address is returned scaled by <tt>Constants.VALUE_SIZE</tt>.
|
||||
* <b>Note:</b> address is returned scaled by <code>Constants.VALUE_SIZE</code>.
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @return int address into data segment
|
||||
@ -83,9 +83,9 @@ interface Utils {
|
||||
/**
|
||||
* Returns byte offset from code bytes at current position.
|
||||
* <p>
|
||||
* Initial position is <tt>codeByteBuffer.position()</tt> but on return is incremented by 1.
|
||||
* Initial position is <code>codeByteBuffer.position()</code> but on return is incremented by 1.
|
||||
* <p>
|
||||
* <b>Note:</b> offset is not scaled by <tt>Constants.VALUE_SIZE</tt> unlike other methods in this class.
|
||||
* <b>Note:</b> offset is not scaled by <code>Constants.VALUE_SIZE</code> unlike other methods in this class.
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @return byte offset
|
||||
@ -103,7 +103,7 @@ interface Utils {
|
||||
/**
|
||||
* Returns long immediate value from code bytes at current position.
|
||||
* <p>
|
||||
* Initial position is <tt>codeByteBuffer.position()</tt> but on return is incremented by 8.
|
||||
* Initial position is <code>codeByteBuffer.position()</code> but on return is incremented by 8.
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @return long value
|
||||
|
@ -145,6 +145,33 @@ public class CompileTests {
|
||||
assertTrue(Arrays.equals(expectedBytes, actualBytes));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoPassBranchCompileFailure() throws CompilationException {
|
||||
int addrData = 0;
|
||||
Integer actualTarget = null;
|
||||
int expectedTarget = 0x06;
|
||||
|
||||
// Old version
|
||||
codeByteBuffer.put(OpCode.BZR_DAT.value).putInt(addrData).put((byte) expectedTarget);
|
||||
|
||||
// Two-pass version
|
||||
ByteBuffer compileBuffer = ByteBuffer.allocate(512);
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
compileBuffer.clear();
|
||||
|
||||
try {
|
||||
compileBuffer.put(OpCode.BZR_DAT.compile(addrData, calcOffset(compileBuffer, actualTarget)));
|
||||
} catch (CompilationException e) {
|
||||
// expected
|
||||
return;
|
||||
}
|
||||
|
||||
actualTarget = compileBuffer.position() + 333; // wider than a signed byte
|
||||
}
|
||||
|
||||
fail("CompilationException should have been thrown");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testComplexCompile() throws CompilationException {
|
||||
|
@ -16,6 +16,40 @@ public class FunctionCodeTests extends ExecutableTest {
|
||||
int sourceAddress = 2;
|
||||
int destAddress = sourceAddress + MachineState.AB_REGISTER_SIZE / MachineState.VALUE_SIZE;
|
||||
|
||||
// Not used (compared to indirect method)
|
||||
dataByteBuffer.putLong(12345L);
|
||||
// Not used (compared to indirect method)
|
||||
dataByteBuffer.putLong(54321L);
|
||||
|
||||
// Data to load into A (or B)
|
||||
assertEquals(sourceAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
dataByteBuffer.put(TEST_BYTES);
|
||||
|
||||
// Data saved from A (or B)
|
||||
assertEquals(destAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
|
||||
// Set A register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.SET_A_DAT.value).putLong(sourceAddress);
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
|
||||
// Save B register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.GET_B_DAT.value).putLong(destAddress);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
byte[] dest = new byte[TEST_BYTES.length];
|
||||
getDataBytes(destAddress, dest);
|
||||
assertTrue("Data wasn't copied correctly", Arrays.equals(TEST_BYTES, dest));
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testABGetSetIndirect() throws ExecutionException {
|
||||
int sourceAddress = 2;
|
||||
int destAddress = sourceAddress + MachineState.AB_REGISTER_SIZE / MachineState.VALUE_SIZE;
|
||||
|
||||
// Address of source bytes
|
||||
dataByteBuffer.putLong(sourceAddress);
|
||||
// Address where to save bytes
|
||||
@ -111,4 +145,122 @@ public class FunctionCodeTests extends ExecutableTest {
|
||||
assertTrue(state.hadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnsignedCompare() throws ExecutionException {
|
||||
int compareABresultAddress = 0;
|
||||
int compareBAresultAddress = 1;
|
||||
int compareAAresultAddress = 2;
|
||||
|
||||
int smallerAddress = 3;
|
||||
int largerAddress = smallerAddress + MachineState.AB_REGISTER_SIZE / MachineState.VALUE_SIZE;
|
||||
|
||||
// A-B Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
// B-A Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
// A-A Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
|
||||
// Smaller value to load into A (or B)
|
||||
assertEquals(smallerAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
dataByteBuffer.putLong(0x4444444444444444L);
|
||||
dataByteBuffer.putLong(0x3333333333333333L);
|
||||
dataByteBuffer.putLong(0xF222222222222222L);
|
||||
dataByteBuffer.putLong(0xF111111111111111L);
|
||||
|
||||
// Larger value to load into A (or B)
|
||||
assertEquals(largerAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
dataByteBuffer.putLong(0xCCCCCCCCCCCCCCCCL); // negative if signed, larger if unsigned
|
||||
dataByteBuffer.putLong(0xDDDDDDDDDDDDDDDDL);
|
||||
dataByteBuffer.putLong(0x2222222222222222L);
|
||||
dataByteBuffer.putLong(0x1111111111111111L);
|
||||
|
||||
// Set A register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.SET_A_DAT.value).putLong(smallerAddress);
|
||||
// Set B register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.SET_B_DAT.value).putLong(largerAddress);
|
||||
// Compare A and B, put result into compareABresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.UNSIGNED_COMPARE_A_WITH_B.value).putInt(compareABresultAddress);
|
||||
|
||||
// Swap A and B
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
|
||||
// Compare A and B, put result into compareBAresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.UNSIGNED_COMPARE_A_WITH_B.value).putInt(compareBAresultAddress);
|
||||
|
||||
// Copy A to B
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.COPY_B_FROM_A.value);
|
||||
// Compare A and B, put result into compareBAresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.UNSIGNED_COMPARE_A_WITH_B.value).putInt(compareAAresultAddress);
|
||||
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
|
||||
assertEquals("AB compare failed", -1L, getData(compareABresultAddress));
|
||||
assertEquals("BA compare failed", +1L, getData(compareBAresultAddress));
|
||||
assertEquals("AA compare failed", 0L, getData(compareAAresultAddress));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignedCompare() throws ExecutionException {
|
||||
int compareABresultAddress = 0;
|
||||
int compareBAresultAddress = 1;
|
||||
int compareAAresultAddress = 2;
|
||||
|
||||
int smallerAddress = 3;
|
||||
int largerAddress = smallerAddress + MachineState.AB_REGISTER_SIZE / MachineState.VALUE_SIZE;
|
||||
|
||||
// A-B Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
// B-A Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
// A-A Comparison result
|
||||
dataByteBuffer.putLong(999L);
|
||||
|
||||
// Smaller value to load into A (or B)
|
||||
assertEquals(smallerAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
dataByteBuffer.putLong(0xCCCCCCCCCCCCCCCCL); // negative if signed, larger if unsigned
|
||||
dataByteBuffer.putLong(0xDDDDDDDDDDDDDDDDL);
|
||||
dataByteBuffer.putLong(0x2222222222222222L);
|
||||
dataByteBuffer.putLong(0x1111111111111111L);
|
||||
|
||||
// Larger value to load into A (or B)
|
||||
assertEquals(largerAddress * MachineState.VALUE_SIZE, dataByteBuffer.position());
|
||||
dataByteBuffer.putLong(0x4444444444444444L);
|
||||
dataByteBuffer.putLong(0x3333333333333333L);
|
||||
dataByteBuffer.putLong(0xF222222222222222L);
|
||||
dataByteBuffer.putLong(0xF111111111111111L);
|
||||
|
||||
// Set A register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.SET_A_DAT.value).putLong(smallerAddress);
|
||||
// Set B register to data segment starting at address passed by value
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_VAL.value).putShort(FunctionCode.SET_B_DAT.value).putLong(largerAddress);
|
||||
// Compare A and B, put result into compareABresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.SIGNED_COMPARE_A_WITH_B.value).putInt(compareABresultAddress);
|
||||
|
||||
// Swap A and B
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.SWAP_A_AND_B.value);
|
||||
// Compare A and B, put result into compareBAresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.SIGNED_COMPARE_A_WITH_B.value).putInt(compareBAresultAddress);
|
||||
|
||||
// Copy A to B
|
||||
codeByteBuffer.put(OpCode.EXT_FUN.value).putShort(FunctionCode.COPY_B_FROM_A.value);
|
||||
// Compare A and B, put result into compareBAresultAddress
|
||||
codeByteBuffer.put(OpCode.EXT_FUN_RET.value).putShort(FunctionCode.SIGNED_COMPARE_A_WITH_B.value).putInt(compareAAresultAddress);
|
||||
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
|
||||
assertEquals("AB compare failed", -1L, getData(compareABresultAddress));
|
||||
assertEquals("BA compare failed", +1L, getData(compareBAresultAddress));
|
||||
assertEquals("AA compare failed", 0L, getData(compareAAresultAddress));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,18 +44,29 @@ public class OpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
||||
public void testSLP_DAT() throws ExecutionException {
|
||||
int blockHeight = 12345;
|
||||
int targetBlockHeight = api.getCurrentBlockHeight() + 5;
|
||||
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).putLong(blockHeight);
|
||||
codeByteBuffer.put(OpCode.SLP_DAT.value).putInt(0);
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(targetBlockHeight);
|
||||
codeByteBuffer.put(OpCode.SLP_DAT.value).putInt(1);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
// one round only, to check AT has started sleeping
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isSleeping());
|
||||
assertFalse(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Sleep-until block height incorrect", blockHeight, getData(0));
|
||||
assertEquals("Sleep-until block height incorrect", targetBlockHeight, state.getSleepUntilHeight().intValue());
|
||||
|
||||
// continue sleeping until AT awakens
|
||||
execute(false);
|
||||
|
||||
assertEquals("Reawaken block height incorrect", targetBlockHeight, state.getCurrentBlockHeight());
|
||||
|
||||
assertFalse(state.isSleeping());
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Sleep-until block height incorrect", null, state.getSleepUntilHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -189,6 +200,35 @@ public class OpCodeTests extends ExecutableTest {
|
||||
assertEquals("Error flag not set", 1L, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSLP_VAL() throws ExecutionException {
|
||||
int blockCount = 5;
|
||||
|
||||
int startingBlockHeight = api.getCurrentBlockHeight();
|
||||
int targetBlockHeight = startingBlockHeight + blockCount;
|
||||
|
||||
codeByteBuffer.put(OpCode.SLP_VAL.value).putLong(blockCount);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
// one round only, to check AT has started sleeping
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isSleeping());
|
||||
assertFalse(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Sleep-until block height incorrect", targetBlockHeight, state.getSleepUntilHeight().intValue());
|
||||
|
||||
// continue sleeping until AT awakens
|
||||
execute(false);
|
||||
|
||||
assertEquals("Reawaken block height incorrect", targetBlockHeight, state.getCurrentBlockHeight());
|
||||
|
||||
assertFalse(state.isSleeping());
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Sleep-until block height incorrect", null, state.getSleepUntilHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPCS() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(0).put(hexToBytes("0000000011111111"));
|
||||
|
@ -146,4 +146,70 @@ public class ValueOpCodeTests extends ExecutableTest {
|
||||
assertEquals("Error flag not set", 1L, getData(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHL_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.SHL_VAL.value).putInt(2).putLong(3L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 2222L << 3, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHL_VALexcess() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.SHL_VAL.value).putInt(2).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 0L, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHR_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.SHR_VAL.value).putInt(2).putLong(3L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 2222L >>> 3, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHR_VALexcess() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.SHR_VAL.value).putInt(2).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 0L, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSHR_VALsign() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(-1L);
|
||||
codeByteBuffer.put(OpCode.SHR_VAL.value).putInt(2).putLong(3L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", -1L >>> 3, getData(2));
|
||||
assertTrue("Sign does not match", getData(2) >= 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user