* 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.
+ *
+ * Example: SET_VAL DEST_ADDR VALUE
+ * Example: SET_VAL 3 12345
+ * Data segment address 3 will be set to the value 12345.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Example: JMP_ADR CODE_ADDR
+ * Example: JMP_ADR 123
+ * Jump (set PC) to code address 123.
+ *
+ * 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!
+ *
+ * Example: EXT_FUN FUNC
+ * Example: EXT_FUN 0x0001
+ * Calls function 0x0001 (ECHO).
+ *
+ * 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.
+ *
* With CIYAM-ATs, "timestamp" does not mean a real timestamp but instead is an artificial timestamp that includes three parts: - *
+ *
*+ *
* 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);
+ }
+
}