Browse Source

Add ADD/SUB/MUL/DIV_VAL opcodes + tests

master
catbref 4 years ago
parent
commit
8ff61a6081
  1. 69
      Java/src/main/java/org/ciyam/at/OpCode.java
  2. 24
      Java/src/test/java/org/ciyam/at/DataOpCodeTests.java
  3. 149
      Java/src/test/java/org/ciyam/at/ValueOpCodeTests.java

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

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

24
Java/src/test/java/org/ciyam/at/DataOpCodeTests.java

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

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