diff --git a/Java/pom.xml b/Java/pom.xml index f668173..f49c4cd 100644 --- a/Java/pom.xml +++ b/Java/pom.xml @@ -4,10 +4,16 @@ 4.0.0 org.ciyam AT - 1.3.8 + 1.4.0 jar - true + UTF-8 + false + + 3.8.1 + 3.3.1 + 3.0.0-M4 + 1.64 @@ -15,20 +21,35 @@ src/test/java + org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + ${maven-compiler-plugin.version} - 11 + 11 + 11 org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M4 + ${maven-surefire-plugin.version} ${skipTests} + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + attach-javadoc + + jar + + + + diff --git a/Java/src/main/java/org/ciyam/at/API.java b/Java/src/main/java/org/ciyam/at/API.java index 7244da6..4e46990 100644 --- a/Java/src/main/java/org/ciyam/at/API.java +++ b/Java/src/main/java/org/ciyam/at/API.java @@ -76,8 +76,8 @@ public abstract class API { *

* AT should sleep so it can use next block as source of entropy. *

- * Set state.isSleeping = true before exit on first call.
- * state.steps will be zero on second call after wake-up. + * Set state.isSleeping = true before exit on first call.
+ * state.steps will be zero on second call after wake-up. *

* Returns 0xffffffffffffffff if A not valid transaction. */ @@ -102,9 +102,9 @@ public abstract class API { public abstract void messageAToB(MachineState state); /** - * Returns minutes of blocks added to 'timestamp' + * Returns minutes of blocks added to 'timestamp' *

- * minutes is converted to rough number of blocks and added to 'timestamp' to create return value. + * minutes 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); diff --git a/Java/src/main/java/org/ciyam/at/FunctionCode.java b/Java/src/main/java/org/ciyam/at/FunctionCode.java index 3c6c17a..f3fb5a9 100644 --- a/Java/src/main/java/org/ciyam/at/FunctionCode.java +++ b/Java/src/main/java/org/ciyam/at/FunctionCode.java @@ -12,19 +12,19 @@ import java.util.stream.Collectors; *

* Function codes are represented by a short. Functions can take 0 to 2 additional long values and optionally return a value too. *

- * FunctionCode instances can be obtained via the default FunctionCode.valueOf(String) or the additional FunctionCode.valueOf(int). + * FunctionCode instances can be obtained via the default FunctionCode.valueOf(String) or the additional FunctionCode.valueOf(int). *

- * Use the FunctionCode.execute method to perform the operation. + * Use the FunctionCode.execute method to perform the operation. *

* For more details, view the API Specification. * * @see FunctionCode#valueOf(int) - * @see FunctionCode#execute(FunctionData, MachineState) + * @see FunctionCode#execute(FunctionData, MachineState, short) */ public enum FunctionCode { /** * ECHO value to logger
- * 0x0001 value + * 0x0001 value */ ECHO(0x0001, 1, false) { @Override @@ -34,7 +34,7 @@ public enum FunctionCode { } }, /** - * 0x0100
+ * 0x0100
* Returns A1 value */ GET_A1(0x0100, 0, true) { @@ -44,7 +44,7 @@ public enum FunctionCode { } }, /** - * 0x0101
+ * 0x0101
* Returns A2 value */ GET_A2(0x0101, 0, true) { @@ -54,7 +54,7 @@ public enum FunctionCode { } }, /** - * 0x0102
+ * 0x0102
* Returns A3 value */ GET_A3(0x0102, 0, true) { @@ -64,7 +64,7 @@ public enum FunctionCode { } }, /** - * 0x0103
+ * 0x0103
* Returns A4 value */ GET_A4(0x0103, 0, true) { @@ -74,7 +74,7 @@ public enum FunctionCode { } }, /** - * 0x0104
+ * 0x0104
* Returns B1 value */ GET_B1(0x0104, 0, true) { @@ -84,7 +84,7 @@ public enum FunctionCode { } }, /** - * 0x0105
+ * 0x0105
* Returns B2 value */ GET_B2(0x0105, 0, true) { @@ -94,7 +94,7 @@ public enum FunctionCode { } }, /** - * 0x0106
+ * 0x0106
* Returns B3 value */ GET_B3(0x0106, 0, true) { @@ -104,7 +104,7 @@ public enum FunctionCode { } }, /** - * 0x0107
+ * 0x0107
* Returns B4 value */ GET_B4(0x0107, 0, true) { @@ -115,7 +115,7 @@ public enum FunctionCode { }, /** * Copies A into addr to addr+3
- * 0x0108 addr + * 0x0108 addr */ GET_A_IND(0x0108, 1, false) { @Override @@ -133,7 +133,7 @@ public enum FunctionCode { }, /** * Copies B into addr to addr+3
- * 0x0109 addr + * 0x0109 addr */ 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
+ * 0x010a addr
+ * To be used by calling EXT_FUN_VAL, passing destination address as value + */ + 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
+ * 0x010b addr
+ * To be used by calling EXT_FUN_VAL, passing destination address as value + */ + 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
- * 0x0110 value + * 0x0110 value */ SET_A1(0x0110, 1, false) { @Override @@ -161,7 +199,7 @@ public enum FunctionCode { }, /** * Set A2
- * 0x0111 value + * 0x0111 value */ SET_A2(0x0111, 1, false) { @Override @@ -171,7 +209,7 @@ public enum FunctionCode { }, /** * Set A3
- * 0x0112 value + * 0x0112 value */ SET_A3(0x0112, 1, false) { @Override @@ -181,7 +219,7 @@ public enum FunctionCode { }, /** * Set A4
- * 0x0113 value + * 0x0113 value */ SET_A4(0x0113, 1, false) { @Override @@ -191,7 +229,7 @@ public enum FunctionCode { }, /** * Set A1 and A2
- * 0x0114 value value + * 0x0114 value value */ SET_A1_A2(0x0114, 2, false) { @Override @@ -202,7 +240,7 @@ public enum FunctionCode { }, /** * Set A3 and A4
- * 0x0115 value value + * 0x0115 value value */ SET_A3_A4(0x0115, 2, false) { @Override @@ -213,7 +251,7 @@ public enum FunctionCode { }, /** * Set B1
- * 0x0116 value + * 0x0116 value */ SET_B1(0x0116, 1, false) { @Override @@ -223,7 +261,7 @@ public enum FunctionCode { }, /** * Set B2
- * 0x0117 value + * 0x0117 value */ SET_B2(0x0117, 1, false) { @Override @@ -233,7 +271,7 @@ public enum FunctionCode { }, /** * Set B3
- * 0x0118 value + * 0x0118 value */ SET_B3(0x0118, 1, false) { @Override @@ -243,7 +281,7 @@ public enum FunctionCode { }, /** * Set B4
- * 0x0119 value + * 0x0119 value */ SET_B4(0x0119, 1, false) { @Override @@ -253,7 +291,7 @@ public enum FunctionCode { }, /** * Set B1 and B2
- * 0x011a value value + * 0x011a value value */ SET_B1_B2(0x011a, 2, false) { @Override @@ -264,7 +302,7 @@ public enum FunctionCode { }, /** * Set B3 and B4
- * 0x011b value value + * 0x011b value value */ SET_B3_B4(0x011b, 2, false) { @Override @@ -275,7 +313,7 @@ public enum FunctionCode { }, /** * Copies addr to addr+3 into A
- * 0x011c addr + * 0x011c addr */ SET_A_IND(0x011c, 1, false) { @Override @@ -293,7 +331,7 @@ public enum FunctionCode { }, /** * Copies addr to addr+3 into B
- * 0x011d addr + * 0x011d addr */ 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
+ * 0x011e addr
+ * To be used by calling EXT_FUN_VAL, passing source address as value + */ + 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
+ * 0x011f addr
+ * To be used by calling EXT_FUN_VAL, passing source address as value + */ + 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
- * 0x0120 + * 0x0120 */ CLEAR_A(0x0120, 0, false) { @Override @@ -324,7 +400,7 @@ public enum FunctionCode { }, /** * Clear B
- * 0x0121 + * 0x0121 */ CLEAR_B(0x0121, 0, false) { @Override @@ -337,7 +413,7 @@ public enum FunctionCode { }, /** * Clear A and B
- * 0x0122 + * 0x0122 */ CLEAR_A_AND_B(0x0122, 0, false) { @Override @@ -354,7 +430,7 @@ public enum FunctionCode { }, /** * Copy A from B
- * 0x0123 + * 0x0123 */ COPY_A_FROM_B(0x0123, 0, false) { @Override @@ -367,7 +443,7 @@ public enum FunctionCode { }, /** * Copy B from A
- * 0x0124 + * 0x0124 */ COPY_B_FROM_A(0x0124, 0, false) { @Override @@ -380,7 +456,7 @@ public enum FunctionCode { }, /** * Check A is zero
- * 0x0125
+ * 0x0125
* 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
- * 0x0126
+ * 0x0126
* 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
- * 0x0127
+ * 0x0127
* 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
- * 0x0128 + * 0x0128 */ SWAP_A_AND_B(0x0128, 0, false) { @Override @@ -445,7 +521,8 @@ public enum FunctionCode { }, /** * Bitwise-OR A with B
- * 0x0129 + * 0x0129
+ * A = A | B */ OR_A_WITH_B(0x0129, 0, false) { @Override @@ -458,20 +535,22 @@ public enum FunctionCode { }, /** * Bitwise-OR B with A
- * 0x012a + * 0x012a
+ * 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
- * 0x012b + * 0x012b
+ * A = A & B */ AND_A_WITH_B(0x012b, 0, false) { @Override @@ -484,20 +563,22 @@ public enum FunctionCode { }, /** * Bitwise-AND B with A
- * 0x012c + * 0x012c
+ * 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
- * 0x012d + * 0x012d
+ * A = A ^ B */ XOR_A_WITH_B(0x012d, 0, false) { @Override @@ -510,20 +591,65 @@ public enum FunctionCode { }, /** * Bitwise-XOR B with A
- * 0x012e + * 0x012e
+ * 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
+ * 0x0130
+ * 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
+ * 0x0131
+ * 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
- * 0x0200 start-addr byte-length
+ * 0x0200 start-addr byte-length
* 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
- * 0x0201 start-addr byte-length
+ * 0x0201 start-addr byte-length
* Other MD5 hash is in B1 and B2. B3 and B4 are ignored.
* Returns 1 if true, 0 if false */ @@ -579,7 +705,7 @@ public enum FunctionCode { }, /** * RIPE-MD160 data into B
- * 0x0202 start-addr byte-length
+ * 0x0202 start-addr byte-length
* 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
- * 0x0203 start-addr byte-length
+ * 0x0203 start-addr byte-length
* Other RIPE-MD160 hash is in LSB of B1 and all of B2 and B3. B4 is ignored.
* Returns 1 if true, 0 if false */ @@ -637,7 +763,7 @@ public enum FunctionCode { }, /** * SHA256 data into B
- * 0x0204 start-addr byte-length
+ * 0x0204 start-addr byte-length
* 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
- * 0x0205 start-addr byte-length
+ * 0x0205 start-addr byte-length
* Other SHA256 hash is in B1 through B4.
* Returns 1 if true, 0 if false */ @@ -695,7 +821,7 @@ public enum FunctionCode { }, /** * HASH160 data into B
- * 0x0206 start-addr byte-length
+ * 0x0206 start-addr byte-length
* Bitcoin's HASH160 hash is equivalent to RMD160(SHA256(data)).
* 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
- * 0x0207 start-addr byte-length
+ * 0x0207 start-addr byte-length
* 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 { } }, /** - * 0x0300
+ * 0x0300
* Returns current block's "timestamp" */ GET_BLOCK_TIMESTAMP(0x0300, 0, true) { @@ -769,7 +895,7 @@ public enum FunctionCode { } }, /** - * 0x0301
+ * 0x0301
* Returns AT's creation block's "timestamp" */ GET_CREATION_TIMESTAMP(0x0301, 0, true) { @@ -779,7 +905,7 @@ public enum FunctionCode { } }, /** - * 0x0302
+ * 0x0302
* 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
- * 0x0303 + * 0x0303 */ 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
- * 0x0304 timestamp
+ * 0x0304 timestamp
* a-k-a "A_To_Tx_After_Timestamp" */ PUT_TX_AFTER_TIMESTAMP_INTO_A(0x0304, 1, false) { @@ -810,7 +936,7 @@ public enum FunctionCode { } }, /** - * 0x0305
+ * 0x0305
* Return transaction type from transaction in A
* Returns 0xffffffffffffffff in A not valid transaction */ @@ -821,7 +947,7 @@ public enum FunctionCode { } }, /** - * 0x0306
+ * 0x0306
* Return transaction amount from transaction in A
* Returns 0xffffffffffffffff in A not valid transaction */ @@ -832,7 +958,7 @@ public enum FunctionCode { } }, /** - * 0x0307
+ * 0x0307
* Return transaction timestamp from transaction in A
* Returns 0xffffffffffffffff in A not valid transaction */ @@ -844,7 +970,7 @@ public enum FunctionCode { }, /** * Generate random number using transaction in A
- * 0x0308
+ * 0x0308
* Returns 0xffffffffffffffff in A not valid transaction
* 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
- * 0x0309
+ * 0x0309
* If transaction has no 'message' then zero B
* 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
- * 0x030a + * 0x030a */ 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
- * 0x030b + * 0x030b */ PUT_CREATOR_INTO_B(0x030b, 0, false) { @Override @@ -897,7 +1023,7 @@ public enum FunctionCode { } }, /** - * 0x0400
+ * 0x0400
* Returns AT's current balance */ GET_CURRENT_BALANCE(0x0400, 0, true) { @@ -907,7 +1033,7 @@ public enum FunctionCode { } }, /** - * 0x0401
+ * 0x0401
* Returns AT's previous balance at end of last execution round
* 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
- * 0x0402 amount
+ * 0x0402 amount
* 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
- * 0x0403 + * 0x0403 */ 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
- * 0x0404
+ * 0x0404
* 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
- * 0x0405 + * 0x0405 */ MESSAGE_A_TO_ADDRESS_IN_B(0x0405, 0, false) { @Override @@ -987,7 +1113,7 @@ public enum FunctionCode { }, /** * Add minutes to timestamp
- * 0x0406 timestamp minutes
+ * 0x0406 timestamp minutes
* Return 'timestamp' based on passed 'timestamp' plus minutes */ ADD_MINUTES_TO_TIMESTAMP(0x0406, 2, true) { @@ -997,7 +1123,7 @@ public enum FunctionCode { } }, /** - * 0x0500 - 0x06ff
+ * 0x0500 - 0x06ff
* Platform-specific functions.
* These are passed through to the API */ @@ -1050,12 +1176,10 @@ public enum FunctionCode { /** * Execute Function *

- * Can modify various fields of state, including programCounter. + * Can modify various fields of state, including programCounter. *

- * Throws a subclass of ExecutionException on error, e.g. InvalidAddressException. + * Throws a subclass of ExecutionException on error, e.g. InvalidAddressException. * - * @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(); diff --git a/Java/src/main/java/org/ciyam/at/OpCode.java b/Java/src/main/java/org/ciyam/at/OpCode.java index 95fb649..ae13ece 100644 --- a/Java/src/main/java/org/ciyam/at/OpCode.java +++ b/Java/src/main/java/org/ciyam/at/OpCode.java @@ -13,19 +13,19 @@ import java.util.stream.Collectors; *

* Op codes are represented by a single byte and maybe be followed by additional arguments like data addresses, offset, immediate values, etc. *

- * OpCode instances can be obtained via the default OpCode.valueOf(String) or the additional OpCode.valueOf(int). + * OpCode instances can be obtained via the default OpCode.valueOf(String) or the additional OpCode.valueOf(int). *

- * Use the OpCode.execute method to perform the operation. + * Use the OpCode.execute method to perform the operation. *

* In the documentation for each OpCode: *

- * @addr means "store at addr" + * @addr means "store at addr" *

- * $addr means "fetch from addr" + * $addr means "fetch from addr" *

- * @($addr) means "store at address fetched from addr", i.e. indirect + * @($addr) means "store at address fetched from addr", i.e. indirect *

- * $($addr1 + $addr2) means "fetch from address fetched from addr1 plus offset fetched from addr2", i.e. indirect indexed + * $($addr1 + $addr2) means "fetch from address fetched from addr1 plus offset fetched from addr2", i.e. indirect indexed * * @see OpCode#valueOf(int) * @see OpCode#executeWithParams(MachineState, Object...) @@ -34,7 +34,7 @@ public enum OpCode { /** * No OPeration
- * 0x7f
+ * 0x7f
* (Does nothing) */ NOP(0x7f) { @@ -45,8 +45,8 @@ public enum OpCode { }, /** * SET VALue
- * 0x01 addr value
- * @addr = value + * 0x01 addr value
+ * @addr = value */ SET_VAL(0x01, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) { @Override @@ -59,8 +59,8 @@ public enum OpCode { }, /** * SET DATa
- * 0x02 addr1 addr2
- * @addr1 = $addr2 + * 0x02 addr1 addr2
+ * @addr1 = $addr2 */ SET_DAT(0x02, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -74,8 +74,8 @@ public enum OpCode { }, /** * CLeaR DATa
- * 0x03 addr
- * @addr = 0 + * 0x03 addr
+ * @addr = 0 */ CLR_DAT(0x03, OpCodeParam.DEST_ADDR) { @Override @@ -87,8 +87,8 @@ public enum OpCode { }, /** * INCrement DATa
- * 0x04 addr
- * @addr += 1 + * 0x04 addr
+ * @addr += 1 */ INC_DAT(0x04, OpCodeParam.DEST_ADDR) { @Override @@ -101,8 +101,8 @@ public enum OpCode { }, /** * DECrement DATa
- * 0x05 addr
- * @addr -= 1 + * 0x05 addr
+ * @addr -= 1 */ DEC_DAT(0x05, OpCodeParam.DEST_ADDR) { @Override @@ -115,8 +115,8 @@ public enum OpCode { }, /** * ADD DATa
- * 0x06 addr1 addr2
- * @addr1 += $addr2 + * 0x06 addr1 addr2
+ * @addr1 += $addr2 */ ADD_DAT(0x06, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -126,8 +126,8 @@ public enum OpCode { }, /** * SUBtract DATa
- * 0x07 addr1 addr2
- * @addr1 -= $addr2 + * 0x07 addr1 addr2
+ * @addr1 -= $addr2 */ SUB_DAT(0x07, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -137,8 +137,8 @@ public enum OpCode { }, /** * MULtiply DATa
- * 0x08 addr1 addr2
- * @addr1 *= $addr2 + * 0x08 addr1 addr2
+ * @addr1 *= $addr2 */ MUL_DAT(0x08, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -148,9 +148,9 @@ public enum OpCode { }, /** * DIVide DATa
- * 0x09 addr1 addr2
- * @addr1 /= $addr2
- * Can also throw IllegealOperationException if divide-by-zero attempted. + * 0x09 addr1 addr2
+ * @addr1 /= $addr2
+ * Can also throw IllegalOperationException if divide-by-zero attempted. */ DIV_DAT(0x09, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -164,8 +164,8 @@ public enum OpCode { }, /** * Binary-OR DATa
- * 0x0a addr1 addr2
- * @addr1 |= $addr2 + * 0x0a addr1 addr2
+ * @addr1 |= $addr2 */ BOR_DAT(0x0a, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -175,8 +175,8 @@ public enum OpCode { }, /** * Binary-AND DATa
- * 0x0b addr1 addr2
- * @addr1 &= $addr2 + * 0x0b addr1 addr2
+ * @addr1 &= $addr2 */ AND_DAT(0x0b, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -186,8 +186,8 @@ public enum OpCode { }, /** * EXclusive OR DATa
- * 0x0c addr1 addr2
- * @addr1 ^= $addr2 + * 0x0c addr1 addr2
+ * @addr1 ^= $addr2 */ XOR_DAT(0x0c, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -197,8 +197,8 @@ public enum OpCode { }, /** * Bitwise-NOT DATa
- * 0x0d addr
- * @addr = ~$addr + * 0x0d addr
+ * @addr = ~$addr */ NOT_DAT(0x0d, OpCodeParam.DEST_ADDR) { @Override @@ -211,8 +211,8 @@ public enum OpCode { }, /** * SET using INDirect data
- * 0x0e addr1 addr2
- * @addr1 = $($addr2) + * 0x0e addr1 addr2
+ * @addr1 = $($addr2) */ SET_IND(0x0e, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR) { @Override @@ -231,8 +231,8 @@ public enum OpCode { }, /** * SET using indirect InDeXed data
- * 0x0f addr1 addr2 addr3
- * @addr1 = $($addr2 + $addr3) + * 0x0f addr1 addr2 addr3
+ * @addr1 = $($addr2 + $addr3) */ SET_IDX(0x0f, OpCodeParam.DEST_ADDR, OpCodeParam.INDIRECT_SRC_ADDR_WITH_INDEX, OpCodeParam.INDEX) { @Override @@ -255,9 +255,9 @@ public enum OpCode { }, /** * PuSH DATa onto user stack
- * 0x10 addr
- * @--user_stack = $addr
- * Can also throw StackBoundsException if user stack exhausted. + * 0x10 addr
+ * @--user_stack = $addr
+ * Can also throw StackBoundsException if user stack exhausted. */ PSH_DAT(0x10, OpCodeParam.SRC_ADDR) { @Override @@ -278,9 +278,9 @@ public enum OpCode { }, /** * POP DATa from user stack
- * 0x11 addr
- * @addr = $user_stack++
- * Can also throw StackBoundsException if user stack empty. + * 0x11 addr
+ * @addr = $user_stack++
+ * Can also throw StackBoundsException if user stack empty. */ POP_DAT(0x11, OpCodeParam.DEST_ADDR) { @Override @@ -302,9 +302,10 @@ public enum OpCode { }, /** * JuMP into SUBroutine
- * 0x12 addr
- * @--call_stack = PC after opcode & args, PC = addr
- * Can also throw StackBoundsException if call stack exhausted. + * 0x12 addr
+ * @--call_stack = PC after opcode and args,
+ * PC = addr
+ * Can also throw StackBoundsException if call stack exhausted. */ JMP_SUB(0x12, OpCodeParam.CODE_ADDR) { @Override @@ -325,9 +326,9 @@ public enum OpCode { }, /** * RETurn from SUBroutine
- * 0x13
- * PC = $call_stack++
- * Can also throw StackBoundsException if call stack empty. + * 0x13
+ * PC = $call_stack++
+ * Can also throw StackBoundsException if call stack empty. */ RET_SUB(0x13) { @Override @@ -346,8 +347,8 @@ public enum OpCode { }, /** * Store INDirect DATa
- * 0x14 addr1 addr2
- * @($addr1) = $addr2 + * 0x14 addr1 addr2
+ * @($addr1) = $addr2 */ IND_DAT(0x14, OpCodeParam.INDIRECT_DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -366,8 +367,8 @@ public enum OpCode { }, /** * Store indirect InDeXed DATa
- * 0x15 addr1 addr2
- * @($addr1 + $addr2) = $addr3 + * 0x15 addr1 addr2
+ * @($addr1 + $addr2) = $addr3 */ IDX_DAT(0x15, OpCodeParam.INDIRECT_DEST_ADDR_WITH_INDEX, OpCodeParam.INDEX, OpCodeParam.SRC_ADDR) { @Override @@ -390,8 +391,8 @@ public enum OpCode { }, /** * MODulo DATa
- * 0x16 addr1 addr2
- * @addr1 %= $addr2 + * 0x16 addr1 addr2
+ * @addr1 %= $addr2 */ MOD_DAT(0x16, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -405,8 +406,8 @@ public enum OpCode { }, /** * SHift Left DATa
- * 0x17 addr1 addr2
- * @addr1 <<= $addr2 + * 0x17 addr1 addr2
+ * @addr1 <<= $addr2 */ 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 { }, /** * SHift Right DATa
- * 0x18 addr1 addr2
- * @addr1 >>= $addr2
+ * 0x18 addr1 addr2
+ * @addr1 >>= $addr2
* Note: new MSB bit will be zero */ SHR_DAT(0x18, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @@ -434,8 +435,8 @@ public enum OpCode { }, /** * JuMP to ADdRess
- * 0x1a addr
- * PC = addr + * 0x1a addr
+ * PC = addr */ JMP_ADR(0x1a, OpCodeParam.CODE_ADDR) { @Override @@ -447,9 +448,9 @@ public enum OpCode { }, /** * Branch if ZeRo
- * 0x1b addr offset
- * if ($addr == 0) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x1b addr offset
+ * if ($addr == 0) PC += offset
+ * Note: PC is considered to be immediately before opcode byte. */ BZR_DAT(0x1b, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override @@ -467,9 +468,9 @@ public enum OpCode { }, /** * Branch if Not Zero
- * 0x1e addr offset
- * if ($addr != 0) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x1e addr offset
+ * if ($addr != 0) PC += offset
+ * Note: PC is considered to be immediately before opcode byte. */ BNZ_DAT(0x1e, OpCodeParam.SRC_ADDR, OpCodeParam.OFFSET) { @Override @@ -487,9 +488,9 @@ public enum OpCode { }, /** * Branch if Greater-Than DATa
- * 0x1f addr1 addr2 offset
- * if ($addr1 > $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x1f addr1 addr2 offset
+ * if ($addr1 > $addr2) PC += offset
+ * Note: PC 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 { }, /** * Branch if Less-Than DATa
- * 0x20 addr1 addr2 offset
- * if ($addr1 < $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x20 addr1 addr2 offset
+ * if ($addr1 < $addr2) PC += offset
+ * Note: PC 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 { }, /** * Branch if Greater-or-Equal DATa
- * 0x21 addr1 addr2 offset
- * if ($addr1 >= $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x21 addr1 addr2 offset
+ * if ($addr1 >= $addr2) PC += offset
+ * Note: PC 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 { }, /** * Branch if Less-or-Equal DATa
- * 0x22 addr1 addr2 offset
- * if ($addr1 <= $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x22 addr1 addr2 offset
+ * if ($addr1 <= $addr2) PC += offset
+ * Note: PC 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 { }, /** * Branch if EQual DATa
- * 0x23 addr1 addr2 offset
- * if ($addr1 == $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x23 addr1 addr2 offset
+ * if ($addr1 == $addr2) PC += offset
+ * Note: PC 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 { }, /** * Branch if Not-Equal DATa
- * 0x24 addr1 addr2 offset
- * if ($addr1 != $addr2) PC += offset
- * Note: PC is considered to be immediately before opcode byte. + * 0x24 addr1 addr2 offset
+ * if ($addr1 != $addr2) PC += offset
+ * Note: PC 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 { }, /** * SLeeP until DATa
- * 0x25 addr
- * sleep until $addr, then carry on from current PC
- * Note: The value from $addr is considered to be a block height. + * 0x25 addr
+ * sleep until $addr, then carry on from current PC
+ * Note: The value from $addr 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 { }, /** * FInish if Zero DATa
- * 0x26 addr
- * if ($addr == 0) permanently stop + * 0x26 addr
+ * if ($addr == 0) permanently stop */ FIZ_DAT(0x26, OpCodeParam.SRC_ADDR) { @Override @@ -592,8 +593,8 @@ public enum OpCode { }, /** * STop if Zero DATa
- * 0x27 addr
- * if ($addr == 0) PC = PCS and stop + * 0x27 addr
+ * if ($addr == 0) PC = PCS and stop */ STZ_DAT(0x27, OpCodeParam.SRC_ADDR) { @Override @@ -610,8 +611,8 @@ public enum OpCode { }, /** * FINish IMmeDiately
- * 0x28
- * permanently stop + * 0x28
+ * permanently stop */ FIN_IMD(0x28) { @Override @@ -621,8 +622,8 @@ public enum OpCode { }, /** * SToP IMmeDiately
- * 0x29
- * stop + * 0x29
+ * stop */ STP_IMD(0x29) { @Override @@ -632,8 +633,8 @@ public enum OpCode { }, /** * SLeeP IMmeDiately
- * 0x2a
- * sleep until next block, then carry on from current PC + * 0x2a
+ * sleep until next block, then carry on from current PC */ SLP_IMD(0x2a) { @Override @@ -644,8 +645,8 @@ public enum OpCode { }, /** * Set ERRor ADdRess
- * 0x2b addr
- * PCE = addr + * 0x2b addr
+ * PCE = addr */ ERR_ADR(0x2b, OpCodeParam.CODE_ADDR) { @Override @@ -655,11 +656,26 @@ public enum OpCode { state.setOnErrorAddress(address); } }, + /** + * SLeeP for VALue blocks
+ * 0x2c value
+ * sleep until $addr, then carry on from current PC
+ * Note: The value from $addr 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); + } + }, /** * SET PCS (stop address)
- * 0x30
- * PCS = PC
- * Note: PC is considered to be immediately after this opcode byte. + * 0x30
+ * PCS = PC
+ * Note: PC is considered to be immediately after this opcode byte. */ SET_PCS(0x30) { @Override @@ -669,8 +685,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction
- * 0x32 func
- * func() + * 0x32 func
+ * func() */ EXT_FUN(0x32, OpCodeParam.FUNC) { @Override @@ -699,8 +715,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction with DATa
- * 0x33 func addr
- * func($addr) + * 0x33 func addr
+ * func($addr) */ EXT_FUN_DAT(0x33, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR) { @Override @@ -731,8 +747,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction with DATa x2
- * 0x34 func addr1 addr2
- * func($addr1, $addr2) + * 0x34 func addr1 addr2
+ * func($addr1, $addr2) */ EXT_FUN_DAT_2(0x34, OpCodeParam.FUNC, OpCodeParam.SRC_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -765,8 +781,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction expecting RETurn value
- * 0x35 func addr
- * @addr = func() + * 0x35 func addr
+ * @addr = func() */ EXT_FUN_RET(0x35, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR) { @Override @@ -801,8 +817,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction expecting RETurn value with DATa
- * 0x36 func addr1 addr2
- * @addr1 = func($addr2) + * 0x36 func addr1 addr2
+ * @addr1 = func($addr2) */ EXT_FUN_RET_DAT(0x36, OpCodeParam.FUNC, OpCodeParam.DEST_ADDR, OpCodeParam.SRC_ADDR) { @Override @@ -839,8 +855,8 @@ public enum OpCode { }, /** * Call EXTernal FUNction expecting RETurn value with DATa x2
- * 0x37 func addr1 addr2 addr3
- * @addr1 = func($addr2, $addr3) + * 0x37 func addr1 addr2 addr3
+ * @addr1 = func($addr2, $addr3) */ 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 EXTernal FUNction with VALue
+ * 0x38 func value
+ * func(value) + */ + 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); + } + }, /** * ADD VALue
- * 0x46 addr1 value
- * @addr1 += value + * 0x46 addr1 value
+ * @addr1 += value */ ADD_VAL(0x46, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) { @Override @@ -890,8 +937,8 @@ public enum OpCode { }, /** * SUBtract VALue
- * 0x07 addr1 value
- * @addr1 -= value + * 0x47 addr1 value
+ * @addr1 -= value */ SUB_VAL(0x47, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) { @Override @@ -901,8 +948,8 @@ public enum OpCode { }, /** * MULtiply VALue
- * 0x08 addr1 value
- * @addr1 *= value + * 0x48 addr1 value
+ * @addr1 *= value */ MUL_VAL(0x48, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) { @Override @@ -912,9 +959,9 @@ public enum OpCode { }, /** * DIVide VALue
- * 0x09 addr1 value
- * @addr1 /= value - * Can also throw IllegealOperationException if divide-by-zero attempted. + * 0x49 addr1 value
+ * @addr1 /= value + * Can also throw IllegalOperationException 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); } } + }, + /** + * SHift Left VALue
+ * 0x4a addr1 value
+ * @addr1 <<= value + */ + 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); + } + }, + /** + * SHift Right VALue
+ * 0x4b addr1 value
+ * @addr1 >>= value
+ * 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 *

- * Assumes codeByteBuffer.position() is already placed immediately after opcode and params.
- * state.getProgramCounter() is available to return position immediately before opcode and params. + * Assumes codeByteBuffer.position() is already placed immediately after opcode and params.
+ * state.getProgramCounter() is available to return position immediately before opcode and params. *

- * OpCode execution can modify codeByteBuffer.position() in cases like jumps, branches, etc. + * OpCode execution can modify codeByteBuffer.position() in cases like jumps, branches, etc. *

- * Can also modify userStackByteBuffer and various fields of state. + * Can also modify userStackByteBuffer and various fields of state. *

- * Throws a subclass of ExecutionException on error, e.g. InvalidAddressException. + * Throws a subclass of ExecutionException on error, e.g. InvalidAddressException. * * @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 long params, e.g. (a, b) -> a + b + * - typically a lambda operating on two long params, e.g. (a, b) → a + b + * @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 long params, e.g. (a, b) -> a + b + * - typically a lambda operating on two long params, e.g. (a, b) → a + b + * @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 long params, e.g. (a, b) -> a == b + * - typically a lambda comparing two long params, e.g. (a, b) → a == b + * @param args * @throws ExecutionException */ protected void executeBranchConditional(MachineState state, TwoValueComparator comparator, Object... args) throws ExecutionException { diff --git a/Java/src/main/java/org/ciyam/at/OpCodeParam.java b/Java/src/main/java/org/ciyam/at/OpCodeParam.java index e86d1a8..4293037 100644 --- a/Java/src/main/java/org/ciyam/at/OpCodeParam.java +++ b/Java/src/main/java/org/ciyam/at/OpCodeParam.java @@ -4,6 +4,15 @@ import java.nio.ByteBuffer; enum OpCodeParam { + /** + * Literal 64-bit long value supplied from code segment. + *

+ *

+ * Example: SET_VAL DEST_ADDR VALUE
+ * Example: SET_VAL 3 12345
+ * Data segment address 3 will be set to the value 12345. + *

+ */ 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 data segment. + *

+ *

+ * Example: SET_VAL DEST_ADDR VALUE
+ * Example: SET_VAL 3 12345
+ * Data segment address 3 will be set to the value 12345. + *

+ */ 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 data segment,
+ * using value extracted from supplied address, also in data segment. + *

+ *

+ * Example: IND_DAT INDIRECT_DEST_ADDR SRC_ADDR
+ * Example: IND_DAT 3 4
+ * If data segment address 3 contains the value 7,
+ * and data segment address 4 contains the value 12345,
+ * then data segment address 7 will be set to the value 12345. + *

+ */ 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 data segment,
+ * using value extracted from supplied address, also in data segment,
+ * and then offset by the value extracted from 2nd 'index' address, also in data segment. + *

+ *

+ * Example: IDX_DAT INDIRECT_DEST_ADDR_WITH_INDEX INDEX SRC_ADDR
+ * Example: IDX_DAT 3 4 20
+ * If data segment address 3 contains the value 7,
+ * and data segment address 4 contains the value 2,
+ * and data segment address 20 contains the value 12345,
+ * then data segment address 9 (7 + 2) will be set to the value 12345. + *

+ * @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 data segment. + *

+ *

+ * Example: SET_DAT DEST_ADDR SRC_ADDR
+ * Example: SET_DAT 2 3
+ * If data segment address 3 contains the value 12345,
+ * then data segment address 2 will be set to the value 12345. + *

+ */ 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 data segment,
+ * using value extracted from supplied address, also in data segment. + *

+ *

+ * Example: SET_IND DEST_ADDR INDIRECT_SRC_ADDR
+ * Example: SET_IND 3 4
+ * If data segment address 4 contains the value 7,
+ * and data segment address 7 contains the value 12345,
+ * then data segment address 3 will be set to the value 12345. + *

+ */ 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 data segment,
+ * using value extracted from supplied address, also in data segment,
+ * and then offset by the value extracted from 2nd 'index' address, also in data segment. + *

+ *

+ * Example: SET_IDX DEST_ADDR INDIRECT_SRC_ADDR INDEX
+ * Example: SET_IDX 3 4 5
+ * If data segment address 4 contains the value 7,
+ * and data segment address 5 contains the value 2,
+ * and data segment address 9 (7 + 2) contains the value 12345,
+ * then data segment address 3 will be set to the value 12345. + *

+ */ 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 data segment.
+ * Used with {@link OpCodeParam#INDIRECT_DEST_ADDR_WITH_INDEX} and {@link OpCodeParam#INDIRECT_DEST_ADDR_WITH_INDEX} + *

+ *

+ * Example: SET_IDX DEST_ADDR INDIRECT_SRC_ADDR INDEX
+ * Example: SET_IDX 3 4 5
+ * If data segment address 4 contains the value 7,
+ * and data segment address 5 contains the value 2,
+ * and data segment address 9 (7 + 2) contains the value 12345,
+ * then data segment address 3 will be set to the value 12345. + *

+ * @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 code segment. + *

+ *

+ * Example: JMP_ADR CODE_ADDR
+ * Example: JMP_ADR 123
+ * Jump (set PC) to code address 123. + *

+ */ 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); } }, + /** + * Byte offset from current program counter, in code segment. + *

+ *

+ * Example: BZR_DAT SRC_ADDR OFFSET
+ * Example: BZR_DAT 4 123
+ * If data segment address 4 contains the value 0,
+ * then add 123 to program counter (PC). + *

+ *

+ *

+ * Note: PC is considered to be immediately before opcode byte.
+ * Because this value is only a signed byte, maximum offsets are -128 and +127! + *

+ */ 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 16-bit short function code supplied from code segment. + *

+ *

+ * Example: EXT_FUN FUNC
+ * Example: EXT_FUN 0x0001
+ * Calls function 0x0001 (ECHO). + *

+ * @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 data segment. + *

+ *

+ * Example: SLP_DAT BLOCK_HEIGHT
+ * Example: SLP_DAT 3
+ * If data segment address 3 contains the value 12345,
+ * then the AT will sleep until block height reaches 12345. + *

+ */ 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 diff --git a/Java/src/main/java/org/ciyam/at/Timestamp.java b/Java/src/main/java/org/ciyam/at/Timestamp.java index 64cb407..c8c936d 100644 --- a/Java/src/main/java/org/ciyam/at/Timestamp.java +++ b/Java/src/main/java/org/ciyam/at/Timestamp.java @@ -4,16 +4,16 @@ package org.ciyam.at; * CIYAM-AT "Timestamp" *

* With CIYAM-ATs, "timestamp" does not mean a real timestamp but instead is an artificial timestamp that includes three parts: - *

+ *

*
    *
  • block height (32 bits)
  • *
  • blockchain ID (8 bits)
  • *
  • intra-block transaction sequence (24 bits)
  • *
* This allows up to 256 different blockchains and up to ~16million transactions per block. - *

+ *

* A blockchain ID of zero is assumed to be the 'native' blockchain. - *

+ *

* Timestamp values are not directly manipulated by AT OpCodes so endianness isn't important here. * * @see Timestamp#Timestamp(int, int, int) diff --git a/Java/src/main/java/org/ciyam/at/Utils.java b/Java/src/main/java/org/ciyam/at/Utils.java index 34e9ab6..af3f060 100644 --- a/Java/src/main/java/org/ciyam/at/Utils.java +++ b/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. *

- * Initial position is codeByteBuffer.position() but on return is incremented by 2. + * Initial position is codeByteBuffer.position() 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. *

- * Initial position is codeByteBuffer.position() but on return is incremented by 4. + * Initial position is codeByteBuffer.position() but on return is incremented by 4. *

- * Note: address is not scaled by Constants.VALUE_SIZE unlike other methods in this class. + * Note: address is not scaled by Constants.VALUE_SIZE 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. *

- * Initial position is codeByteBuffer.position() but on return is incremented by 4. + * Initial position is codeByteBuffer.position() but on return is incremented by 4. *

- * Note: address is returned scaled by Constants.VALUE_SIZE. + * Note: address is returned scaled by Constants.VALUE_SIZE. * * @param codeByteBuffer * @return int address into data segment @@ -83,9 +83,9 @@ interface Utils { /** * Returns byte offset from code bytes at current position. *

- * Initial position is codeByteBuffer.position() but on return is incremented by 1. + * Initial position is codeByteBuffer.position() but on return is incremented by 1. *

- * Note: offset is not scaled by Constants.VALUE_SIZE unlike other methods in this class. + * Note: offset is not scaled by Constants.VALUE_SIZE 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. *

- * Initial position is codeByteBuffer.position() but on return is incremented by 8. + * Initial position is codeByteBuffer.position() but on return is incremented by 8. * * @param codeByteBuffer * @return long value diff --git a/Java/src/test/java/org/ciyam/at/CompileTests.java b/Java/src/test/java/org/ciyam/at/CompileTests.java index 6a7a20c..8d994df 100644 --- a/Java/src/test/java/org/ciyam/at/CompileTests.java +++ b/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 { diff --git a/Java/src/test/java/org/ciyam/at/FunctionCodeTests.java b/Java/src/test/java/org/ciyam/at/FunctionCodeTests.java index 35894c7..94bc2b9 100644 --- a/Java/src/test/java/org/ciyam/at/FunctionCodeTests.java +++ b/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)); + } + } diff --git a/Java/src/test/java/org/ciyam/at/OpCodeTests.java b/Java/src/test/java/org/ciyam/at/OpCodeTests.java index b262484..2f6ee7d 100644 --- a/Java/src/test/java/org/ciyam/at/OpCodeTests.java +++ b/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")); diff --git a/Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java b/Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java index fc36a95..3ef84f4 100644 --- a/Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java +++ b/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); + } + }