mirror of
https://github.com/Qortal/AT.git
synced 2025-01-30 02:42:14 +00:00
Add ADD/SUB/MUL/DIV_VAL opcodes + tests
This commit is contained in:
parent
09d2b5be1f
commit
8ff61a6081
@ -876,6 +876,55 @@ public enum OpCode {
|
||||
|
||||
state.dataByteBuffer.putLong(address1, functionData.returnValue);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>ADD</b> <b>VAL</b>ue<br>
|
||||
* <tt>0x46 addr1 value</tt><br>
|
||||
* <tt>@addr1 += value</tt>
|
||||
*/
|
||||
ADD_VAL(0x46, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeValueOperation(state, (a, b) -> a + b, args);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>SUB</b>tract <b>VAL</b>ue<br>
|
||||
* <tt>0x07 addr1 value</tt><br>
|
||||
* <tt>@addr1 -= value</tt>
|
||||
*/
|
||||
SUB_VAL(0x47, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeValueOperation(state, (a, b) -> a - b, args);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>MUL</b>tiply <b>VAL</b>ue<br>
|
||||
* <tt>0x08 addr1 value</tt><br>
|
||||
* <tt>@addr1 *= value</tt>
|
||||
*/
|
||||
MUL_VAL(0x48, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
executeValueOperation(state, (a, b) -> a * b, args);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* <b>DIV</b>ide <b>VAL</b>ue<br>
|
||||
* <tt>0x09 addr1 value</tt><br>
|
||||
* <tt>@addr1 /= value</tt>
|
||||
* Can also throw <tt>IllegealOperationException</tt> if divide-by-zero attempted.
|
||||
*/
|
||||
DIV_VAL(0x49, OpCodeParam.DEST_ADDR, OpCodeParam.VALUE) {
|
||||
@Override
|
||||
protected void executeWithParams(MachineState state, Object... args) throws ExecutionException {
|
||||
try {
|
||||
executeValueOperation(state, (a, b) -> a / b, args);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new IllegalOperationException("Divide by zero", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public final byte value;
|
||||
@ -1001,6 +1050,26 @@ public enum OpCode {
|
||||
state.dataByteBuffer.putLong(address1, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for ADD_VAL/SUB_VAL/MUL_VAL/DIV_VAL/MOD_VAL/SHL_VAL/SHR_VAL
|
||||
*
|
||||
* @param codeByteBuffer
|
||||
* @param dataByteBuffer
|
||||
* @param operator
|
||||
* - typically a lambda operating on two <tt>long</tt> params, e.g. <tt>(a, b) -> a + b</tt>
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
protected void executeValueOperation(MachineState state, TwoValueOperator operator, Object... args) throws ExecutionException {
|
||||
int address1 = (int) args[0];
|
||||
|
||||
long value1 = state.dataByteBuffer.getLong(address1);
|
||||
long value2 = (long) args[1];
|
||||
|
||||
long newValue = operator.apply(value1, value2);
|
||||
|
||||
state.dataByteBuffer.putLong(address1, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for BGT/BLT/BGE/BLE/BEQ/BNE
|
||||
*
|
||||
|
@ -9,30 +9,6 @@ import org.junit.Test;
|
||||
|
||||
public class DataOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
||||
public void testSET_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 2222L, getData(2));
|
||||
}
|
||||
|
||||
/** Check that trying to use an address outside data segment throws a fatal error. */
|
||||
@Test
|
||||
public void testSET_VALunbounded() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(9999).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertTrue(state.hadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSET_DAT() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
|
149
Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java
Normal file
149
Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java
Normal file
@ -0,0 +1,149 @@
|
||||
package org.ciyam.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.ciyam.at.test.ExecutableTest;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ValueOpCodeTests extends ExecutableTest {
|
||||
|
||||
@Test
|
||||
public void testSET_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 2222L, getData(2));
|
||||
}
|
||||
|
||||
/** Check that trying to use an address outside data segment throws a fatal error. */
|
||||
@Test
|
||||
public void testSET_VALunbounded() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(9999).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertTrue(state.hadFatalError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testADD_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.ADD_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", 2222L + 3333L, getData(2));
|
||||
}
|
||||
|
||||
/** Check that trying to use an address outside data segment throws a fatal error. */
|
||||
@Test
|
||||
public void testADD_VALunbounded() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.ADD_VAL.value).putInt(9999).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertTrue(state.hadFatalError());
|
||||
}
|
||||
|
||||
/** Check that adding to an unsigned long value overflows correctly. */
|
||||
@Test
|
||||
public void testADD_VALoverflow() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(0x7fffffffffffffffL);
|
||||
codeByteBuffer.put(OpCode.ADD_VAL.value).putInt(2).putLong(0x8000000000000099L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 0x0000000000000098L, getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSUB_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(3).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.SUB_VAL.value).putInt(3).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", 3333L - 2222L, getData(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMUL_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.MUL_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", (3333L * 2222L), getData(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDIV_VAL() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(2).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.DIV_VAL.value).putInt(2).putLong(2222L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Data does not match", (3333L / 2222L), getData(2));
|
||||
}
|
||||
|
||||
/** Check divide-by-zero throws fatal error because error handler not set. */
|
||||
@Test
|
||||
public void testDIV_VALzero() throws ExecutionException {
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(3).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.DIV_VAL.value).putInt(3).putLong(0);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertTrue(state.hadFatalError());
|
||||
}
|
||||
|
||||
/** Check divide-by-zero is non-fatal because error handler is set. */
|
||||
@Test
|
||||
public void testDIV_DATzeroWithOnError() throws ExecutionException {
|
||||
int errorAddr = 0x20; // adjust this manually
|
||||
|
||||
codeByteBuffer.put(OpCode.ERR_ADR.value).putInt(errorAddr);
|
||||
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(3).putLong(3333L);
|
||||
codeByteBuffer.put(OpCode.DIV_VAL.value).putInt(3).putLong(0);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
// errorAddr:
|
||||
assertEquals(errorAddr, codeByteBuffer.position());
|
||||
// Set 1 at address 1 to indicate we handled error OK
|
||||
codeByteBuffer.put(OpCode.SET_VAL.value).putInt(1).putLong(1L);
|
||||
codeByteBuffer.put(OpCode.FIN_IMD.value);
|
||||
|
||||
execute(true);
|
||||
|
||||
assertTrue(state.isFinished());
|
||||
assertFalse(state.hadFatalError());
|
||||
assertEquals("Error flag not set", 1L, getData(1));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user