simh-testsetgenerator/BESM6/besm6_arith.c
Leo Broukhis c70a1ba3df BESM6: Fixed a bug in multiplication.
Multiplying a negative number by a zero value with a non-zero exponent produced wrong results.
2017-01-07 17:11:21 -08:00

483 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 ? (0xFFFFFFFFFFFFFFFFLL << 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 && (acc.mantissa || mr)) {
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;
}
}
}