/* | |
* 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 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; | |
} | |
} | |
} |