URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/forwardcom/bintools
- from Rev 52 to Rev 53
- ↔ Reverse comparison
Rev 52 → Rev 53
/emulator2.cpp
0,0 → 1,962
/**************************** emulator2.cpp ******************************** |
* Author: Agner Fog |
* date created: 2018-02-18 |
* Last modified: 2021-02-19 |
* Version: 1.11 |
* Project: Binary tools for ForwardCom instruction set |
* Description: |
* Emulator: Execution functions for jump instructions |
* |
* Copyright 2018-2021 GNU General Public License http://www.gnu.org/licenses |
*****************************************************************************/ |
|
#include "stdafx.h" |
|
static uint64_t f_jump(CThread * t) { |
// simple self-relative jump |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->running = 2; t->returnType = 0; // no return value to save |
return 0; |
} |
|
static uint64_t f_call(CThread * t) { |
// simple self-relative call |
t->callStack.push(t->ip); // push return address on call stack |
if (t->callStack.numEntries() > t->callDept) t->callDept = t->callStack.numEntries(); |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->running = 2; t->returnType = 0; // no return value to save |
return 0; |
} |
|
static uint64_t compare_jump_generic(CThread * t) { |
// compare operands, jump on contition |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
uint8_t branch = 0; // jump if 1 |
if (t->operandType < 4) { |
// all integer types |
uint64_t sizeMask = dataSizeMask[t->operandType]; // mask for data size |
uint64_t signBit = (sizeMask >> 1) + 1; // sign bit |
a.q &= sizeMask; b.q &= sizeMask; // limit to right number of bits |
// select condition |
switch (t->op & 0xE) { // mask out constant bits and invert bit |
case 0: // jump if equal |
branch = a.q == b.q; break; |
case 2: // jump if signed below |
branch = (a.q ^ signBit) < (b.q ^ signBit); break; |
case 4: // jump if signed above |
branch = (a.q ^ signBit) > (b.q ^ signBit); break; |
case 6: // jump if unsigned below |
branch = a.q < b.q; break; |
case 8: // jump if unsigned above |
branch = a.q > b.q; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
branch ^= t->op; // invert branch condition if op odd |
} |
else { |
// vector registers |
if (t->operandType != 5 && t->operandType != 6) { |
t->interrupt(INT_WRONG_PARAMETERS); // unsupported operand type |
return 0; |
} |
if ((t->op & 0xFE) == 24) { |
// fp_category test |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
bool mant0; // mantissa is zero |
bool exp0; // exponent is all zeroes |
bool exp1; // exponent is all ones |
bool sign; // sign bit |
if (t->operandType == 5) { // float |
mant0 = (a.i & 0x007FFFFF) == 0; // mantissa is zero |
exp0 = (a.i & 0x7F800000) == 0; // exponent is all zeroes |
exp1 = (a.i & 0x7F800000) == 0x7F800000; // exponent is all ones |
sign = a.i >> 31 != 0; // sign bit |
} |
else { // double |
mant0 = a.q << 12 == 0; // mantissa is zero |
exp0 = a.q << 1 >> 53 == 0; // exponent is all zeroes |
exp1 = a.q << 1 >> 53 == 0xFFFFFFFFFFFFF; // exponent is all ones |
sign = a.q >> 63 != 0; // sign bit |
} |
if (exp1) { |
if (b.b & 1) branch |= !mant0; // NAN |
if (b.b & 0x40) branch |= mant0 && sign; // -INF |
if (b.b & 0x80) branch |= mant0 && !sign; // +INF |
} |
else if (exp0) { |
if (b.b & 2) branch |= uint8_t(mant0); // +/- 0.0 |
if (b.b & 4) branch |= !mant0 && sign; // - subnormal |
if (b.b & 8) branch |= !mant0 && !sign; // + subnormal |
} |
else { |
if (b.b & 0x10) branch |= uint8_t(sign); // - normal |
if (b.b & 0x20) branch |= !sign; // + normal |
} |
branch ^= t->op; // invert branch condition if op odd |
} |
else { |
bool unordered; |
uint8_t opj = t->op; |
if (t->operandType == 5) { |
// float |
unordered = isnan_f(a.i) || isnan_f(b.i); // check if unordered |
if (unordered) { |
// a or b is NAN. Don't check the condition. Branch only if unordered version of instruction. |
branch = t->op < 32; |
} |
else { |
if ((opj & 0xE) > 5) { |
// compare absolute values |
a.i &= 0x7FFFFFFF; b.i &= 0x7FFFFFFF; |
opj -= 4; |
} |
// select condition, float |
switch (opj & 0xE) { // mask out bits for ordered/unordered and invert |
case 0: // jump if equal |
branch = a.f == b.f; break; |
case 2: // jump if below |
branch = a.f < b.f; break; |
case 4: // jump if above |
branch = a.f > b.f; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
branch ^= t->op; // invert branch condition if op odd |
} |
} |
else { |
// double |
unordered = isnan_d(a.q) || isnan_d(b.q); // check if unordered |
if (unordered) { |
// a or b is NAN. Don't check the condition. Branch only if unordered version of instruction. |
branch = t->op < 32; |
} |
else { |
if ((opj & 0xE) > 5) { |
// compare absolute values |
a.q &= 0x7FFFFFFFFFFFFFFF; b.q &= 0x7FFFFFFFFFFFFFFF; |
opj -= 4; |
} |
// select condition, float |
switch (opj & 0xE) { // mask out bits for ordered/unordered and invert |
case 0: // jump if equal |
branch = a.d == b.d; break; |
case 2: // jump if below |
branch = a.d < b.d; break; |
case 4: // jump if above |
branch = a.d > b.d; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
branch ^= t->op; // invert branch condition if op odd |
} |
} |
} |
} |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType = 0x2000; // debug output jump taken |
} |
else t->returnType = 0x1000; // debug output jump taken |
if (t->vect) t->vect = 4; // stop vector loop |
t->running = 2; // don't save result |
return 0; |
} |
|
|
static uint64_t sub_jump_generic(CThread * t) { |
// subtract and jump on some condition |
if (t->operandType > 4) { // floating point types have no add/jump |
// the opcode is used for floating point compare unordered |
return compare_jump_generic(t); |
} |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
int8_t unsignedOverflow = 0; // unsigned overflow detected |
int8_t signedOverflow = 0; // signed overflow detected |
int8_t op1 = t->op >> 1; // operation without toggle bit |
uint64_t sizeMask = dataSizeMask[t->operandType]; // mask for data size |
uint64_t signBit = (sizeMask >> 1) + 1; // sign bit |
result.q = a.q - b.q; // subtract integers |
|
// detection of signed overflow |
SNum overfl; // overflow if a and b have opposite sign and result has opposite sign of a |
overfl.q = (a.q ^ b.q) & (a.q ^ result.q); |
signedOverflow = (overfl.q & signBit) != 0; |
// detection of borrow / unsigned overflow required |
unsignedOverflow = (result.q & sizeMask) > (a.q & sizeMask); |
|
// detect branch condition |
switch (op1) { |
case 0: // jump if zero |
branch = (result.q & sizeMask) == 0; |
break; |
case 1: // jump if negative |
branch = (result.q & signBit) != 0; |
break; |
case 2: // jump if positive |
branch = (result.q & signBit) == 0 && (result.q & sizeMask) != 0; |
break; |
case 3: // jump if signed overflow |
branch = signedOverflow; |
signedOverflow = unsignedOverflow = 0; // no interrupt for this condition |
break; |
case 4: // jump if borrow (unsigned overflow) |
branch = unsignedOverflow; |
signedOverflow = unsignedOverflow = 0; // no interrupt for this condition |
break; |
default: |
err.submit(ERR_INTERNAL); |
} |
// only integer types in g.p. registers allowed |
if (t->operandType > 3) t->interrupt(INT_WRONG_PARAMETERS); |
// overflow interrupts |
//if (signedOverflow) t->interrupt(INT_OVERFL_SIGN); |
//if (unsignedOverflow) t->interrupt(INT_OVERFL_UNSIGN); |
|
// invert condition if op odd |
branch ^= t->op; |
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; // return result |
} |
|
static uint64_t add_jump_generic(CThread * t) { |
// add and jump on some condition |
if (t->operandType > 4) { // floating point types have no add/jump |
// the opcode is used for floating point compare unordered |
return compare_jump_generic(t); |
} |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
int8_t unsignedOverflow = 0; // unsigned overflow detected |
int8_t signedOverflow = 0; // signed overflow detected |
int8_t op1 = t->op >> 1; // operation without toggle bit |
uint64_t sizeMask = dataSizeMask[t->operandType]; // mask for data size |
uint64_t signBit = (sizeMask >> 1) + 1; // sign bit |
|
// add integers |
result.q = a.q + b.q; |
// detection of signed overflow required |
SNum overfl; // overflow if a and b have same sign and result has opposite sign of a |
overfl.q = ~(a.q ^ b.q) & (a.q ^ result.q); |
signedOverflow = (overfl.q & signBit) != 0; |
// detection of carry / unsigned overflow required |
unsignedOverflow = (result.q & sizeMask) < (a.q & sizeMask); |
|
// detect branch condition |
switch (op1) { |
case 8: // jump if zero |
branch = (result.q & sizeMask) == 0; |
break; |
case 9: // jump if negative |
branch = (result.q & signBit) != 0; |
break; |
case 10: // jump if positive |
branch = (result.q & signBit) == 0 && (result.q & sizeMask) != 0; |
break; |
case 11: // jump if signed overflow |
branch = signedOverflow; |
signedOverflow = unsignedOverflow = 0; // no interrupt for this condition |
break; |
case 12: // jump if borrow (unsigned overflow) |
branch = unsignedOverflow; |
signedOverflow = unsignedOverflow = 0; // no interrupts for these conditions |
break; |
default: |
err.submit(ERR_INTERNAL); |
} |
// only integer types in g.p. registers allowed for add/jump instructions |
if (t->operandType > 3 && op1 < 12) t->interrupt(INT_WRONG_PARAMETERS); |
// overflow interrupts |
//if (signedOverflow) t->interrupt(INT_OVERFL_SIGN); |
//if (unsignedOverflow) t->interrupt(INT_OVERFL_UNSIGN); |
|
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; // return result |
} |
/* |
static uint64_t shift_left_jump_zero(CThread * t) { |
// shift left and jump if zero |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = 0; |
|
switch (t->operandType) { |
case 0: // int8 |
if (b.b < 8) result.b = a.b << b.b; |
branch = result.b == 0; |
break; |
case 1: // int16 |
if (b.s < 16) result.s = a.s << b.b; |
branch = result.s == 0; |
break; |
case 2: case 5: // int32, float |
if (b.i < 32) result.i = a.i << b.b; |
branch = result.i == 0; |
break; |
case 3: // int64, double |
if (b.q < 64) result.q = a.q << b.b; |
branch = result.q == 0; |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t shift_right_u_jump_zero(CThread * t) { |
// shift right unsigned and jump if zero |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = 0; |
|
switch (t->operandType) { |
case 0: // int8 |
if (b.b < 8) result.b = a.b >> b.b; |
branch = result.b == 0; |
break; |
case 1: // int16 |
if (b.s < 16) result.s = a.s >> b.b; |
branch = result.s == 0; |
break; |
case 2: case 5: // int32, float |
if (b.i < 32) result.i = a.i >> b.b; |
branch = result.i == 0; |
break; |
case 3: // int64, double |
if (b.q < 64) result.q = a.q >> b.b; |
branch = result.q == 0; |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t rotate_jump_carry(CThread * t) { |
// rotate left and jump if the last rotated bit is 1 |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = 0; |
|
switch (t->operandType) { |
case 0: // int8 |
b.b &= 7; |
result.b = (a.b << b.b) | (a.b >> (8 - b.b)); |
branch = b.bs < 0 ? result.b >> 7 : result.b; // most or least significant bit |
break; |
case 1: // int16 |
b.b &= 15; |
result.b = (a.s << b.b) | (a.s >> (16 - b.b)); |
branch = uint8_t(b.ss < 0 ? result.s >> 15 : result.s); // most or least significant bit |
break; |
case 2: case 5: // int32, float |
b.b &= 31; |
result.i = (a.i << b.b) | (a.i >> (32 - b.b)); |
branch = uint8_t(b.is < 0 ? result.i >> 31 : result.i); // most or least significant bit |
break; |
case 3: // int64, double |
b.b &= 63; |
result.q = (a.q << b.b) | (a.q >> (64 - b.b)); |
branch = uint8_t(b.qs < 0 ? result.q >> 63 : result.q); // most or least significant bit |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} */ |
|
static uint64_t and_jump_zero(CThread * t) { |
// bitwise AND, jump if zero |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = a.q & b.q; // bitwise AND |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = result.b == 0; break; |
case 1: // int16 |
branch = result.s == 0; break; |
case 2: case 5: // int32, float |
branch = result.i == 0; break; |
case 3: case 6: // int64, double |
branch = result.q == 0; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t or_jump_zero(CThread * t) { |
// bitwise OR, jump if zero |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = a.q | b.q; // bitwise AND |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = result.b == 0; break; |
case 1: // int16 |
branch = result.s == 0; break; |
case 2: case 5: // int32, float |
branch = result.i == 0; break; |
case 3: case 6: // int64, double |
branch = result.q == 0; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t xor_jump_zero(CThread * t) { |
// bitwise XOR, jump if zero |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
result.q = a.q ^ b.q; // bitwise AND |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = result.b == 0; break; |
case 1: // int16 |
branch = result.s == 0; break; |
case 2: case 5: // int32, float |
branch = result.i == 0; break; |
case 3: case 6: // int64, double |
branch = result.q == 0; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) { // vector registers. make result scalar and stop vector loop |
t->vectorLength[t->operands[0]] = t->vectorLengthR = dataSizeTable[t->operandType]; |
} |
// invert condition if op odd |
branch ^= t->op; |
|
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t test_bit_jump_true(CThread * t) { |
// test bit number b in a, jump if zero |
// floating point operands are treated as integers with the same size |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
uint8_t branch = 0; // treat bits out of range as zero |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
if (b.b < 8) branch = a.b >> b.b; |
break; |
case 1: // int16 |
if (b.s < 16) branch = uint8_t(a.s >> b.b); |
break; |
case 2: // int32 |
case 5: // float |
if (b.i < 32) branch = uint8_t(a.i >> b.b); |
break; |
case 3: // int64 |
case 6: // double |
if (b.q < 64) branch = uint8_t(a.q >> b.b); |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) t->vect = 4; // stop vector loop |
|
// invert condition if op odd |
branch ^= t->op; |
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType = 0x2000; // debug output jump taken |
} |
else t->returnType = 0x1000; // debug output jump taken |
t->running = 2; // don't save result |
return 0; |
} |
|
static uint64_t test_bits_and(CThread * t) { |
// jump if (a & b) == b |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
int8_t branch = 0; // branch condition is inverted if op is odd |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = (a.b & b.b) == b.b; break; |
case 1: // int16 |
branch = (a.s & b.s) == b.s; break; |
case 2: // int32 |
case 5: // float |
branch = (a.i & b.i) == b.i; break; |
case 3: // int64 |
case 6: // double |
branch = (a.q & b.q) == b.q; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) t->vect = 4; // stop vector loop |
|
// invert condition if op odd |
branch ^= t->op; |
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType = 0x2000; // debug output jump taken |
} |
else t->returnType = 0x1000; // debug output jump taken |
t->running = 2; // don't save result |
return 0; |
} |
|
static uint64_t test_bits_or(CThread * t) { |
// jump if (a & b) != 0 |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
if (t->fInstr->immSize) b = t->parm[4]; // avoid conversion of b to float |
int8_t branch = 0; // branch condition is inverted if op is odd |
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = (a.b & b.b) != 0; break; |
case 1: // int16 |
branch = (a.s & b.s) != 0; break; |
case 2: // int32 |
case 5: // float |
branch = (a.i & b.i) != 0; break; |
case 3: // int64 |
case 6: // double |
branch = (a.q & b.q) != 0; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
if (t->vect) t->vect = 4; // stop vector loop |
// invert condition if op odd |
branch ^= t->op; |
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType = 0x2000; // debug output jump taken |
} |
else t->returnType = 0x1000; // debug output jump taken |
t->running = 2; // don't save result |
return 0; |
} |
|
|
static uint64_t increment_compare_jump(CThread * t) { |
// result = a + 1. Jump if condition |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t branch1 = 0; // jump if 1 |
int8_t branch2 = 0; // jump if 1 |
result.q = a.q + 1; // increment |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch1 = result.bs < b.bs; |
branch2 = result.bs > b.bs; |
break; |
case 1: // int16 |
branch1 = result.ss < b.ss; |
branch2 = result.ss > b.ss; |
break; |
case 2: // int32 |
branch1 = result.is < b.is; |
branch2 = result.is > b.is; |
break; |
case 3: // int64 |
branch1 = result.qs < b.qs; |
branch2 = result.qs > b.qs; |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
// select instruction |
if ((t->op & 0x3E) != II_INCREMENT_COMPARE_JBELOW) { |
branch1 = branch2; // increment_compare/jump_above |
} |
// invert condition if opj odd |
branch1 ^= t->op; |
// conditional branch |
if (branch1 & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
|
static uint64_t sub_maxlen_jump_pos(CThread * t) { |
// Subtract the maximum vector length (in bytes) from a general purpose register |
// and jump if the result is positive. The 8-bit immediate operand indicates the |
// operand type for which the maximum vector length is obtained. |
// The register operand must be a 64-bit general purpose register. |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t branch = 0; // jump if 1 |
// b indicates the operand type for which the maximum vector length is used. |
// to do: allow different maximum vector lengths for different operand types |
if (b.q > 7) t->interrupt(INT_WRONG_PARAMETERS); |
uint64_t maxlen = t->MaxVectorLength; |
|
result.q = a.q - maxlen; // subtract maximum length |
|
// branch condition |
switch (t->operandType) { |
case 0: // int8 |
branch = result.bs > 0; break; |
case 1: // int16 |
branch = result.ss > 0; break; |
case 2: // int32 |
branch = result.is > 0; break; |
case 3: // int64. This is the preferred operant types. |
branch = result.qs > 0; break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
// invert condition if op odd |
branch ^= t->op; |
// conditional branch |
if (branch & 1) { |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
} |
return result.q; |
} |
/* |
static uint64_t sub_jump(CThread * t) { |
// subtract integers and jump unconditionally. optional |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t unsignedOverflow = 0; // unsigned overflow detected |
int8_t signedOverflow = 0; // signed overflow detected |
result.q = a.q - b.q; // subtract integers |
// only integer types in g.p. registers allowed |
if (t->operandType > 3) t->interrupt(INT_WRONG_PARAMETERS); |
// overflow interrupts |
if (signedOverflow) t->interrupt(INT_OVERFL_SIGN); |
if (unsignedOverflow) t->interrupt(INT_OVERFL_UNSIGN); |
|
// unconditional branch |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
return result.q; // return result |
} |
|
static uint64_t add_jump(CThread * t) { |
// add integers and jump unconditionally. optional |
SNum a = t->parm[1]; // first parameter |
SNum b = t->parm[2]; // second parameter |
SNum result; // result |
int8_t unsignedOverflow = 0; // unsigned overflow detected |
int8_t signedOverflow = 0; // signed overflow detected |
result.q = a.q + b.q; // add integers |
// only integer types in g.p. registers allowed |
if (t->operandType > 3) t->interrupt(INT_WRONG_PARAMETERS); |
// overflow interrupts |
if (signedOverflow) t->interrupt(INT_OVERFL_SIGN); |
if (unsignedOverflow) t->interrupt(INT_OVERFL_UNSIGN); |
|
// unconditional branch |
t->ip += t->addrOperand * 4; // add relative offset to IP |
t->returnType |= 0x2000; // debug output jump taken |
return result.q; // return result |
}*/ |
|
static uint64_t jump_call_58(CThread * t) { |
// op = 58: jump, 59: call |
// Format 1.6 and 2.5.0: Indirect jump or call with memory operand |
// Format 1.7 C, 2.5.4, and 3.1.0: Unconditional direct jump or call |
uint64_t target = 0; // target address |
|
// different instructions for different formats |
switch (t->fInstr->format2) { |
case 0x161: case 0x252: // Indirect jump or call with memory operand |
target = t->readMemoryOperand(t->memAddress); |
break; |
case 0x172: case 0x254: // Unconditional direct jump or call with relative address |
target = t->ip + t->addrOperand * 4; // add relative offset to IP |
break; |
case 0x310: // Unconditional direct jump or call with absolute address |
target = t->addrOperand; // absolute address |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
return 0; |
} |
if (target & 3) { // misaligned jump target |
t->interrupt(INT_MISALIGNED_JUMP); |
return 0; |
} |
|
if (t->op & 1) { |
// this is a call instruction. push return address |
t->callStack.push(t->ip); // push return address on call stack |
if (t->callStack.numEntries() > t->callDept) t->callDept = t->callStack.numEntries(); |
} |
t->ip = target; // jump to new address |
t->running = 2; // don't save result |
return 0; |
} |
|
static uint64_t multiway_and_indirect(CThread * t) { |
// op = 60: jump, 61: call |
// Format 1.6 and 2.5.2: Multiway jump or call with table of relative addresses |
// Format 1.7 C: Indirect jump or call to value of register |
uint64_t target = 0; // target address |
uint64_t offset; // offset relative to reference point |
|
// different instructions for different formats |
switch (t->fInstr->format2) { |
case 0x162: case 0x252: // Indirect jump or call with memory operand |
offset = t->readMemoryOperand(t->memAddress); |
// sign extend table entry |
switch (t->operandType) { |
case 0: // int8 |
offset = (uint64_t)(int64_t)(int8_t)offset; break; |
case 1: // int16 |
offset = (uint64_t)(int64_t)(int16_t)offset; break; |
case 2: // int32 |
offset = (uint64_t)(int64_t)(int32_t)offset; break; |
case 3: // int64 |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
offset <<= 2; // scale by 4 |
target = t->parm[1].q + offset; // add reference point |
break; |
case 0x173: // Unconditional indirect jump or call to value of register |
target = t->registers[t->operands[0]]; |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
return 0; |
} |
if (target & 3) { // misaligned jump target |
t->interrupt(INT_MISALIGNED_JUMP); |
return 0; |
} |
|
if (t->op & 1) { |
// this is a call instruction. push return address |
t->callStack.push(t->ip); // push return address on call stack |
if (t->callStack.numEntries() > t->callDept) t->callDept = t->callStack.numEntries(); |
} |
t->ip = target; // jump to new address |
t->returnType = 0x2000; // debug output jump taken |
t->running = 2; // don't save result |
return 0; |
} |
|
static uint64_t return_62(CThread * t) { |
// Format 1.6: Normal function return |
// Format 1.7 C: system return |
uint64_t target = 0; // target address |
switch (t->fInstr->format2) { |
case 0x163: // return |
if (t->callStack.numEntries() == 0) { |
t->interrupt(INT_CALL_STACK); // call stack empty |
target = t->entry_point; // return to program start |
} |
else { |
target = t->callStack.pop(); // pop return address |
} |
break; |
case 0x173: // system return |
// to do! |
target = t->ip; |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
t->ip = target; // go to return address |
t->running = 2; // don't save result |
return 0; |
} |
|
static uint64_t syscall_63(CThread * t) { |
// Format 1.6: sys call. ID in register |
// Format 2.5.1, 2.5.7 and 3.1.0: sys call. ID in constants |
// Format 1.7 C: trap or filler |
// Format 2.5.5: conditional traps |
uint8_t rd = t->pInstr->a.rd; |
uint8_t rs = t->pInstr->a.rs; |
uint8_t rt = t->pInstr->a.rt; |
uint32_t mod; // module id |
uint32_t funcid; // function id |
switch (t->fInstr->format2) { |
case 0x163: // system call. ID in register |
if (t->operandType == 2) { // 16+16 bit |
mod = uint16_t(t->registers[rt] >> 16); |
funcid = uint16_t(t->registers[rt]); |
} |
else if (t->operandType == 3) { // 32+32 bit |
mod = uint32_t(t->registers[rt] >> 32); |
funcid = uint32_t(t->registers[rt]); |
} |
else {t->interrupt(INT_WRONG_PARAMETERS); return 0;} |
t->systemCall(mod, funcid, rd, rs); |
break; |
case 0x251: // system call. ID in constants |
mod = t->pInstr->s[3]; |
funcid = t->pInstr->s[2]; |
t->systemCall(mod, funcid, rd, rs); |
break; |
case 0x257: // system call. ID in constants. no registers |
mod = t->pInstr->i[1]; |
funcid = t->pInstr->s[0]; |
t->systemCall(mod, funcid, 0, 0); |
break; |
case 0x310: // system call. ID in constants |
mod = t->pInstr->i[2]; |
funcid = t->pInstr->i[1]; |
t->systemCall(mod, funcid, rd, rs); |
break; |
case 0x174: case 0x175: // trap or filler |
t->interrupt(t->pInstr->s[0]); |
break; |
case 0x255: // conditional traps |
if (t->pInstr->b[1] != 40) t->interrupt(INT_WRONG_PARAMETERS); // the only condition supported is unsigned above |
if (t->parm[1].i > t->parm[2].i) { // check condition, unsigned compare |
t->interrupt(t->pInstr->b[0]); // generate interrupt |
} |
break; |
default: |
t->interrupt(INT_WRONG_PARAMETERS); |
} |
t->running = 2; // don't save result |
t->returnType = 0; // debug output written by system function |
return 0; |
} |
|
// Jump instructions, conditional and indirect |
PFunc funcTab2[64] = { |
sub_jump_generic, sub_jump_generic, sub_jump_generic, sub_jump_generic, // 0 - 3 |
sub_jump_generic, sub_jump_generic, sub_jump_generic, sub_jump_generic, // 4 - 7 |
sub_jump_generic, sub_jump_generic, and_jump_zero, and_jump_zero, // 8 - 11 |
or_jump_zero, or_jump_zero, xor_jump_zero, xor_jump_zero, // 12 - 15 |
add_jump_generic, add_jump_generic, add_jump_generic, add_jump_generic, // 16 - 19 |
add_jump_generic, add_jump_generic, add_jump_generic, add_jump_generic, // 20 - 23 |
add_jump_generic, add_jump_generic, test_bit_jump_true, test_bit_jump_true, // 24 - 27 |
test_bits_and, test_bits_and, test_bits_or, test_bits_or, // 28 - 31 |
compare_jump_generic, compare_jump_generic, compare_jump_generic, compare_jump_generic, // 32 - 35 |
compare_jump_generic, compare_jump_generic, compare_jump_generic, compare_jump_generic, // 36 - 39 |
compare_jump_generic, compare_jump_generic, 0, 0, // 40 - 43 |
0, 0, 0, 0, // 44 - 47 |
increment_compare_jump, increment_compare_jump, increment_compare_jump, increment_compare_jump,// 48 - 51 |
sub_maxlen_jump_pos, sub_maxlen_jump_pos, 0, 0, // 52 - 55 |
0, 0, jump_call_58, jump_call_58, // 56 - 59 |
multiway_and_indirect, multiway_and_indirect, return_62, syscall_63 // 60 - 63 |
}; |
|
// jump and call instructions with 24 bit offset |
PFunc funcTab3[16] = { |
f_jump, f_jump, f_jump, f_jump, f_jump, f_jump, f_jump, f_jump, |
f_call, f_call, f_call, f_call, f_call, f_call, f_call, f_call |
}; |