Browse Source

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 &amp;, &gt; and &rarr;

Bumped version to 1.4.0
master
catbref 3 years ago
parent
commit
0a16696352
  1. 31
      Java/pom.xml
  2. 10
      Java/src/main/java/org/ciyam/at/API.java
  3. 300
      Java/src/main/java/org/ciyam/at/FunctionCode.java
  4. 368
      Java/src/main/java/org/ciyam/at/OpCode.java
  5. 143
      Java/src/main/java/org/ciyam/at/OpCodeParam.java
  6. 6
      Java/src/main/java/org/ciyam/at/Timestamp.java
  7. 16
      Java/src/main/java/org/ciyam/at/Utils.java
  8. 27
      Java/src/test/java/org/ciyam/at/CompileTests.java
  9. 152
      Java/src/test/java/org/ciyam/at/FunctionCodeTests.java
  10. 48
      Java/src/test/java/org/ciyam/at/OpCodeTests.java
  11. 66
      Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java

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>

10
Java/src/main/java/org/ciyam/at/API.java

@ -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);

300
Java/src/main/java/org/ciyam/at/FunctionCode.java

@ -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 &amp; 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 &amp; 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();

368
Java/src/main/java/org/ciyam/at/OpCode.java

@ -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 &amp;= $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 &lt;&lt;= $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 &gt;&gt;= $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 &gt; $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 &lt; $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 &gt;= $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 &lt;= $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 &lt;&lt;= 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 &gt;&gt;= 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) &rarr; 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) &rarr; 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) &rarr; a == b</code>
* @param args
* @throws ExecutionException
*/
protected void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException {

143
Java/src/main/java/org/ciyam/at/OpCodeParam.java

@ -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

6
Java/src/main/java/org/ciyam/at/Timestamp.java

@ -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)

16
Java/src/main/java/org/ciyam/at/Utils.java

@ -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

27
Java/src/test/java/org/ciyam/at/CompileTests.java

@ -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 {

152
Java/src/test/java/org/ciyam/at/FunctionCodeTests.java

@ -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));
}
}

48
Java/src/test/java/org/ciyam/at/OpCodeTests.java

@ -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"));

66
Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java

@ -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…
Cancel
Save