blob: 082078c38d191ed2024d078cff046ee789a32508 [file] [log] [blame] [raw]
/*
* BESM-6 arithmetic instructions.
*
* Copyright (c) 1997-2009, Leonid Broukhis
* Copyright (c) 2009, Serge Vakulenko
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SERGE VAKULENKO OR LEONID BROUKHIS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
* Except as contained in this notice, the name of Leonid Broukhis or
* Serge Vakulenko shall not be used in advertising or otherwise to promote
* the sale, use or other dealings in this Software without prior written
* authorization from Leonid Broukhis and Serge Vakulenko.
*/
#include <math.h>
#include "besm6_defs.h"
typedef struct {
t_uint64 mantissa;
unsigned exponent; /* offset by 64 */
} alureg_t; /* ALU register type */
static alureg_t toalu (t_value val)
{
alureg_t ret;
ret.mantissa = val & BITS41;
ret.exponent = (val >> 41) & BITS(7);
if (ret.mantissa & BIT41)
ret.mantissa |= BIT42;
return ret;
}
static int SIM_INLINE is_negative (alureg_t *word)
{
return (word->mantissa & BIT41) != 0;
}
static void negate (alureg_t *val)
{
if (is_negative (val))
val->mantissa |= BIT42;
val->mantissa = (~val->mantissa + 1) & BITS42;
if (((val->mantissa >> 1) ^ val->mantissa) & BIT41) {
val->mantissa >>= 1;
++val->exponent;
}
if (is_negative (val))
val->mantissa |= BIT42;
}
/*
* 48-й разряд -> 1, 47-й -> 2 и т.п.
* Единица 1-го разряда и нулевое слово -> 48,
* как в первоначальном варианте системы команд.
*/
int besm6_highest_bit (t_value val)
{
int n = 32, cnt = 0;
do {
t_value tmp = val;
if (tmp >>= n) {
cnt += n;
val = tmp;
}
} while (n >>= 1);
return 48 - cnt;
}
/*
* Нормализация и округление.
* Результат помещается в регистры ACC и 40-1 разряды RMR.
* 48-41 разряды RMR сохраняются.
*/
static void normalize_and_round (alureg_t acc, t_uint64 mr, int rnd_rq)
{
t_uint64 rr = 0;
int i;
t_uint64 r;
if (RAU & RAU_NORM_DISABLE)
goto chk_rnd;
i = (acc.mantissa >> 39) & 3;
if (i == 0) {
r = acc.mantissa & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
r <<= cnt;
rr = mr >> (40 - cnt);
acc.mantissa = r | rr;
mr <<= cnt;
acc.exponent -= cnt;
goto chk_zero;
}
r = mr & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
rr = mr;
r <<= cnt;
acc.mantissa = r;
mr = 0;
acc.exponent -= 40 + cnt;
goto chk_zero;
}
goto zero;
} else if (i == 3) {
r = ~acc.mantissa & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
r = (r << cnt) | ((1LL << cnt) - 1);
rr = mr >> (40 - cnt);
acc.mantissa = BIT41 | (~r & BITS40) | rr;
mr <<= cnt;
acc.exponent -= cnt;
goto chk_zero;
}
r = ~mr & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
rr = mr;
r = (r << cnt) | ((1LL << cnt) - 1);
acc.mantissa = BIT41 | (~r & BITS40);
mr = 0;
acc.exponent -= 40 + cnt;
goto chk_zero;
} else {
rr = 1;
acc.mantissa = BIT41;
mr = 0;
acc.exponent -= 80;
goto chk_zero;
}
}
chk_zero:
if (rr)
rnd_rq = 0;
chk_rnd:
if (acc.exponent & 0x8000)
goto zero;
if (! (RAU & RAU_ROUND_DISABLE) && rnd_rq)
acc.mantissa |= 1;
if (! acc.mantissa && ! (RAU & RAU_NORM_DISABLE)) {
zero: ACC = 0;
RMR &= ~BITS40;
return;
}
ACC = (t_value) (acc.exponent & BITS(7)) << 41 |
(acc.mantissa & BITS41);
RMR = (RMR & ~BITS40) | (mr & BITS40);
/* При переполнении мантисса и младшие разряды порядка верны */
if (acc.exponent & 0x80) {
if (! (RAU & RAU_OVF_DISABLE))
longjmp (cpu_halt, STOP_OVFL);
}
}
/*
* Сложение и все варианты вычитаний.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC и 40-1 разряды RMR.
*/
void besm6_add (t_value val, int negate_acc, int negate_val)
{
t_uint64 mr;
alureg_t acc, word, a1, a2;
int diff, neg, rnd_rq = 0;
acc = toalu (ACC);
word = toalu (val);
if (! negate_acc) {
if (! negate_val) {
/* Сложение */
} else {
/* Вычитание */
negate (&word);
}
} else {
if (! negate_val) {
/* Обратное вычитание */
negate (&acc);
} else {
/* Вычитание модулей */
if (is_negative (&acc))
negate (&acc);
if (! is_negative (&word))
negate (&word);
}
}
diff = acc.exponent - word.exponent;
if (diff < 0) {
diff = -diff;
a1 = acc;
a2 = word;
} else {
a1 = word;
a2 = acc;
}
mr = 0;
neg = is_negative (&a1);
if (diff == 0) {
/* Nothing to do. */
} else if (diff <= 40) {
rnd_rq = (mr = (a1.mantissa << (40 - diff)) & BITS40) != 0;
a1.mantissa = ((a1.mantissa >> diff) |
(neg ? (~0ll << (40 - diff)) : 0)) & BITS42;
} else if (diff <= 80) {
diff -= 40;
rnd_rq = a1.mantissa != 0;
mr = ((a1.mantissa >> diff) |
(neg ? (~0ll << (40 - diff)) : 0)) & BITS40;
if (neg) {
a1.mantissa = BITS42;
} else
a1.mantissa = 0;
} else {
rnd_rq = a1.mantissa != 0;
if (neg) {
mr = BITS40;
a1.mantissa = BITS42;
} else
mr = a1.mantissa = 0;
}
acc.exponent = a2.exponent;
acc.mantissa = a1.mantissa + a2.mantissa;
/* Если требуется нормализация вправо, биты 42:41
* принимают значение 01 или 10. */
switch ((acc.mantissa >> 40) & 3) {
case 2:
case 1:
rnd_rq |= acc.mantissa & 1;
mr = (mr >> 1) | ((acc.mantissa & 1) << 39);
acc.mantissa >>= 1;
++acc.exponent;
}
normalize_and_round (acc, mr, rnd_rq);
}
/*
* non-restoring division
*/
#define ABS(x) ((x) < 0 ? -x : x)
#define INT64(x) ((x) & BIT41 ? (-1LL << 40) | (x) : x)
static alureg_t nrdiv (alureg_t n, alureg_t d)
{
t_int64 nn, dd, q, res;
alureg_t quot;
/* to compensate for potential normalization to the right */
nn = INT64(n.mantissa)*2;
dd = INT64(d.mantissa)*2;
res = 0, q = BIT41;
if (ABS(nn) >= ABS(dd)) {
/* normalization to the right */
nn/=2;
n.exponent++;
}
while (q > 1) {
if (nn == 0)
break;
if (ABS(nn) < BIT40)
nn *= 2; /* magic shortcut */
else if ((nn > 0) ^ (dd > 0)) {
res -= q;
nn = 2*nn+dd;
} else {
res += q;
nn = 2*nn-dd;
}
q /= 2;
}
quot.mantissa = res/2;
quot.exponent = n.exponent-d.exponent+64;
return quot;
}
/*
* Деление.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC, содержимое RMR не определено.
*/
void besm6_divide (t_value val)
{
alureg_t acc;
alureg_t dividend, divisor;
if (((val ^ (val << 1)) & BIT41) == 0) {
/* Ненормализованный делитель: деление на ноль. */
longjmp (cpu_halt, STOP_DIVZERO);
}
dividend = toalu(ACC);
divisor = toalu(val);
acc = nrdiv(dividend, divisor);
normalize_and_round (acc, 0, 0);
}
/*
* Умножение.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC и 40-1 разряды RMR.
*/
void besm6_multiply (t_value val)
{
uint8 neg = 0;
alureg_t acc, word, a, b;
t_uint64 mr, alo, blo, ahi, bhi;
register t_uint64 l;
if (! ACC || ! val) {
/* multiplication by zero is zero */
ACC = 0;
RMR &= ~BITS40;
return;
}
acc = toalu (ACC);
word = toalu (val);
a = acc;
b = word;
mr = 0;
if (is_negative (&a)) {
neg = 1;
negate (&a);
}
if (is_negative (&b)) {
neg ^= 1;
negate (&b);
}
acc.exponent = a.exponent + b.exponent - 64;
alo = a.mantissa & BITS(20);
ahi = a.mantissa >> 20;
blo = b.mantissa & BITS(20);
bhi = b.mantissa >> 20;
l = alo * blo + ((alo * bhi + ahi * blo) << 20);
mr = l & BITS40;
l >>= 40;
acc.mantissa = l + ahi * bhi;
if (neg) {
mr = (~mr & BITS40) + 1;
acc.mantissa = ((~acc.mantissa & BITS40) + (mr >> 40))
| BIT41 | BIT42;
mr &= BITS40;
}
normalize_and_round (acc, mr, mr != 0);
}
/*
* Изменение знака числа на сумматоре ACC.
* Результат помещается в регистр ACC, RMR гасится.
*/
void besm6_change_sign (int negate_acc)
{
alureg_t acc;
acc = toalu (ACC);
if (negate_acc)
negate (&acc);
RMR = 0;
normalize_and_round (acc, 0, 0);
}
/*
* Изменение порядка числа на сумматоре ACC.
* Результат помещается в регистр ACC, RMR гасится.
*/
void besm6_add_exponent (int val)
{
alureg_t acc;
acc = toalu (ACC);
acc.exponent += val;
RMR = 0;
normalize_and_round (acc, 0, 0);
}
/*
* Сборка значения по маске.
*/
t_value besm6_pack (t_value val, t_value mask)
{
t_value result;
result = 0;
for (; mask; mask>>=1, val>>=1)
if (mask & 1) {
result >>= 1;
if (val & 1)
result |= BIT48;
}
return result;
}
/*
* Разборка значения по маске.
*/
t_value besm6_unpack (t_value val, t_value mask)
{
t_value result;
int i;
result = 0;
for (i=0; i<48; ++i) {
result <<= 1;
if (mask & BIT48) {
if (val & BIT48)
result |= 1;
val <<= 1;
}
mask <<= 1;
}
return result;
}
/*
* Подсчёт количества единиц в слове.
*/
int besm6_count_ones (t_value word)
{
int c;
for (c=0; word; ++c)
word &= word-1;
return c;
}
/*
* Сдвиг сумматора ACC с выдвижением в регистр младших разрядов RMR.
* Величина сдвига находится в диапазоне -64..63.
*/
void besm6_shift (int i)
{
RMR = 0;
if (i > 0) {
/* Сдвиг вправо. */
if (i < 48) {
RMR = (ACC << (48-i)) & BITS48;
ACC >>= i;
} else {
RMR = ACC >> (i-48);
ACC = 0;
}
} else if (i < 0) {
/* Сдвиг влево. */
i = -i;
if (i < 48) {
RMR = ACC >> (48-i);
ACC = (ACC << i) & BITS48;
} else {
RMR = (ACC << (i-48)) & BITS48;
ACC = 0;
}
}
}