diff --git a/BESM6/besm6_arith.c b/BESM6/besm6_arith.c index d29685e0..d88bdd6e 100644 --- a/BESM6/besm6_arith.c +++ b/BESM6/besm6_arith.c @@ -1,483 +1,483 @@ -/* - * 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 -#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; - } - } -} +/* + * 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 +#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; + } + } +} diff --git a/BESM6/besm6_cpu.c b/BESM6/besm6_cpu.c index 50b92fe9..4b782a1e 100644 --- a/BESM6/besm6_cpu.c +++ b/BESM6/besm6_cpu.c @@ -1,1768 +1,1768 @@ -/* - * BESM-6 CPU simulator. - * - * 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. - - * For more information about BESM-6 computer, visit sites: - * - http://www.computer-museum.ru/english/besm6.htm - * - http://mailcom.com/besm6/ - * - http://groups.google.com/group/besm6 - * - * Release notes for BESM-6/SIMH - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * 1) All addresses and data values are displayed in octal. - * 2) Memory size is 128 kwords. - * 3) Interrupt system is to be synchronized with wallclock time. - * 4) Execution times are in 1/10 of microsecond. - * 5) Magnetic drums are implemented as a single "DRUM" device. - * 6) Magnetic disks are implemented. - * 7) Magnetic tape is not implemented. - * 8) Punch tape reader is implemented, punch card reader is planned. - * 9) Card puncher is not implemented. - * 10) Displays are implemented. - * 11) Printer АЦПУ-128 is implemented. - * 12) Instruction mnemonics, register names and stop messages - * are in Russian using UTF-8 encoding. It is assumed, that - * user locale is UTF-8. - * 13) A lot of comments in Russian (UTF-8). - */ -#include "besm6_defs.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#undef SOFT_CLOCK - -t_value memory [MEMSIZE]; -uint32 PC, RK, Aex, M [NREGS], RAU, RUU; -t_value ACC, RMR, GRP, MGRP; -uint32 PRP, MPRP; -uint32 READY, READY2; /* ready flags of various devices */ - -extern const char *scp_errors[]; - -/* нехранящие биты ГРП должны сбрасываться путем обнуления тех регистров, - * сборкой которых они являются - */ -#define GRP_WIRED_BITS 01400743700000000LL - -#define PRP_WIRED_BITS 0770000 - -int corr_stack; -int redraw_panel; -uint32 delay; -jmp_buf cpu_halt; - -t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); -t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw); -t_stat cpu_reset (DEVICE *dptr); - -/* - * CPU data structures - * - * cpu_dev CPU device descriptor - * cpu_unit CPU unit descriptor - * cpu_reg CPU register list - * cpu_mod CPU modifiers list - */ - -UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MEMSIZE) }; - -REG cpu_reg[] = { -{ "СчАС", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ -{ "РК", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ -{ "Аисп", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ -{ "СМ", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ -{ "РМР", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ -{ "РАУ", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ -{ "М1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ -{ "М2", &M[2], 8, 15, 0, 1 }, -{ "М3", &M[3], 8, 15, 0, 1 }, -{ "М4", &M[4], 8, 15, 0, 1 }, -{ "М5", &M[5], 8, 15, 0, 1 }, -{ "М6", &M[6], 8, 15, 0, 1 }, -{ "М7", &M[7], 8, 15, 0, 1 }, -{ "М10", &M[010], 8, 15, 0, 1 }, -{ "М11", &M[011], 8, 15, 0, 1 }, -{ "М12", &M[012], 8, 15, 0, 1 }, -{ "М13", &M[013], 8, 15, 0, 1 }, -{ "М14", &M[014], 8, 15, 0, 1 }, -{ "М15", &M[015], 8, 15, 0, 1 }, -{ "М16", &M[016], 8, 15, 0, 1 }, -{ "М17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ -{ "М20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ -{ "М21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ -{ "М27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ -{ "М32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ -{ "М33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ -{ "М34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ -{ "М35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ -{ "РУУ", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ -{ "ГРП", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ -{ "МГРП", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ -{ "ПРП", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ -{ "МПРП", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ -{ 0 } -}; - -MTAB cpu_mod[] = { - { 0 } -}; - -DEVICE cpu_dev = { - "CPU", &cpu_unit, cpu_reg, cpu_mod, - 1, 8, 17, 1, 8, 50, - &cpu_examine, &cpu_deposit, &cpu_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG -}; - -/* - * REG: псевдоустройство, содержащее латинские синонимы всех регистров. - */ -REG reg_reg[] = { -{ "PC", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ -{ "RK", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ -{ "Aex", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ -{ "ACC", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ -{ "RMR", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ -{ "RAU", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ -{ "M1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ -{ "M2", &M[2], 8, 15, 0, 1 }, -{ "M3", &M[3], 8, 15, 0, 1 }, -{ "M4", &M[4], 8, 15, 0, 1 }, -{ "M5", &M[5], 8, 15, 0, 1 }, -{ "M6", &M[6], 8, 15, 0, 1 }, -{ "M7", &M[7], 8, 15, 0, 1 }, -{ "M10", &M[010], 8, 15, 0, 1 }, -{ "M11", &M[011], 8, 15, 0, 1 }, -{ "M12", &M[012], 8, 15, 0, 1 }, -{ "M13", &M[013], 8, 15, 0, 1 }, -{ "M14", &M[014], 8, 15, 0, 1 }, -{ "M15", &M[015], 8, 15, 0, 1 }, -{ "M16", &M[016], 8, 15, 0, 1 }, -{ "M17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ -{ "M20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ -{ "M21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ -{ "M27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ -{ "M32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ -{ "M33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ -{ "M34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ -{ "M35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ -{ "RUU", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ -{ "GRP", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ -{ "MGRP", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ -{ "PRP", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ -{ "MPRP", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ - -{ "BRZ0", &BRZ[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ1", &BRZ[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ2", &BRZ[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ3", &BRZ[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ4", &BRZ[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ5", &BRZ[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ6", &BRZ[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BRZ7", &BRZ[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "BAZ0", &BAZ[0], 8, 16, 0, 1 }, -{ "BAZ1", &BAZ[1], 8, 16, 0, 1 }, -{ "BAZ2", &BAZ[2], 8, 16, 0, 1 }, -{ "BAZ3", &BAZ[3], 8, 16, 0, 1 }, -{ "BAZ4", &BAZ[4], 8, 16, 0, 1 }, -{ "BAZ5", &BAZ[5], 8, 16, 0, 1 }, -{ "BAZ6", &BAZ[6], 8, 16, 0, 1 }, -{ "BAZ7", &BAZ[7], 8, 16, 0, 1 }, -{ "TABST", &TABST, 8, 28, 0, 1 }, -{ "RP0", &RP[0], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP1", &RP[1], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP2", &RP[2], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP3", &RP[3], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP4", &RP[4], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP5", &RP[5], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP6", &RP[6], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RP7", &RP[7], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, -{ "RZ", &RZ, 8, 32, 0, 1 }, -{ "FP1", &pult[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP2", &pult[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP3", &pult[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP4", &pult[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP5", &pult[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP6", &pult[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ "FP7", &pult[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, -{ 0 } -}; - -UNIT reg_unit = { - UDATA (NULL, 0, 8) -}; - -DEVICE reg_dev = { - "REG", ®_unit, reg_reg, NULL, - 1, 8, 1, 1, 8, 50, -}; - -/* - * SCP data structures and interface routines - * - * sim_name simulator name string - * sim_PC pointer to saved PC register descriptor - * sim_emax maximum number of words for examine/deposit - * sim_devices array of pointers to simulated devices - * sim_stop_messages array of pointers to stop messages - * sim_load binary loader - */ - -char sim_name[] = "БЭСМ-6"; - -REG *sim_PC = &cpu_reg[0]; - -int32 sim_emax = 1; /* максимальное количество слов в машинной команде */ - -DEVICE *sim_devices[] = { - &cpu_dev, - ®_dev, - &drum_dev, - &disk_dev, - &mmu_dev, - &clock_dev, - &printer_dev, - &fs_dev, - &tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */ - 0 -}; - -const char *sim_stop_messages[] = { - "Неизвестная ошибка", /* Unknown error */ - "Останов", /* STOP */ - "Точка останова", /* Emulator breakpoint */ - "Точка останова по считыванию", /* Emulator read watchpoint */ - "Точка останова по записи", /* Emulator write watchpoint */ - "Выход за пределы памяти", /* Run out end of memory */ - "Запрещенная команда", /* Invalid instruction */ - "Контроль команды", /* A data-tagged word fetched */ - "Команда в чужом листе", /* Paging error during fetch */ - "Число в чужом листе", /* Paging error during load/store */ - "Контроль числа МОЗУ", /* RAM parity error */ - "Контроль числа БРЗ", /* Write cache parity error */ - "Переполнение АУ", /* Arith. overflow */ - "Деление на нуль", /* Division by zero or denorm */ - "Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */ - "Чтение неформатированного барабана", /* Reading unformatted drum */ - "Чтение неформатированного диска", /* Reading unformatted disk */ - "Останов по КРА", /* Hardware breakpoint */ - "Останов по считыванию", /* Load watchpoint */ - "Останов по записи", /* Store watchpoint */ - "Не реализовано", /* Unimplemented I/O or special reg. access */ -}; - -/* - * Memory examine - */ -t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) -{ - if (addr >= MEMSIZE) - return SCPE_NXM; - if (vptr) { - if (addr < 010) - *vptr = pult [addr]; - else - *vptr = memory [addr]; - } - return SCPE_OK; -} - -/* - * Memory deposit - */ -t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw) -{ - if (addr >= MEMSIZE) - return SCPE_NXM; - if (addr < 010) - pult [addr] = SET_CONVOL (val, CONVOL_INSN); - else - memory [addr] = SET_CONVOL (val, CONVOL_INSN); - return SCPE_OK; -} - -/* - * Функция вызывается каждые 4 миллисекунды реального времени. - */ -static void cpu_sigalarm (int signum) -{ - static unsigned counter; - - ++counter; - -#ifndef SOFT_CLOCK - /* В 9-й части частота таймера 250 Гц (4 мс). */ - GRP |= GRP_TIMER; - - /* Медленный таймер: должен быть 16 Гц. - * Но от него почему-то зависит вывод на терминалы, - * поэтому ускорим. */ - if ((counter & 3) == 0) { - GRP |= GRP_SLOW_CLK; - } -#endif - - /* Перерисовка панели каждые 64 миллисекунды. */ - if ((counter & 15) == 0) { - redraw_panel = 1; - } -} - -/* - * Reset routine - */ -t_stat cpu_reset (DEVICE *dptr) -{ - int i; - - ACC = 0; - RMR = 0; - RAU = 0; - RUU = RUU_EXTRACODE | RUU_AVOST_DISABLE; - for (i=0; i 0xxxxxxx - * 00000xxx.xxyyyyyy -> 110xxxxx, 10yyyyyy - * xxxxyyyy.yyzzzzzz -> 1110xxxx, 10yyyyyy, 10zzzzzz - */ -void -utf8_putc (unsigned ch, FILE *fout) -{ - if (ch < 0x80) { - putc (ch, fout); - return; - } - if (ch < 0x800) { - putc (ch >> 6 | 0xc0, fout); - putc ((ch & 0x3f) | 0x80, fout); - return; - } - putc (ch >> 12 | 0xe0, fout); - putc (((ch >> 6) & 0x3f) | 0x80, fout); - putc ((ch & 0x3f) | 0x80, fout); -} - -/* - * *call ОКНО - так называлась служебная подпрограмма в мониторной - * системе "Дубна", которая печатала полное состояние всех регистров. - */ -void besm6_okno (const char *message) -{ - besm6_log_cont ("_%%%%%% %s: ", message); - if (sim_log) - besm6_fprint_cmd (sim_log, RK); - besm6_log ("_"); - - /* СчАС, системные индекс-регистры 020-035. */ - besm6_log ("_ СчАС:%05o 20:%05o 21:%05o 27:%05o 32:%05o 33:%05o 34:%05o 35:%05o", - PC, M[020], M[021], M[027], M[032], M[033], M[034], M[035]); - /* Индекс-регистры 1-7. */ - besm6_log ("_ 1:%05o 2:%05o 3:%05o 4:%05o 5:%05o 6:%05o 7:%05o", - M[1], M[2], M[3], M[4], M[5], M[6], M[7]); - /* Индекс-регистры 010-017. */ - besm6_log ("_ 10:%05o 11:%05o 12:%05o 13:%05o 14:%05o 15:%05o 16:%05o 17:%05o", - M[010], M[011], M[012], M[013], M[014], M[015], M[016], M[017]); - /* Сумматор, РМР, режимы АУ и УУ. */ - besm6_log ("_ СМ:%04o %04o %04o %04o РМР:%04o %04o %04o %04o РАУ:%02o РУУ:%03o", - (int) (ACC >> 36) & BITS(12), (int) (ACC >> 24) & BITS(12), - (int) (ACC >> 12) & BITS(12), (int) ACC & BITS(12), - (int) (RMR >> 36) & BITS(12), (int) (RMR >> 24) & BITS(12), - (int) (RMR >> 12) & BITS(12), (int) RMR & BITS(12), - RAU, RUU); -} - -/* - * Команда "рег" - */ -static void cmd_002 () -{ -#if 0 - besm6_debug ("*** рег %03o", Aex & 0377); -#endif - switch (Aex & 0377) { - case 0 ... 7: - /* Запись в БРЗ */ - mmu_setcache (Aex & 7, ACC); - break; - case 020 ... 027: - /* Запись в регистры приписки */ - mmu_setrp (Aex & 7, ACC); - break; - case 030 ... 033: - /* Запись в регистры защиты */ - mmu_setprotection (Aex & 3, ACC); - break; - case 036: - /* Запись в маску главного регистра прерываний */ - MGRP = ACC; - break; - case 037: - /* Гашение главного регистра прерываний */ - /* нехранящие биты невозможно погасить */ - GRP &= ACC | GRP_WIRED_BITS; - break; - case 0100 ... 0137: - /* Бит 1: управление блокировкой режима останова БРО. - * Биты 2 и 3 - признаки формирования контрольных - * разрядов (ПКП и ПКЛ). */ - if (Aex & 1) - RUU |= RUU_AVOST_DISABLE; - else - RUU &= ~RUU_AVOST_DISABLE; - if (Aex & 2) - RUU |= RUU_CONVOL_RIGHT; - else - RUU &= ~RUU_CONVOL_RIGHT; - if (Aex & 4) - RUU |= RUU_CONVOL_LEFT; - else - RUU &= ~RUU_CONVOL_LEFT; - break; - case 0140 ... 0177: - /* TODO: управление блокировкой схемы - * автоматического запуска */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0200 ... 0207: - /* Чтение БРЗ */ - ACC = mmu_getcache (Aex & 7); - break; - case 0237: - /* Чтение главного регистра прерываний */ - ACC = GRP; - break; - default: - /* Неиспользуемые адреса */ - besm6_debug ("*** %05o%s: РЕГ %o - неправильный адрес спец.регистра", - PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex); - break; - } -} - -/* - * Команда "увв" - */ -static void cmd_033 () -{ -#if 0 - besm6_debug ("*** увв %04o, СМ[24:1]=%08o", - Aex & 04177, (uint32) ACC & BITS(24)); -#endif - switch (Aex & 04177) { - case 0: - /* Точно неизвестно, что это такое, но драйвер МД - * иногда выдает команду "увв 0". */ - break; - case 1 ... 2: - /* Управление обменом с магнитными барабанами */ - drum (Aex - 1, (uint32) ACC); - break; - case 3 ... 4: - /* Передача управляющего слова для обмена - * с магнитными дисками */ - disk_io (Aex - 3, (uint32) ACC); - break; - case 5 ... 7: - /* TODO: управление обменом с магнитными лентами */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 010 ... 011: - /* управление устройствами ввода с перфоленты */ - fs_control (Aex - 010, (uint32) (ACC & 07)); - break; - case 012 ... 013: - /* TODO: управление устройствами ввода с перфоленты по запаянной программе */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 014 ... 015: - /* Управление АЦПУ */ - printer_control (Aex - 014, (uint32) (ACC & 017)); - break; - case 023 ... 024: - /* Управление обменом с магнитными дисками */ - disk_ctl (Aex - 023, (uint32) ACC); - break; - case 030: - /* Гашение ПРП */ -/* besm6_debug(">>> гашение ПРП");*/ - PRP &= ACC | PRP_WIRED_BITS; - break; - case 031: - /* Имитация сигналов прерывания ГРП */ - /*besm6_debug ("*** %05o%s: имитация прерываний ГРП %016llo", - PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", ACC << 24);*/ - GRP |= (ACC & BITS(24)) << 24; - break; - case 032 ... 033: - /* TODO: имитация сигналов из КМБ в КВУ */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 034: - /* Запись в МПРП */ -/* besm6_debug(">>> запись в МПРП");*/ - MPRP = ACC & 077777777; - break; - case 035: - /* TODO: управление режимом имитации обмена - * с МБ и МЛ, имитация обмена */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 040 ... 057: - /* Управление молоточками АЦПУ */ - printer_hammer (Aex >= 050, Aex & 7, (uint32) (ACC & BITS(16))); - break; - case 0100 ... 0137: - /* Управление лентопротяжными механизмами - * и гашение разрядов регистров признаков - * окончания подвода зоны. Игнорируем. */ - break; - case 0140: - /* Запись в регистр телеграфных каналов */ - tty_send ((uint32) ACC & BITS(24)); - break; - case 0141: - /* TODO: управление разметкой магнитной ленты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0142: - /* TODO: имитация сигналов прерывания ПРП */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0147: - /* Запись в регистр управления электропитанием, */ - /* не оказывает видимого эффекта на выполнение */ - break; - case 0150 ... 0151: - /* TODO: управление вводом с перфокарт */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0153: - /* гашение аппаратуры сопряжения с терминалами */ -/* besm6_debug(">>> гашение АС: %08o", (uint32) ACC & BITS(24));*/ - break; - case 0154 ... 0155: - /* TODO: управление выводом на перфокарты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0160 ... 0167: - /* TODO: управление электромагнитами пробивки перфокарт */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0170 ... 0171: - /* TODO: пробивка строки на перфоленте */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 0174 ... 0175: - /* Выдача кода в пульт оператора */ - consul_print (Aex & 1, (uint32) ACC & BITS(8)); - break; - case 0177: - /* управление табло ГПВЦ СО АН СССР */ -/* besm6_debug(">>> ТАБЛО: %08o", (uint32) ACC & BITS(24));*/ - break; - case 04001 ... 04002: - /* TODO: считывание слога в режиме имитации обмена */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04003 ... 04004: - /* Запрос статуса контроллера магнитных дисков */ - ACC = disk_state (Aex - 04003); - break; - case 04006: - /* TODO: считывание строки с устройства ввода - * с перфоленты в запаянной программе */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04007: - /* TODO: опрос синхроимпульса ненулевой строки - * в запаянной программе ввода с перфоленты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04014 ... 04015: - /* считывание строки с устройства ввода с перфоленты */ - ACC = fs_read (Aex - 04014); - break; - case 04016 ... 04017: - /* TODO: считывание строки с устройства - * ввода с перфоленты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04020 ... 04023: - /* TODO: считывание слога в режиме имитации - * внешнего обмена */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04030: - /* Чтение старшей половины ПРП */ - ACC = PRP & 077770000; - break; - case 04031: - /* Опрос сигналов готовности (АЦПУ и пр.) */ -/* besm6_debug("Reading READY");*/ - ACC = READY; - break; - case 04034: - /* Чтение младшей половины ПРП */ - ACC = (PRP & 07777) | 0377; - break; - case 04035: - /* Опрос триггера ОШМi - наличие ошибок при внешнем обмене. */ - ACC = drum_errors() | disk_errors(); - break; - case 04100: - /* Опрос телеграфных каналов связи */ - ACC = tty_query (); - break; - case 04102: - /* Опрос сигналов готовности перфокарт и перфолент */ -/* besm6_debug("Reading punchcard/punchtape READY @%05o", PC);*/ - ACC = READY2; - break; - case 04103 ... 04106: - /* Опрос состояния лентопротяжных механизмов. - * Все устройства не готовы. */ - ACC = BITS(24); - break; - case 04107: - /* TODO: опрос схемы контроля записи на МЛ */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04115: - /* Неизвестное обращение. ДИСПАК выдаёт эту команду - * группами по 8 штук каждые несколько секунд. */ - ACC = 0; - break; - case 04140 ... 04157: - /* TODO: считывание строки перфокарты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04160 ... 04167: - /* TODO: контрольное считывание строки перфокарты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04170 ... 04173: - /* TODO: считывание контрольного кода - * строки перфоленты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); - break; - case 04174 ... 04175: - /* Считывание кода с пульта оператора */ - ACC = consul_read (Aex & 1); - break; - case 04177: - /* чтение табло ГПВЦ СО АН СССР */ - ACC = 0; - break; - default: - /* Неиспользуемые адреса */ -/* if (sim_deb && cpu_dev.dctrl)*/ - besm6_debug ("*** %05o%s: УВВ %o - неправильный адрес ввода-вывода", - PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex); - ACC = 0; - break; - } -} - -void check_initial_setup () -{ - const int MGRP_COPY = 01455; /* OS version specific? */ - const int TAKEN = 0442; /* fixed? */ - const int YEAR = 0221; /* fixed */ - - /* 47 р. яч. ЗАНЯТА - разр. приказы вообще */ - const t_value SETUP_REQS_ENABLED = 1LL << 46; - - /* 7 р. яч. ЗАНЯТА - разр любые приказы */ - const t_value ALL_REQS_ENABLED = 1 << 6; - - if ((memory[TAKEN] & SETUP_REQS_ENABLED) == 0 || - (memory[TAKEN] & ALL_REQS_ENABLED) != 0 || - (MGRP & GRP_PANEL_REQ) == 0) { - /* Слишком рано, или уже не надо, или невовремя */ - return; - } - - /* Выдаем приказы оператора СМЕ и ВРЕ, - * а дату корректируем непосредственно в памяти. - */ - /* Номер смены в 22-24 рр. МГРП: если еще не установлен, установить */ - if (((memory[MGRP_COPY] >> 21) & 3) == 0) { - /* приказ СМЕ: ТР6 = 010, ТР4 = 1, 22-24 р ТР5 - #смены */ - pult[6] = 010; - pult[4] = 1; - pult[5] = 1 << 21; - GRP |= GRP_PANEL_REQ; - } else { - /* Яч. ГОД обновляем самостоятельно */ - time_t t; - t_value date; - time(&t); - struct tm * d; - d = localtime(&t); - ++d->tm_mon; - date = (t_value) (d->tm_mday / 10) << 33 | - (t_value) (d->tm_mday % 10) << 29 | - (d->tm_mon / 10) << 28 | - (d->tm_mon % 10) << 24 | - (d->tm_year % 10) << 20 | - ((d->tm_year / 10) % 10) << 16 | - (memory[YEAR] & 7); - memory[YEAR] = SET_CONVOL (date, CONVOL_NUMBER); - /* приказ ВРЕ: ТР6 = 016, ТР5 = 9-14 р.-часы, 1-8 р.-минуты */ - pult[6] = 016; - pult[4] = 0; - pult[5] = (d->tm_hour / 10) << 12 | - (d->tm_hour % 10) << 8 | - (d->tm_min / 10) << 4 | - (d->tm_min % 10); - GRP |= GRP_PANEL_REQ; - } -} - -/* - * Execute one instruction, placed on address PC:RUU_RIGHT_INSTR. - * Increment delay. When stopped, perform a longjmp to cpu_halt, - * sending a stop code. - */ -void cpu_one_inst () -{ - int reg, opcode, addr, nextpc, next_mod; - - corr_stack = 0; - t_value word = mmu_fetch (PC); - if (RUU & RUU_RIGHT_INSTR) - RK = word; /* get right instruction */ - else - RK = word >> 24; /* get left instruction */ - - RK &= BITS(24); - - reg = RK >> 20; - if (RK & BBIT(20)) { - addr = RK & BITS(15); - opcode = (RK >> 12) & 0370; - } else { - addr = RK & BITS(12); - if (RK & BBIT(19)) - addr |= 070000; - opcode = (RK >> 12) & 077; - } - - if (sim_deb && cpu_dev.dctrl) { - fprintf (sim_deb, "*** %05o%s: ", PC, - (RUU & RUU_RIGHT_INSTR) ? "п" : "л"); - besm6_fprint_cmd (sim_deb, RK); - fprintf (sim_deb, "\tСМ="); - fprint_sym (sim_deb, 0, &ACC, 0, 0); - fprintf (sim_deb, "\tРАУ=%02o", RAU); - if (reg) - fprintf (sim_deb, "\tМ[%o]=%05o", reg, M[reg]); - fprintf (sim_deb, "\n"); - } - nextpc = ADDR(PC + 1); - if (RUU & RUU_RIGHT_INSTR) { - PC += 1; /* increment PC */ - RUU &= ~RUU_RIGHT_INSTR; - } else { - mmu_prefetch(nextpc | (IS_SUPERVISOR(RUU) ? BBIT(16) : 0), 0); - RUU |= RUU_RIGHT_INSTR; - } - - if (RUU & RUU_MOD_RK) { - addr = ADDR (addr + M[MOD]); - } - next_mod = 0; - delay = 0; - - switch (opcode) { - case 000: /* зп, atx */ - Aex = ADDR (addr + M[reg]); - mmu_store (Aex, ACC); - if (! addr && reg == 017) - M[017] = ADDR (M[017] + 1); - delay = MEAN_TIME (3, 3); - break; - case 001: /* зпм, stx */ - Aex = ADDR (addr + M[reg]); - mmu_store (Aex, ACC); - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - ACC = mmu_load (M[017]); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (6, 6); - break; - case 002: /* рег, mod */ - Aex = ADDR (addr + M[reg]); - if (! IS_SUPERVISOR (RUU)) - longjmp (cpu_halt, STOP_BADCMD); - cmd_002 (); - /* Режим АУ - логический, если операция была "чтение" */ - if (Aex & 0200) - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 3); - break; - case 003: /* счм, xts */ - mmu_store (M[017], ACC); - M[017] = ADDR (M[017] + 1); - corr_stack = -1; - Aex = ADDR (addr + M[reg]); - ACC = mmu_load (Aex); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (6, 6); - break; - case 004: /* сл, a+x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add (mmu_load (Aex), 0, 0); - RAU = SET_ADDITIVE (RAU); - delay = MEAN_TIME (3, 11); - break; - case 005: /* вч, a-x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add (mmu_load (Aex), 0, 1); - RAU = SET_ADDITIVE (RAU); - delay = MEAN_TIME (3, 11); - break; - case 006: /* вчоб, x-a */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add (mmu_load (Aex), 1, 0); - RAU = SET_ADDITIVE (RAU); - delay = MEAN_TIME (3, 11); - break; - case 007: /* вчаб, amx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add (mmu_load (Aex), 1, 1); - RAU = SET_ADDITIVE (RAU); - delay = MEAN_TIME (3, 11); - break; - case 010: /* сч, xta */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC = mmu_load (Aex); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 3); - break; - case 011: /* и, aax */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC &= mmu_load (Aex); - RMR = 0; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 4); - break; - case 012: /* нтж, aex */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - RMR = ACC; - ACC ^= mmu_load (Aex); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 3); - break; - case 013: /* слц, arx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC += mmu_load (Aex); - if (ACC & BIT49) - ACC = (ACC + 1) & BITS48; - RMR = 0; - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 6); - break; - case 014: /* знак, avx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_change_sign (mmu_load (Aex) >> 40 & 1); - RAU = SET_ADDITIVE (RAU); - delay = MEAN_TIME (3, 5); - break; - case 015: /* или, aox */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC |= mmu_load (Aex); - RMR = 0; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 4); - break; - case 016: /* дел, a/x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_divide (mmu_load (Aex)); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 50); - break; - case 017: /* умн, a*x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_multiply (mmu_load (Aex)); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 18); - break; - case 020: /* сбр, apx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC = besm6_pack (ACC, mmu_load (Aex)); - RMR = 0; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 53); - break; - case 021: /* рзб, aux */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC = besm6_unpack (ACC, mmu_load (Aex)); - RMR = 0; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 53); - break; - case 022: /* чед, acx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - ACC = besm6_count_ones (ACC) + mmu_load (Aex); - if (ACC & BIT49) - ACC = (ACC + 1) & BITS48; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 56); - break; - case 023: /* нед, anx */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - if (ACC) { - int n = besm6_highest_bit (ACC); - - /* "Остаток" сумматора, исключая бит, - * номер которого определен, помещается в РМР, - * начиная со старшего бита РМР. */ - besm6_shift (48 - n); - - /* Циклическое сложение номера со словом по Аисп. */ - ACC = n + mmu_load (Aex); - if (ACC & BIT49) - ACC = (ACC + 1) & BITS48; - } else { - RMR = 0; - ACC = mmu_load (Aex); - } - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 32); - break; - case 024: /* слп, e+x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add_exponent ((mmu_load (Aex) >> 41) - 64); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 5); - break; - case 025: /* вчп, e-x */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - besm6_add_exponent (64 - (mmu_load (Aex) >> 41)); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 5); - break; - case 026: { /* сд, asx */ - int n; - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - n = (mmu_load (Aex) >> 41) - 64; - besm6_shift (n); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 4 + abs (n)); - break; - } - case 027: /* рж, xtr */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - RAU = (mmu_load (Aex) >> 41) & 077; - delay = MEAN_TIME (3, 3); - break; - case 030: /* счрж, rte */ - Aex = ADDR (addr + M[reg]); - ACC = (t_value) (RAU & Aex & 0177) << 41; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 3); - break; - case 031: /* счмр, yta */ - Aex = ADDR (addr + M[reg]); - if (IS_LOGICAL (RAU)) { - ACC = RMR; - } else { - t_value x = RMR; - ACC = (ACC & ~BITS41) | (RMR & BITS40); - besm6_add_exponent ((Aex & 0177) - 64); - RMR = x; - } - delay = MEAN_TIME (3, 5); - break; - case 032: /* э32, ext */ - /* Fall through... */ - case 033: /* увв, ext */ - Aex = ADDR (addr + M[reg]); - if (! IS_SUPERVISOR (RUU)) - longjmp (cpu_halt, STOP_BADCMD); - cmd_033 (); - /* Режим АУ - логический, если операция была "чтение" */ - if (Aex & 04000) - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 8); - break; - case 034: /* слпа, e+n */ - Aex = ADDR (addr + M[reg]); - besm6_add_exponent ((Aex & 0177) - 64); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 5); - break; - case 035: /* вчпа, e-n */ - Aex = ADDR (addr + M[reg]); - besm6_add_exponent (64 - (Aex & 0177)); - RAU = SET_MULTIPLICATIVE (RAU); - delay = MEAN_TIME (3, 5); - break; - case 036: { /* сда, asn */ - int n; - Aex = ADDR (addr + M[reg]); - n = (Aex & 0177) - 64; - besm6_shift (n); - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (3, 4 + abs (n)); - break; - } - case 037: /* ржа, ntr */ - Aex = ADDR (addr + M[reg]); - RAU = Aex & 077; - delay = MEAN_TIME (3, 3); - break; - case 040: /* уи, ati */ - Aex = ADDR (addr + M[reg]); - if (IS_SUPERVISOR (RUU)) { - int reg = Aex & 037; - M[reg] = ADDR (ACC); - /* breakpoint/watchpoint regs will match physical - * or virtual addresses depending on the current - * mapping mode. - */ - if ((M[PSW] & PSW_MMAP_DISABLE) && - (reg == IBP || reg == DWP)) - M[reg] |= BBIT(16); - - } else - M[Aex & 017] = ADDR (ACC); - M[0] = 0; - delay = MEAN_TIME (14, 3); - break; - case 041: { /* уим, sti */ - unsigned rg, ad; - - Aex = ADDR (addr + M[reg]); - rg = Aex & (IS_SUPERVISOR (RUU) ? 037 : 017); - ad = ADDR (ACC); - if (rg != 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - ACC = mmu_load (rg != 017 ? M[017] : ad); - M[rg] = ad; - if ((M[PSW] & PSW_MMAP_DISABLE) && (rg == IBP || rg == DWP)) - M[rg] |= BBIT(16); - M[0] = 0; - RAU = SET_LOGICAL (RAU); - delay = MEAN_TIME (14, 3); - break; - } - case 042: /* счи, ita */ - delay = MEAN_TIME (6, 3); -load_modifier: Aex = ADDR (addr + M[reg]); - ACC = ADDR(M[Aex & (IS_SUPERVISOR (RUU) ? 037 : 017)]); - RAU = SET_LOGICAL (RAU); - break; - case 043: /* счим, its */ - mmu_store (M[017], ACC); - M[017] = ADDR (M[017] + 1); - delay = MEAN_TIME (9, 6); - goto load_modifier; - case 044: /* уии, mtj */ - Aex = addr; - if (IS_SUPERVISOR (RUU)) { -transfer_modifier: M[Aex & 037] = M[reg]; - if ((M[PSW] & PSW_MMAP_DISABLE) && - ((Aex & 037) == IBP || (Aex & 037) == DWP)) - M[Aex & 037] |= BBIT(16); - - } else - M[Aex & 017] = M[reg]; - M[0] = 0; - delay = 6; - break; - case 045: /* сли, j+m */ - Aex = addr; - if ((Aex & 020) && IS_SUPERVISOR (RUU)) - goto transfer_modifier; - M[Aex & 017] = ADDR (M[Aex & 017] + M[reg]); - M[0] = 0; - delay = 6; - break; - case 046: /* э46, x46 */ - Aex = addr; - if (! IS_SUPERVISOR (RUU)) - longjmp (cpu_halt, STOP_BADCMD); - M[Aex & 017] = ADDR (Aex); - M[0] = 0; - delay = 6; - break; - case 047: /* э47, x47 */ - Aex = addr; - if (! IS_SUPERVISOR (RUU)) - longjmp (cpu_halt, STOP_BADCMD); - M[Aex & 017] = ADDR (M[Aex & 017] + Aex); - M[0] = 0; - delay = 6; - break; - case 050 ... 077: /* э50...э77 */ - case 0200: /* э20 */ - case 0210: /* э21 */ - stop_as_extracode: - Aex = ADDR (addr + M[reg]); - if (! sim_deb && sim_log && cpu_dev.dctrl && opcode != 075) { - /* Если включен console log и cpu debug, - * но нет console debug, то печатаем только экстракоды. - * Пропускаем э75, их обычно слишком много. */ - t_value word = mmu_load (Aex); - fprintf (sim_log, "*** %05o%s: ", PC, - (RUU & RUU_RIGHT_INSTR) ? "п" : "л"); - besm6_fprint_cmd (sim_log, RK); - fprintf (sim_log, "\tАисп=%05o (=", Aex); - fprint_sym (sim_log, 0, &word, 0, 0); - fprintf (sim_log, ") СМ="); - fprint_sym (sim_log, 0, &ACC, 0, 0); - if (reg) - fprintf (sim_log, " М[%o]=%05o", reg, M[reg]); - fprintf (sim_log, "\n"); - } - /*besm6_okno ("экстракод");*/ - /* Адрес возврата из экстракода. */ - M[ERET] = nextpc; - /* Сохранённые режимы УУ. */ - M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | - PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); - /* Текущие режимы УУ. */ - M[PSW] = PSW_INTR_DISABLE | PSW_MMAP_DISABLE | - PSW_PROT_DISABLE | /*?*/ PSW_INTR_HALT; - M[14] = Aex; - RUU = SET_SUPERVISOR (RUU, SPSW_EXTRACODE); - - if (opcode <= 077) - PC = 0500 + opcode; /* э50-э77 */ - else - PC = 0540 + (opcode >> 3); /* э20, э21 */ - RUU &= ~RUU_RIGHT_INSTR; - delay = 7; - break; - case 0220: /* мода, utc */ - Aex = ADDR (addr + M[reg]); - next_mod = Aex; - delay = 4; - break; - case 0230: /* мод, wtc */ - if (! addr && reg == 017) { - M[017] = ADDR (M[017] - 1); - corr_stack = 1; - } - Aex = ADDR (addr + M[reg]); - next_mod = ADDR (mmu_load (Aex)); - delay = MEAN_TIME (13, 3); - break; - case 0240: /* уиа, vtm */ - Aex = addr; - M[reg] = addr; - M[0] = 0; - if (IS_SUPERVISOR (RUU) && reg == 0) { - M[PSW] &= ~(PSW_INTR_DISABLE | - PSW_MMAP_DISABLE | PSW_PROT_DISABLE); - M[PSW] |= addr & (PSW_INTR_DISABLE | - PSW_MMAP_DISABLE | PSW_PROT_DISABLE); - } - delay = 4; - break; - case 0250: /* слиа, utm */ - Aex = ADDR (addr + M[reg]); - M[reg] = Aex; - M[0] = 0; - if (IS_SUPERVISOR (RUU) && reg == 0) { - M[PSW] &= ~(PSW_INTR_DISABLE | - PSW_MMAP_DISABLE | PSW_PROT_DISABLE); - M[PSW] |= addr & (PSW_INTR_DISABLE | - PSW_MMAP_DISABLE | PSW_PROT_DISABLE); - } - delay = 4; - break; - case 0260: /* по, uza */ - Aex = ADDR (addr + M[reg]); - RMR = ACC; - delay = MEAN_TIME (12, 3); - if (IS_ADDITIVE (RAU)) { - if (ACC & BIT41) - break; - } else if (IS_MULTIPLICATIVE (RAU)) { - if (! (ACC & BIT48)) - break; - } else if (IS_LOGICAL (RAU)) { - if (ACC) - break; - } else - break; - PC = Aex; - RUU &= ~RUU_RIGHT_INSTR; - delay += 3; - break; - case 0270: /* пе, u1a */ - Aex = ADDR (addr + M[reg]); - RMR = ACC; - delay = MEAN_TIME (12, 3); - if (IS_ADDITIVE (RAU)) { - if (! (ACC & BIT41)) - break; - } else if (IS_MULTIPLICATIVE (RAU)) { - if (ACC & BIT48) - break; - } else if (IS_LOGICAL (RAU)) { - if (! ACC) - break; - } else - /* fall thru, i.e. branch */; - PC = Aex; - RUU &= ~RUU_RIGHT_INSTR; - delay += 3; - break; - case 0300: /* пб, uj */ - Aex = ADDR (addr + M[reg]); - PC = Aex; - RUU &= ~RUU_RIGHT_INSTR; - delay = 7; - break; - case 0310: /* пв, vjm */ - Aex = addr; - M[reg] = nextpc; - M[0] = 0; - PC = addr; - RUU &= ~RUU_RIGHT_INSTR; - delay = 7; - break; - case 0320: /* выпр, iret */ - Aex = addr; - if (! IS_SUPERVISOR (RUU)) { - longjmp (cpu_halt, STOP_BADCMD); - } - M[PSW] = (M[PSW] & PSW_WRITE_WATCH) | - (M[SPSW] & (SPSW_INTR_DISABLE | - SPSW_MMAP_DISABLE | SPSW_PROT_DISABLE)); - PC = M[(reg & 3) | 030]; - RUU &= ~RUU_RIGHT_INSTR; - if (M[SPSW] & SPSW_RIGHT_INSTR) - RUU |= RUU_RIGHT_INSTR; - else - RUU &= ~RUU_RIGHT_INSTR; - RUU = SET_SUPERVISOR (RUU, - M[SPSW] & (SPSW_EXTRACODE | SPSW_INTERRUPT)); - if (M[SPSW] & SPSW_MOD_RK) - next_mod = M[MOD]; - /*besm6_okno ("Выход из прерывания");*/ - delay = 7; - break; - case 0330: /* стоп, stop */ - Aex = ADDR (addr + M[reg]); - delay = 7; - if (! IS_SUPERVISOR(RUU)) { - if (M[PSW] & PSW_CHECK_HALT) - break; - else { - opcode = 063; - goto stop_as_extracode; - } - } - mmu_print_brz (); - longjmp (cpu_halt, STOP_STOP); - break; - case 0340: /* пио, vzm */ -branch_zero: Aex = addr; - delay = 4; - if (! M[reg]) { - PC = addr; - RUU &= ~RUU_RIGHT_INSTR; - delay += 3; - } - break; - case 0350: /* пино, v1m */ - Aex = addr; - delay = 4; - if (M[reg]) { - PC = addr; - RUU &= ~RUU_RIGHT_INSTR; - delay += 3; - } - break; - case 0360: /* э36, *36 */ - goto branch_zero; - case 0370: /* цикл, vlm */ - Aex = addr; - delay = 4; - if (! M[reg]) - break; - M[reg] = ADDR (M[reg] + 1); - PC = addr; - RUU &= ~RUU_RIGHT_INSTR; - delay += 3; - break; - default: - /* Unknown instruction - cannot happen. */ - longjmp (cpu_halt, STOP_STOP); - break; - } - if (next_mod) { - /* Модификация адреса следующей команды. */ - M[MOD] = next_mod; - RUU |= RUU_MOD_RK; - } else - RUU &= ~RUU_MOD_RK; - - /* Не находимся ли мы в цикле "ЖДУ" диспака? */ - if (RUU == 047 && PC == 04440 && RK == 067704440) { - /* Притормаживаем выполнение каждой команды холостого цикла, - * чтобы быстрее обрабатывались прерывания: ускоряются - * терминалы и АЦПУ. */ - delay = sim_interval; - - /* Если периферия простаивает, освобождаем процессор - * до следующего тика таймера. */ - if (vt_is_idle() && - printer_is_idle() && fs_is_idle()) { - check_initial_setup (); - pause (); - } - } -} - -/* - * Операция прерывания 1: внутреннее прерывание. - * Описана в 9-м томе технического описания БЭСМ-6, страница 119. - */ -void op_int_1 (const char *msg) -{ - /*besm6_okno (msg);*/ - M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | - PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); - if (RUU & RUU_RIGHT_INSTR) - M[SPSW] |= SPSW_RIGHT_INSTR; - M[IRET] = PC; - M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE; - if (RUU & RUU_MOD_RK) { - M[SPSW] |= SPSW_MOD_RK; - RUU &= ~RUU_MOD_RK; - } - PC = 0500; - RUU &= ~RUU_RIGHT_INSTR; - RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT); -} - -/* - * Операция прерывания 2: внешнее прерывание. - * Описана в 9-м томе технического описания БЭСМ-6, страница 129. - */ -void op_int_2 () -{ - /*besm6_okno ("Внешнее прерывание");*/ - M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | - PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); - M[IRET] = PC; - M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE; - if (RUU & RUU_MOD_RK) { - M[SPSW] |= SPSW_MOD_RK; - RUU &= ~RUU_MOD_RK; - } - PC = 0501; - RUU &= ~RUU_RIGHT_INSTR; - RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT); -} - -/* - * Main instruction fetch/decode loop - */ -t_stat sim_instr (void) -{ - t_stat r; - int iintr = 0; - - /* Restore register state */ - PC = PC & BITS(15); /* mask PC */ - sim_cancel_step (); /* defang SCP step */ - mmu_setup (); /* copy RP to TLB */ - - /* An internal interrupt or user intervention */ - r = setjmp (cpu_halt); - if (r) { - M[017] += corr_stack; - if (cpu_dev.dctrl) { - const char *message = (r >= SCPE_BASE) ? - scp_errors [r - SCPE_BASE] : - sim_stop_messages [r]; - besm6_debug ("/// %05o%s: %s", PC, - (RUU & RUU_RIGHT_INSTR) ? "п" : "л", - message); - } - - /* - * ПоП и ПоК вызывают останов при любом внутреннем прерывании - * или прерывании по контролю, соответственно. - * Если произошёл останов по ПоП или ПоК, - * то продолжение выполнения начнётся с команды, следующей - * за вызвавшей прерывание. Как если бы кнопка "ТП" (тип - * перехода) была включена. Подробнее на странице 119 ТО9. - */ - switch (r) { - default: -ret: besm6_draw_panel(); - return r; - case STOP_RWATCH: - case STOP_WWATCH: - /* Step back one insn to reexecute it */ - if (! (RUU & RUU_RIGHT_INSTR)) { - --PC; - } - RUU ^= RUU_RIGHT_INSTR; - goto ret; - case STOP_BADCMD: - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; - op_int_1 (sim_stop_messages[r]); - // SPSW_NEXT_RK is not important for this interrupt - GRP |= GRP_ILL_INSN; - break; - case STOP_INSN_CHECK: - if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ - goto ret; - op_int_1 (sim_stop_messages[r]); - // SPSW_NEXT_RK must be 0 for this interrupt; it is already - GRP |= GRP_INSN_CHECK; - break; - case STOP_INSN_PROT: - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; - if (RUU & RUU_RIGHT_INSTR) { - ++PC; - } - RUU ^= RUU_RIGHT_INSTR; - op_int_1 (sim_stop_messages[r]); - // SPSW_NEXT_RK must be 1 for this interrupt - M[SPSW] |= SPSW_NEXT_RK; - GRP |= GRP_INSN_PROT; - break; - case STOP_OPERAND_PROT: -#if 0 -/* ДИСПАК держит признак ПоП установленным. - * При запуске СЕРП возникает обращение к чужому листу. */ - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; -#endif - if (RUU & RUU_RIGHT_INSTR) { - ++PC; - } - RUU ^= RUU_RIGHT_INSTR; - op_int_1 (sim_stop_messages[r]); - M[SPSW] |= SPSW_NEXT_RK; - // The offending virtual page is in bits 5-9 - GRP |= GRP_OPRND_PROT; - GRP = GRP_SET_PAGE (GRP, iintr_data); - break; - case STOP_RAM_CHECK: - if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ - goto ret; - op_int_1 (sim_stop_messages[r]); - // The offending interleaved block # is in bits 1-3. - GRP |= GRP_CHECK | GRP_RAM_CHECK; - GRP = GRP_SET_BLOCK (GRP, iintr_data); - break; - case STOP_CACHE_CHECK: - if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ - goto ret; - op_int_1 (sim_stop_messages[r]); - // The offending BRZ # is in bits 1-3. - GRP |= GRP_CHECK; - GRP &= ~GRP_RAM_CHECK; - GRP = GRP_SET_BLOCK (GRP, iintr_data); - break; - case STOP_INSN_ADDR_MATCH: - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; - if (RUU & RUU_RIGHT_INSTR) { - ++PC; - } - RUU ^= RUU_RIGHT_INSTR; - op_int_1 (sim_stop_messages[r]); - M[SPSW] |= SPSW_NEXT_RK; - GRP |= GRP_BREAKPOINT; - break; - case STOP_LOAD_ADDR_MATCH: - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; - if (RUU & RUU_RIGHT_INSTR) { - ++PC; - } - RUU ^= RUU_RIGHT_INSTR; - op_int_1 (sim_stop_messages[r]); - M[SPSW] |= SPSW_NEXT_RK; - GRP |= GRP_WATCHPT_R; - break; - case STOP_STORE_ADDR_MATCH: - if (M[PSW] & PSW_INTR_HALT) /* ПоП */ - goto ret; - if (RUU & RUU_RIGHT_INSTR) { - ++PC; - } - RUU ^= RUU_RIGHT_INSTR; - op_int_1 (sim_stop_messages[r]); - M[SPSW] |= SPSW_NEXT_RK; - GRP |= GRP_WATCHPT_W; - break; - case STOP_OVFL: - /* Прерывание по АУ вызывает останов, если БРО=0 - * и установлен ПоП или ПоК. - * Страница 118 ТО9.*/ - if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */ - ((M[PSW] & PSW_INTR_HALT) || /* ПоП */ - (M[PSW] & PSW_CHECK_HALT))) /* ПоК */ - goto ret; - op_int_1 (sim_stop_messages[r]); - GRP |= GRP_OVERFLOW|GRP_RAM_CHECK; - break; - case STOP_DIVZERO: - if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */ - ((M[PSW] & PSW_INTR_HALT) || /* ПоП */ - (M[PSW] & PSW_CHECK_HALT))) /* ПоК */ - goto ret; - op_int_1 (sim_stop_messages[r]); - GRP |= GRP_DIVZERO|GRP_RAM_CHECK; - break; - } - ++iintr; - } - - if (iintr > 1) { - besm6_draw_panel(); - return STOP_DOUBLE_INTR; - } - /* Main instruction fetch/decode loop */ - for (;;) { - if (sim_interval <= 0) { /* check clock queue */ - r = sim_process_event (); - if (r) { - besm6_draw_panel(); - return r; - } - } - - if (PC > BITS(15)) { /* выход за пределы памяти */ - besm6_draw_panel(); - return STOP_RUNOUT; /* stop simulation */ - } - - if (sim_brk_summ & SWMASK('E') && /* breakpoint? */ - sim_brk_test (PC, SWMASK ('E'))) { - besm6_draw_panel(); - return STOP_IBKPT; /* stop simulation */ - } - - if (PRP & MPRP) { - /* регистр хранящий, сбрасывается программно */ - GRP |= GRP_SLAVE; - } - - if (! iintr && ! (RUU & RUU_RIGHT_INSTR) && - ! (M[PSW] & PSW_INTR_DISABLE) && (GRP & MGRP)) { - /* external interrupt */ - op_int_2(); - } - cpu_one_inst (); /* one instr */ - iintr = 0; - if (redraw_panel) { - besm6_draw_panel(); - redraw_panel = 0; - } - - if (delay < 1) - delay = 1; - sim_interval -= delay; /* count down delay */ - if (sim_step && (--sim_step <= 0)) { /* do step count */ - besm6_draw_panel(); - return SCPE_STOP; - } - } -} - -t_stat slow_clk (UNIT * this) -{ - /*besm6_debug ("*** таймер 80 мсек");*/ - GRP |= GRP_SLOW_CLK; - return sim_activate (this, MSEC*125/2); -} - -/* - * В 9-й части частота таймера 250 Гц (4 мс), - * в жизни - 50 Гц (20 мс). - */ -t_stat fast_clk (UNIT * this) -{ - /*besm6_debug ("*** таймер 20 мсек");*/ - GRP |= GRP_TIMER; - return sim_activate (this, 20*MSEC); -} - -UNIT clocks[] = { - { UDATA(slow_clk, 0, 0) }, /* 10 р, 16 Гц */ - { UDATA(fast_clk, 0, 0) }, /* 40 р, 50 Гц */ -}; - -t_stat clk_reset (DEVICE * dev) -{ - /* Схема автозапуска включается по нереализованной кнопке "МР" */ -#ifdef SOFT_CLOCK - sim_activate (&clocks[0], MSEC*125/2); - return sim_activate (&clocks[1], 20*MSEC); -#else - return SCPE_OK; -#endif -} - -DEVICE clock_dev = { - "CLK", clocks, NULL, NULL, - 2, 0, 0, 0, 0, 0, - NULL, NULL, &clk_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG -}; +/* + * BESM-6 CPU simulator. + * + * 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. + + * For more information about BESM-6 computer, visit sites: + * - http://www.computer-museum.ru/english/besm6.htm + * - http://mailcom.com/besm6/ + * - http://groups.google.com/group/besm6 + * + * Release notes for BESM-6/SIMH + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 1) All addresses and data values are displayed in octal. + * 2) Memory size is 128 kwords. + * 3) Interrupt system is to be synchronized with wallclock time. + * 4) Execution times are in 1/10 of microsecond. + * 5) Magnetic drums are implemented as a single "DRUM" device. + * 6) Magnetic disks are implemented. + * 7) Magnetic tape is not implemented. + * 8) Punch tape reader is implemented, punch card reader is planned. + * 9) Card puncher is not implemented. + * 10) Displays are implemented. + * 11) Printer АЦПУ-128 is implemented. + * 12) Instruction mnemonics, register names and stop messages + * are in Russian using UTF-8 encoding. It is assumed, that + * user locale is UTF-8. + * 13) A lot of comments in Russian (UTF-8). + */ +#include "besm6_defs.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#undef SOFT_CLOCK + +t_value memory [MEMSIZE]; +uint32 PC, RK, Aex, M [NREGS], RAU, RUU; +t_value ACC, RMR, GRP, MGRP; +uint32 PRP, MPRP; +uint32 READY, READY2; /* ready flags of various devices */ + +extern const char *scp_errors[]; + +/* нехранящие биты ГРП должны сбрасываться путем обнуления тех регистров, + * сборкой которых они являются + */ +#define GRP_WIRED_BITS 01400743700000000LL + +#define PRP_WIRED_BITS 0770000 + +int corr_stack; +int redraw_panel; +uint32 delay; +jmp_buf cpu_halt; + +t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); + +/* + * CPU data structures + * + * cpu_dev CPU device descriptor + * cpu_unit CPU unit descriptor + * cpu_reg CPU register list + * cpu_mod CPU modifiers list + */ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MEMSIZE) }; + +REG cpu_reg[] = { +{ "СчАС", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ +{ "РК", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ +{ "Аисп", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ +{ "СМ", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ +{ "РМР", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ +{ "РАУ", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ +{ "М1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ +{ "М2", &M[2], 8, 15, 0, 1 }, +{ "М3", &M[3], 8, 15, 0, 1 }, +{ "М4", &M[4], 8, 15, 0, 1 }, +{ "М5", &M[5], 8, 15, 0, 1 }, +{ "М6", &M[6], 8, 15, 0, 1 }, +{ "М7", &M[7], 8, 15, 0, 1 }, +{ "М10", &M[010], 8, 15, 0, 1 }, +{ "М11", &M[011], 8, 15, 0, 1 }, +{ "М12", &M[012], 8, 15, 0, 1 }, +{ "М13", &M[013], 8, 15, 0, 1 }, +{ "М14", &M[014], 8, 15, 0, 1 }, +{ "М15", &M[015], 8, 15, 0, 1 }, +{ "М16", &M[016], 8, 15, 0, 1 }, +{ "М17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ +{ "М20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ +{ "М21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ +{ "М27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ +{ "М32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ +{ "М33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ +{ "М34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ +{ "М35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ +{ "РУУ", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ +{ "ГРП", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ +{ "МГРП", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ +{ "ПРП", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ +{ "МПРП", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ +{ 0 } +}; + +MTAB cpu_mod[] = { + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 17, 1, 8, 50, + &cpu_examine, &cpu_deposit, &cpu_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG +}; + +/* + * REG: псевдоустройство, содержащее латинские синонимы всех регистров. + */ +REG reg_reg[] = { +{ "PC", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ +{ "RK", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ +{ "Aex", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ +{ "ACC", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ +{ "RMR", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ +{ "RAU", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ +{ "M1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ +{ "M2", &M[2], 8, 15, 0, 1 }, +{ "M3", &M[3], 8, 15, 0, 1 }, +{ "M4", &M[4], 8, 15, 0, 1 }, +{ "M5", &M[5], 8, 15, 0, 1 }, +{ "M6", &M[6], 8, 15, 0, 1 }, +{ "M7", &M[7], 8, 15, 0, 1 }, +{ "M10", &M[010], 8, 15, 0, 1 }, +{ "M11", &M[011], 8, 15, 0, 1 }, +{ "M12", &M[012], 8, 15, 0, 1 }, +{ "M13", &M[013], 8, 15, 0, 1 }, +{ "M14", &M[014], 8, 15, 0, 1 }, +{ "M15", &M[015], 8, 15, 0, 1 }, +{ "M16", &M[016], 8, 15, 0, 1 }, +{ "M17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ +{ "M20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ +{ "M21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ +{ "M27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ +{ "M32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ +{ "M33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ +{ "M34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ +{ "M35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ +{ "RUU", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ +{ "GRP", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ +{ "MGRP", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ +{ "PRP", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ +{ "MPRP", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ + +{ "BRZ0", &BRZ[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ1", &BRZ[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ2", &BRZ[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ3", &BRZ[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ4", &BRZ[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ5", &BRZ[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ6", &BRZ[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BRZ7", &BRZ[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "BAZ0", &BAZ[0], 8, 16, 0, 1 }, +{ "BAZ1", &BAZ[1], 8, 16, 0, 1 }, +{ "BAZ2", &BAZ[2], 8, 16, 0, 1 }, +{ "BAZ3", &BAZ[3], 8, 16, 0, 1 }, +{ "BAZ4", &BAZ[4], 8, 16, 0, 1 }, +{ "BAZ5", &BAZ[5], 8, 16, 0, 1 }, +{ "BAZ6", &BAZ[6], 8, 16, 0, 1 }, +{ "BAZ7", &BAZ[7], 8, 16, 0, 1 }, +{ "TABST", &TABST, 8, 28, 0, 1 }, +{ "RP0", &RP[0], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP1", &RP[1], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP2", &RP[2], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP3", &RP[3], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP4", &RP[4], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP5", &RP[5], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP6", &RP[6], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RP7", &RP[7], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, +{ "RZ", &RZ, 8, 32, 0, 1 }, +{ "FP1", &pult[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP2", &pult[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP3", &pult[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP4", &pult[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP5", &pult[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP6", &pult[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ "FP7", &pult[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, +{ 0 } +}; + +UNIT reg_unit = { + UDATA (NULL, 0, 8) +}; + +DEVICE reg_dev = { + "REG", ®_unit, reg_reg, NULL, + 1, 8, 1, 1, 8, 50, +}; + +/* + * SCP data structures and interface routines + * + * sim_name simulator name string + * sim_PC pointer to saved PC register descriptor + * sim_emax maximum number of words for examine/deposit + * sim_devices array of pointers to simulated devices + * sim_stop_messages array of pointers to stop messages + * sim_load binary loader + */ + +char sim_name[] = "БЭСМ-6"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; /* максимальное количество слов в машинной команде */ + +DEVICE *sim_devices[] = { + &cpu_dev, + ®_dev, + &drum_dev, + &disk_dev, + &mmu_dev, + &clock_dev, + &printer_dev, + &fs_dev, + &tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */ + 0 +}; + +const char *sim_stop_messages[] = { + "Неизвестная ошибка", /* Unknown error */ + "Останов", /* STOP */ + "Точка останова", /* Emulator breakpoint */ + "Точка останова по считыванию", /* Emulator read watchpoint */ + "Точка останова по записи", /* Emulator write watchpoint */ + "Выход за пределы памяти", /* Run out end of memory */ + "Запрещенная команда", /* Invalid instruction */ + "Контроль команды", /* A data-tagged word fetched */ + "Команда в чужом листе", /* Paging error during fetch */ + "Число в чужом листе", /* Paging error during load/store */ + "Контроль числа МОЗУ", /* RAM parity error */ + "Контроль числа БРЗ", /* Write cache parity error */ + "Переполнение АУ", /* Arith. overflow */ + "Деление на нуль", /* Division by zero or denorm */ + "Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */ + "Чтение неформатированного барабана", /* Reading unformatted drum */ + "Чтение неформатированного диска", /* Reading unformatted disk */ + "Останов по КРА", /* Hardware breakpoint */ + "Останов по считыванию", /* Load watchpoint */ + "Останов по записи", /* Store watchpoint */ + "Не реализовано", /* Unimplemented I/O or special reg. access */ +}; + +/* + * Memory examine + */ +t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (vptr) { + if (addr < 010) + *vptr = pult [addr]; + else + *vptr = memory [addr]; + } + return SCPE_OK; +} + +/* + * Memory deposit + */ +t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) + return SCPE_NXM; + if (addr < 010) + pult [addr] = SET_CONVOL (val, CONVOL_INSN); + else + memory [addr] = SET_CONVOL (val, CONVOL_INSN); + return SCPE_OK; +} + +/* + * Функция вызывается каждые 4 миллисекунды реального времени. + */ +static void cpu_sigalarm (int signum) +{ + static unsigned counter; + + ++counter; + +#ifndef SOFT_CLOCK + /* В 9-й части частота таймера 250 Гц (4 мс). */ + GRP |= GRP_TIMER; + + /* Медленный таймер: должен быть 16 Гц. + * Но от него почему-то зависит вывод на терминалы, + * поэтому ускорим. */ + if ((counter & 3) == 0) { + GRP |= GRP_SLOW_CLK; + } +#endif + + /* Перерисовка панели каждые 64 миллисекунды. */ + if ((counter & 15) == 0) { + redraw_panel = 1; + } +} + +/* + * Reset routine + */ +t_stat cpu_reset (DEVICE *dptr) +{ + int i; + + ACC = 0; + RMR = 0; + RAU = 0; + RUU = RUU_EXTRACODE | RUU_AVOST_DISABLE; + for (i=0; i 0xxxxxxx + * 00000xxx.xxyyyyyy -> 110xxxxx, 10yyyyyy + * xxxxyyyy.yyzzzzzz -> 1110xxxx, 10yyyyyy, 10zzzzzz + */ +void +utf8_putc (unsigned ch, FILE *fout) +{ + if (ch < 0x80) { + putc (ch, fout); + return; + } + if (ch < 0x800) { + putc (ch >> 6 | 0xc0, fout); + putc ((ch & 0x3f) | 0x80, fout); + return; + } + putc (ch >> 12 | 0xe0, fout); + putc (((ch >> 6) & 0x3f) | 0x80, fout); + putc ((ch & 0x3f) | 0x80, fout); +} + +/* + * *call ОКНО - так называлась служебная подпрограмма в мониторной + * системе "Дубна", которая печатала полное состояние всех регистров. + */ +void besm6_okno (const char *message) +{ + besm6_log_cont ("_%%%%%% %s: ", message); + if (sim_log) + besm6_fprint_cmd (sim_log, RK); + besm6_log ("_"); + + /* СчАС, системные индекс-регистры 020-035. */ + besm6_log ("_ СчАС:%05o 20:%05o 21:%05o 27:%05o 32:%05o 33:%05o 34:%05o 35:%05o", + PC, M[020], M[021], M[027], M[032], M[033], M[034], M[035]); + /* Индекс-регистры 1-7. */ + besm6_log ("_ 1:%05o 2:%05o 3:%05o 4:%05o 5:%05o 6:%05o 7:%05o", + M[1], M[2], M[3], M[4], M[5], M[6], M[7]); + /* Индекс-регистры 010-017. */ + besm6_log ("_ 10:%05o 11:%05o 12:%05o 13:%05o 14:%05o 15:%05o 16:%05o 17:%05o", + M[010], M[011], M[012], M[013], M[014], M[015], M[016], M[017]); + /* Сумматор, РМР, режимы АУ и УУ. */ + besm6_log ("_ СМ:%04o %04o %04o %04o РМР:%04o %04o %04o %04o РАУ:%02o РУУ:%03o", + (int) (ACC >> 36) & BITS(12), (int) (ACC >> 24) & BITS(12), + (int) (ACC >> 12) & BITS(12), (int) ACC & BITS(12), + (int) (RMR >> 36) & BITS(12), (int) (RMR >> 24) & BITS(12), + (int) (RMR >> 12) & BITS(12), (int) RMR & BITS(12), + RAU, RUU); +} + +/* + * Команда "рег" + */ +static void cmd_002 () +{ +#if 0 + besm6_debug ("*** рег %03o", Aex & 0377); +#endif + switch (Aex & 0377) { + case 0 ... 7: + /* Запись в БРЗ */ + mmu_setcache (Aex & 7, ACC); + break; + case 020 ... 027: + /* Запись в регистры приписки */ + mmu_setrp (Aex & 7, ACC); + break; + case 030 ... 033: + /* Запись в регистры защиты */ + mmu_setprotection (Aex & 3, ACC); + break; + case 036: + /* Запись в маску главного регистра прерываний */ + MGRP = ACC; + break; + case 037: + /* Гашение главного регистра прерываний */ + /* нехранящие биты невозможно погасить */ + GRP &= ACC | GRP_WIRED_BITS; + break; + case 0100 ... 0137: + /* Бит 1: управление блокировкой режима останова БРО. + * Биты 2 и 3 - признаки формирования контрольных + * разрядов (ПКП и ПКЛ). */ + if (Aex & 1) + RUU |= RUU_AVOST_DISABLE; + else + RUU &= ~RUU_AVOST_DISABLE; + if (Aex & 2) + RUU |= RUU_CONVOL_RIGHT; + else + RUU &= ~RUU_CONVOL_RIGHT; + if (Aex & 4) + RUU |= RUU_CONVOL_LEFT; + else + RUU &= ~RUU_CONVOL_LEFT; + break; + case 0140 ... 0177: + /* TODO: управление блокировкой схемы + * автоматического запуска */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0200 ... 0207: + /* Чтение БРЗ */ + ACC = mmu_getcache (Aex & 7); + break; + case 0237: + /* Чтение главного регистра прерываний */ + ACC = GRP; + break; + default: + /* Неиспользуемые адреса */ + besm6_debug ("*** %05o%s: РЕГ %o - неправильный адрес спец.регистра", + PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex); + break; + } +} + +/* + * Команда "увв" + */ +static void cmd_033 () +{ +#if 0 + besm6_debug ("*** увв %04o, СМ[24:1]=%08o", + Aex & 04177, (uint32) ACC & BITS(24)); +#endif + switch (Aex & 04177) { + case 0: + /* Точно неизвестно, что это такое, но драйвер МД + * иногда выдает команду "увв 0". */ + break; + case 1 ... 2: + /* Управление обменом с магнитными барабанами */ + drum (Aex - 1, (uint32) ACC); + break; + case 3 ... 4: + /* Передача управляющего слова для обмена + * с магнитными дисками */ + disk_io (Aex - 3, (uint32) ACC); + break; + case 5 ... 7: + /* TODO: управление обменом с магнитными лентами */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 010 ... 011: + /* управление устройствами ввода с перфоленты */ + fs_control (Aex - 010, (uint32) (ACC & 07)); + break; + case 012 ... 013: + /* TODO: управление устройствами ввода с перфоленты по запаянной программе */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 014 ... 015: + /* Управление АЦПУ */ + printer_control (Aex - 014, (uint32) (ACC & 017)); + break; + case 023 ... 024: + /* Управление обменом с магнитными дисками */ + disk_ctl (Aex - 023, (uint32) ACC); + break; + case 030: + /* Гашение ПРП */ +/* besm6_debug(">>> гашение ПРП");*/ + PRP &= ACC | PRP_WIRED_BITS; + break; + case 031: + /* Имитация сигналов прерывания ГРП */ + /*besm6_debug ("*** %05o%s: имитация прерываний ГРП %016llo", + PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", ACC << 24);*/ + GRP |= (ACC & BITS(24)) << 24; + break; + case 032 ... 033: + /* TODO: имитация сигналов из КМБ в КВУ */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 034: + /* Запись в МПРП */ +/* besm6_debug(">>> запись в МПРП");*/ + MPRP = ACC & 077777777; + break; + case 035: + /* TODO: управление режимом имитации обмена + * с МБ и МЛ, имитация обмена */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 040 ... 057: + /* Управление молоточками АЦПУ */ + printer_hammer (Aex >= 050, Aex & 7, (uint32) (ACC & BITS(16))); + break; + case 0100 ... 0137: + /* Управление лентопротяжными механизмами + * и гашение разрядов регистров признаков + * окончания подвода зоны. Игнорируем. */ + break; + case 0140: + /* Запись в регистр телеграфных каналов */ + tty_send ((uint32) ACC & BITS(24)); + break; + case 0141: + /* TODO: управление разметкой магнитной ленты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0142: + /* TODO: имитация сигналов прерывания ПРП */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0147: + /* Запись в регистр управления электропитанием, */ + /* не оказывает видимого эффекта на выполнение */ + break; + case 0150 ... 0151: + /* TODO: управление вводом с перфокарт */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0153: + /* гашение аппаратуры сопряжения с терминалами */ +/* besm6_debug(">>> гашение АС: %08o", (uint32) ACC & BITS(24));*/ + break; + case 0154 ... 0155: + /* TODO: управление выводом на перфокарты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0160 ... 0167: + /* TODO: управление электромагнитами пробивки перфокарт */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0170 ... 0171: + /* TODO: пробивка строки на перфоленте */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 0174 ... 0175: + /* Выдача кода в пульт оператора */ + consul_print (Aex & 1, (uint32) ACC & BITS(8)); + break; + case 0177: + /* управление табло ГПВЦ СО АН СССР */ +/* besm6_debug(">>> ТАБЛО: %08o", (uint32) ACC & BITS(24));*/ + break; + case 04001 ... 04002: + /* TODO: считывание слога в режиме имитации обмена */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04003 ... 04004: + /* Запрос статуса контроллера магнитных дисков */ + ACC = disk_state (Aex - 04003); + break; + case 04006: + /* TODO: считывание строки с устройства ввода + * с перфоленты в запаянной программе */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04007: + /* TODO: опрос синхроимпульса ненулевой строки + * в запаянной программе ввода с перфоленты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04014 ... 04015: + /* считывание строки с устройства ввода с перфоленты */ + ACC = fs_read (Aex - 04014); + break; + case 04016 ... 04017: + /* TODO: считывание строки с устройства + * ввода с перфоленты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04020 ... 04023: + /* TODO: считывание слога в режиме имитации + * внешнего обмена */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04030: + /* Чтение старшей половины ПРП */ + ACC = PRP & 077770000; + break; + case 04031: + /* Опрос сигналов готовности (АЦПУ и пр.) */ +/* besm6_debug("Reading READY");*/ + ACC = READY; + break; + case 04034: + /* Чтение младшей половины ПРП */ + ACC = (PRP & 07777) | 0377; + break; + case 04035: + /* Опрос триггера ОШМi - наличие ошибок при внешнем обмене. */ + ACC = drum_errors() | disk_errors(); + break; + case 04100: + /* Опрос телеграфных каналов связи */ + ACC = tty_query (); + break; + case 04102: + /* Опрос сигналов готовности перфокарт и перфолент */ +/* besm6_debug("Reading punchcard/punchtape READY @%05o", PC);*/ + ACC = READY2; + break; + case 04103 ... 04106: + /* Опрос состояния лентопротяжных механизмов. + * Все устройства не готовы. */ + ACC = BITS(24); + break; + case 04107: + /* TODO: опрос схемы контроля записи на МЛ */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04115: + /* Неизвестное обращение. ДИСПАК выдаёт эту команду + * группами по 8 штук каждые несколько секунд. */ + ACC = 0; + break; + case 04140 ... 04157: + /* TODO: считывание строки перфокарты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04160 ... 04167: + /* TODO: контрольное считывание строки перфокарты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04170 ... 04173: + /* TODO: считывание контрольного кода + * строки перфоленты */ + longjmp (cpu_halt, STOP_UNIMPLEMENTED); + break; + case 04174 ... 04175: + /* Считывание кода с пульта оператора */ + ACC = consul_read (Aex & 1); + break; + case 04177: + /* чтение табло ГПВЦ СО АН СССР */ + ACC = 0; + break; + default: + /* Неиспользуемые адреса */ +/* if (sim_deb && cpu_dev.dctrl)*/ + besm6_debug ("*** %05o%s: УВВ %o - неправильный адрес ввода-вывода", + PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex); + ACC = 0; + break; + } +} + +void check_initial_setup () +{ + const int MGRP_COPY = 01455; /* OS version specific? */ + const int TAKEN = 0442; /* fixed? */ + const int YEAR = 0221; /* fixed */ + + /* 47 р. яч. ЗАНЯТА - разр. приказы вообще */ + const t_value SETUP_REQS_ENABLED = 1LL << 46; + + /* 7 р. яч. ЗАНЯТА - разр любые приказы */ + const t_value ALL_REQS_ENABLED = 1 << 6; + + if ((memory[TAKEN] & SETUP_REQS_ENABLED) == 0 || + (memory[TAKEN] & ALL_REQS_ENABLED) != 0 || + (MGRP & GRP_PANEL_REQ) == 0) { + /* Слишком рано, или уже не надо, или невовремя */ + return; + } + + /* Выдаем приказы оператора СМЕ и ВРЕ, + * а дату корректируем непосредственно в памяти. + */ + /* Номер смены в 22-24 рр. МГРП: если еще не установлен, установить */ + if (((memory[MGRP_COPY] >> 21) & 3) == 0) { + /* приказ СМЕ: ТР6 = 010, ТР4 = 1, 22-24 р ТР5 - #смены */ + pult[6] = 010; + pult[4] = 1; + pult[5] = 1 << 21; + GRP |= GRP_PANEL_REQ; + } else { + /* Яч. ГОД обновляем самостоятельно */ + time_t t; + t_value date; + time(&t); + struct tm * d; + d = localtime(&t); + ++d->tm_mon; + date = (t_value) (d->tm_mday / 10) << 33 | + (t_value) (d->tm_mday % 10) << 29 | + (d->tm_mon / 10) << 28 | + (d->tm_mon % 10) << 24 | + (d->tm_year % 10) << 20 | + ((d->tm_year / 10) % 10) << 16 | + (memory[YEAR] & 7); + memory[YEAR] = SET_CONVOL (date, CONVOL_NUMBER); + /* приказ ВРЕ: ТР6 = 016, ТР5 = 9-14 р.-часы, 1-8 р.-минуты */ + pult[6] = 016; + pult[4] = 0; + pult[5] = (d->tm_hour / 10) << 12 | + (d->tm_hour % 10) << 8 | + (d->tm_min / 10) << 4 | + (d->tm_min % 10); + GRP |= GRP_PANEL_REQ; + } +} + +/* + * Execute one instruction, placed on address PC:RUU_RIGHT_INSTR. + * Increment delay. When stopped, perform a longjmp to cpu_halt, + * sending a stop code. + */ +void cpu_one_inst () +{ + int reg, opcode, addr, nextpc, next_mod; + + corr_stack = 0; + t_value word = mmu_fetch (PC); + if (RUU & RUU_RIGHT_INSTR) + RK = word; /* get right instruction */ + else + RK = word >> 24; /* get left instruction */ + + RK &= BITS(24); + + reg = RK >> 20; + if (RK & BBIT(20)) { + addr = RK & BITS(15); + opcode = (RK >> 12) & 0370; + } else { + addr = RK & BITS(12); + if (RK & BBIT(19)) + addr |= 070000; + opcode = (RK >> 12) & 077; + } + + if (sim_deb && cpu_dev.dctrl) { + fprintf (sim_deb, "*** %05o%s: ", PC, + (RUU & RUU_RIGHT_INSTR) ? "п" : "л"); + besm6_fprint_cmd (sim_deb, RK); + fprintf (sim_deb, "\tСМ="); + fprint_sym (sim_deb, 0, &ACC, 0, 0); + fprintf (sim_deb, "\tРАУ=%02o", RAU); + if (reg) + fprintf (sim_deb, "\tМ[%o]=%05o", reg, M[reg]); + fprintf (sim_deb, "\n"); + } + nextpc = ADDR(PC + 1); + if (RUU & RUU_RIGHT_INSTR) { + PC += 1; /* increment PC */ + RUU &= ~RUU_RIGHT_INSTR; + } else { + mmu_prefetch(nextpc | (IS_SUPERVISOR(RUU) ? BBIT(16) : 0), 0); + RUU |= RUU_RIGHT_INSTR; + } + + if (RUU & RUU_MOD_RK) { + addr = ADDR (addr + M[MOD]); + } + next_mod = 0; + delay = 0; + + switch (opcode) { + case 000: /* зп, atx */ + Aex = ADDR (addr + M[reg]); + mmu_store (Aex, ACC); + if (! addr && reg == 017) + M[017] = ADDR (M[017] + 1); + delay = MEAN_TIME (3, 3); + break; + case 001: /* зпм, stx */ + Aex = ADDR (addr + M[reg]); + mmu_store (Aex, ACC); + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + ACC = mmu_load (M[017]); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (6, 6); + break; + case 002: /* рег, mod */ + Aex = ADDR (addr + M[reg]); + if (! IS_SUPERVISOR (RUU)) + longjmp (cpu_halt, STOP_BADCMD); + cmd_002 (); + /* Режим АУ - логический, если операция была "чтение" */ + if (Aex & 0200) + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 3); + break; + case 003: /* счм, xts */ + mmu_store (M[017], ACC); + M[017] = ADDR (M[017] + 1); + corr_stack = -1; + Aex = ADDR (addr + M[reg]); + ACC = mmu_load (Aex); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (6, 6); + break; + case 004: /* сл, a+x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add (mmu_load (Aex), 0, 0); + RAU = SET_ADDITIVE (RAU); + delay = MEAN_TIME (3, 11); + break; + case 005: /* вч, a-x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add (mmu_load (Aex), 0, 1); + RAU = SET_ADDITIVE (RAU); + delay = MEAN_TIME (3, 11); + break; + case 006: /* вчоб, x-a */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add (mmu_load (Aex), 1, 0); + RAU = SET_ADDITIVE (RAU); + delay = MEAN_TIME (3, 11); + break; + case 007: /* вчаб, amx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add (mmu_load (Aex), 1, 1); + RAU = SET_ADDITIVE (RAU); + delay = MEAN_TIME (3, 11); + break; + case 010: /* сч, xta */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC = mmu_load (Aex); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 3); + break; + case 011: /* и, aax */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC &= mmu_load (Aex); + RMR = 0; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 4); + break; + case 012: /* нтж, aex */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + RMR = ACC; + ACC ^= mmu_load (Aex); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 3); + break; + case 013: /* слц, arx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC += mmu_load (Aex); + if (ACC & BIT49) + ACC = (ACC + 1) & BITS48; + RMR = 0; + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 6); + break; + case 014: /* знак, avx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_change_sign (mmu_load (Aex) >> 40 & 1); + RAU = SET_ADDITIVE (RAU); + delay = MEAN_TIME (3, 5); + break; + case 015: /* или, aox */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC |= mmu_load (Aex); + RMR = 0; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 4); + break; + case 016: /* дел, a/x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_divide (mmu_load (Aex)); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 50); + break; + case 017: /* умн, a*x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_multiply (mmu_load (Aex)); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 18); + break; + case 020: /* сбр, apx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC = besm6_pack (ACC, mmu_load (Aex)); + RMR = 0; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 53); + break; + case 021: /* рзб, aux */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC = besm6_unpack (ACC, mmu_load (Aex)); + RMR = 0; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 53); + break; + case 022: /* чед, acx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + ACC = besm6_count_ones (ACC) + mmu_load (Aex); + if (ACC & BIT49) + ACC = (ACC + 1) & BITS48; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 56); + break; + case 023: /* нед, anx */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + if (ACC) { + int n = besm6_highest_bit (ACC); + + /* "Остаток" сумматора, исключая бит, + * номер которого определен, помещается в РМР, + * начиная со старшего бита РМР. */ + besm6_shift (48 - n); + + /* Циклическое сложение номера со словом по Аисп. */ + ACC = n + mmu_load (Aex); + if (ACC & BIT49) + ACC = (ACC + 1) & BITS48; + } else { + RMR = 0; + ACC = mmu_load (Aex); + } + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 32); + break; + case 024: /* слп, e+x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add_exponent ((mmu_load (Aex) >> 41) - 64); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 5); + break; + case 025: /* вчп, e-x */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + besm6_add_exponent (64 - (mmu_load (Aex) >> 41)); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 5); + break; + case 026: { /* сд, asx */ + int n; + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + n = (mmu_load (Aex) >> 41) - 64; + besm6_shift (n); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 4 + abs (n)); + break; + } + case 027: /* рж, xtr */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + RAU = (mmu_load (Aex) >> 41) & 077; + delay = MEAN_TIME (3, 3); + break; + case 030: /* счрж, rte */ + Aex = ADDR (addr + M[reg]); + ACC = (t_value) (RAU & Aex & 0177) << 41; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 3); + break; + case 031: /* счмр, yta */ + Aex = ADDR (addr + M[reg]); + if (IS_LOGICAL (RAU)) { + ACC = RMR; + } else { + t_value x = RMR; + ACC = (ACC & ~BITS41) | (RMR & BITS40); + besm6_add_exponent ((Aex & 0177) - 64); + RMR = x; + } + delay = MEAN_TIME (3, 5); + break; + case 032: /* э32, ext */ + /* Fall through... */ + case 033: /* увв, ext */ + Aex = ADDR (addr + M[reg]); + if (! IS_SUPERVISOR (RUU)) + longjmp (cpu_halt, STOP_BADCMD); + cmd_033 (); + /* Режим АУ - логический, если операция была "чтение" */ + if (Aex & 04000) + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 8); + break; + case 034: /* слпа, e+n */ + Aex = ADDR (addr + M[reg]); + besm6_add_exponent ((Aex & 0177) - 64); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 5); + break; + case 035: /* вчпа, e-n */ + Aex = ADDR (addr + M[reg]); + besm6_add_exponent (64 - (Aex & 0177)); + RAU = SET_MULTIPLICATIVE (RAU); + delay = MEAN_TIME (3, 5); + break; + case 036: { /* сда, asn */ + int n; + Aex = ADDR (addr + M[reg]); + n = (Aex & 0177) - 64; + besm6_shift (n); + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (3, 4 + abs (n)); + break; + } + case 037: /* ржа, ntr */ + Aex = ADDR (addr + M[reg]); + RAU = Aex & 077; + delay = MEAN_TIME (3, 3); + break; + case 040: /* уи, ati */ + Aex = ADDR (addr + M[reg]); + if (IS_SUPERVISOR (RUU)) { + int reg = Aex & 037; + M[reg] = ADDR (ACC); + /* breakpoint/watchpoint regs will match physical + * or virtual addresses depending on the current + * mapping mode. + */ + if ((M[PSW] & PSW_MMAP_DISABLE) && + (reg == IBP || reg == DWP)) + M[reg] |= BBIT(16); + + } else + M[Aex & 017] = ADDR (ACC); + M[0] = 0; + delay = MEAN_TIME (14, 3); + break; + case 041: { /* уим, sti */ + unsigned rg, ad; + + Aex = ADDR (addr + M[reg]); + rg = Aex & (IS_SUPERVISOR (RUU) ? 037 : 017); + ad = ADDR (ACC); + if (rg != 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + ACC = mmu_load (rg != 017 ? M[017] : ad); + M[rg] = ad; + if ((M[PSW] & PSW_MMAP_DISABLE) && (rg == IBP || rg == DWP)) + M[rg] |= BBIT(16); + M[0] = 0; + RAU = SET_LOGICAL (RAU); + delay = MEAN_TIME (14, 3); + break; + } + case 042: /* счи, ita */ + delay = MEAN_TIME (6, 3); +load_modifier: Aex = ADDR (addr + M[reg]); + ACC = ADDR(M[Aex & (IS_SUPERVISOR (RUU) ? 037 : 017)]); + RAU = SET_LOGICAL (RAU); + break; + case 043: /* счим, its */ + mmu_store (M[017], ACC); + M[017] = ADDR (M[017] + 1); + delay = MEAN_TIME (9, 6); + goto load_modifier; + case 044: /* уии, mtj */ + Aex = addr; + if (IS_SUPERVISOR (RUU)) { +transfer_modifier: M[Aex & 037] = M[reg]; + if ((M[PSW] & PSW_MMAP_DISABLE) && + ((Aex & 037) == IBP || (Aex & 037) == DWP)) + M[Aex & 037] |= BBIT(16); + + } else + M[Aex & 017] = M[reg]; + M[0] = 0; + delay = 6; + break; + case 045: /* сли, j+m */ + Aex = addr; + if ((Aex & 020) && IS_SUPERVISOR (RUU)) + goto transfer_modifier; + M[Aex & 017] = ADDR (M[Aex & 017] + M[reg]); + M[0] = 0; + delay = 6; + break; + case 046: /* э46, x46 */ + Aex = addr; + if (! IS_SUPERVISOR (RUU)) + longjmp (cpu_halt, STOP_BADCMD); + M[Aex & 017] = ADDR (Aex); + M[0] = 0; + delay = 6; + break; + case 047: /* э47, x47 */ + Aex = addr; + if (! IS_SUPERVISOR (RUU)) + longjmp (cpu_halt, STOP_BADCMD); + M[Aex & 017] = ADDR (M[Aex & 017] + Aex); + M[0] = 0; + delay = 6; + break; + case 050 ... 077: /* э50...э77 */ + case 0200: /* э20 */ + case 0210: /* э21 */ + stop_as_extracode: + Aex = ADDR (addr + M[reg]); + if (! sim_deb && sim_log && cpu_dev.dctrl && opcode != 075) { + /* Если включен console log и cpu debug, + * но нет console debug, то печатаем только экстракоды. + * Пропускаем э75, их обычно слишком много. */ + t_value word = mmu_load (Aex); + fprintf (sim_log, "*** %05o%s: ", PC, + (RUU & RUU_RIGHT_INSTR) ? "п" : "л"); + besm6_fprint_cmd (sim_log, RK); + fprintf (sim_log, "\tАисп=%05o (=", Aex); + fprint_sym (sim_log, 0, &word, 0, 0); + fprintf (sim_log, ") СМ="); + fprint_sym (sim_log, 0, &ACC, 0, 0); + if (reg) + fprintf (sim_log, " М[%o]=%05o", reg, M[reg]); + fprintf (sim_log, "\n"); + } + /*besm6_okno ("экстракод");*/ + /* Адрес возврата из экстракода. */ + M[ERET] = nextpc; + /* Сохранённые режимы УУ. */ + M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | + PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); + /* Текущие режимы УУ. */ + M[PSW] = PSW_INTR_DISABLE | PSW_MMAP_DISABLE | + PSW_PROT_DISABLE | /*?*/ PSW_INTR_HALT; + M[14] = Aex; + RUU = SET_SUPERVISOR (RUU, SPSW_EXTRACODE); + + if (opcode <= 077) + PC = 0500 + opcode; /* э50-э77 */ + else + PC = 0540 + (opcode >> 3); /* э20, э21 */ + RUU &= ~RUU_RIGHT_INSTR; + delay = 7; + break; + case 0220: /* мода, utc */ + Aex = ADDR (addr + M[reg]); + next_mod = Aex; + delay = 4; + break; + case 0230: /* мод, wtc */ + if (! addr && reg == 017) { + M[017] = ADDR (M[017] - 1); + corr_stack = 1; + } + Aex = ADDR (addr + M[reg]); + next_mod = ADDR (mmu_load (Aex)); + delay = MEAN_TIME (13, 3); + break; + case 0240: /* уиа, vtm */ + Aex = addr; + M[reg] = addr; + M[0] = 0; + if (IS_SUPERVISOR (RUU) && reg == 0) { + M[PSW] &= ~(PSW_INTR_DISABLE | + PSW_MMAP_DISABLE | PSW_PROT_DISABLE); + M[PSW] |= addr & (PSW_INTR_DISABLE | + PSW_MMAP_DISABLE | PSW_PROT_DISABLE); + } + delay = 4; + break; + case 0250: /* слиа, utm */ + Aex = ADDR (addr + M[reg]); + M[reg] = Aex; + M[0] = 0; + if (IS_SUPERVISOR (RUU) && reg == 0) { + M[PSW] &= ~(PSW_INTR_DISABLE | + PSW_MMAP_DISABLE | PSW_PROT_DISABLE); + M[PSW] |= addr & (PSW_INTR_DISABLE | + PSW_MMAP_DISABLE | PSW_PROT_DISABLE); + } + delay = 4; + break; + case 0260: /* по, uza */ + Aex = ADDR (addr + M[reg]); + RMR = ACC; + delay = MEAN_TIME (12, 3); + if (IS_ADDITIVE (RAU)) { + if (ACC & BIT41) + break; + } else if (IS_MULTIPLICATIVE (RAU)) { + if (! (ACC & BIT48)) + break; + } else if (IS_LOGICAL (RAU)) { + if (ACC) + break; + } else + break; + PC = Aex; + RUU &= ~RUU_RIGHT_INSTR; + delay += 3; + break; + case 0270: /* пе, u1a */ + Aex = ADDR (addr + M[reg]); + RMR = ACC; + delay = MEAN_TIME (12, 3); + if (IS_ADDITIVE (RAU)) { + if (! (ACC & BIT41)) + break; + } else if (IS_MULTIPLICATIVE (RAU)) { + if (ACC & BIT48) + break; + } else if (IS_LOGICAL (RAU)) { + if (! ACC) + break; + } else + /* fall thru, i.e. branch */; + PC = Aex; + RUU &= ~RUU_RIGHT_INSTR; + delay += 3; + break; + case 0300: /* пб, uj */ + Aex = ADDR (addr + M[reg]); + PC = Aex; + RUU &= ~RUU_RIGHT_INSTR; + delay = 7; + break; + case 0310: /* пв, vjm */ + Aex = addr; + M[reg] = nextpc; + M[0] = 0; + PC = addr; + RUU &= ~RUU_RIGHT_INSTR; + delay = 7; + break; + case 0320: /* выпр, iret */ + Aex = addr; + if (! IS_SUPERVISOR (RUU)) { + longjmp (cpu_halt, STOP_BADCMD); + } + M[PSW] = (M[PSW] & PSW_WRITE_WATCH) | + (M[SPSW] & (SPSW_INTR_DISABLE | + SPSW_MMAP_DISABLE | SPSW_PROT_DISABLE)); + PC = M[(reg & 3) | 030]; + RUU &= ~RUU_RIGHT_INSTR; + if (M[SPSW] & SPSW_RIGHT_INSTR) + RUU |= RUU_RIGHT_INSTR; + else + RUU &= ~RUU_RIGHT_INSTR; + RUU = SET_SUPERVISOR (RUU, + M[SPSW] & (SPSW_EXTRACODE | SPSW_INTERRUPT)); + if (M[SPSW] & SPSW_MOD_RK) + next_mod = M[MOD]; + /*besm6_okno ("Выход из прерывания");*/ + delay = 7; + break; + case 0330: /* стоп, stop */ + Aex = ADDR (addr + M[reg]); + delay = 7; + if (! IS_SUPERVISOR(RUU)) { + if (M[PSW] & PSW_CHECK_HALT) + break; + else { + opcode = 063; + goto stop_as_extracode; + } + } + mmu_print_brz (); + longjmp (cpu_halt, STOP_STOP); + break; + case 0340: /* пио, vzm */ +branch_zero: Aex = addr; + delay = 4; + if (! M[reg]) { + PC = addr; + RUU &= ~RUU_RIGHT_INSTR; + delay += 3; + } + break; + case 0350: /* пино, v1m */ + Aex = addr; + delay = 4; + if (M[reg]) { + PC = addr; + RUU &= ~RUU_RIGHT_INSTR; + delay += 3; + } + break; + case 0360: /* э36, *36 */ + goto branch_zero; + case 0370: /* цикл, vlm */ + Aex = addr; + delay = 4; + if (! M[reg]) + break; + M[reg] = ADDR (M[reg] + 1); + PC = addr; + RUU &= ~RUU_RIGHT_INSTR; + delay += 3; + break; + default: + /* Unknown instruction - cannot happen. */ + longjmp (cpu_halt, STOP_STOP); + break; + } + if (next_mod) { + /* Модификация адреса следующей команды. */ + M[MOD] = next_mod; + RUU |= RUU_MOD_RK; + } else + RUU &= ~RUU_MOD_RK; + + /* Не находимся ли мы в цикле "ЖДУ" диспака? */ + if (RUU == 047 && PC == 04440 && RK == 067704440) { + /* Притормаживаем выполнение каждой команды холостого цикла, + * чтобы быстрее обрабатывались прерывания: ускоряются + * терминалы и АЦПУ. */ + delay = sim_interval; + + /* Если периферия простаивает, освобождаем процессор + * до следующего тика таймера. */ + if (vt_is_idle() && + printer_is_idle() && fs_is_idle()) { + check_initial_setup (); + pause (); + } + } +} + +/* + * Операция прерывания 1: внутреннее прерывание. + * Описана в 9-м томе технического описания БЭСМ-6, страница 119. + */ +void op_int_1 (const char *msg) +{ + /*besm6_okno (msg);*/ + M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | + PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); + if (RUU & RUU_RIGHT_INSTR) + M[SPSW] |= SPSW_RIGHT_INSTR; + M[IRET] = PC; + M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE; + if (RUU & RUU_MOD_RK) { + M[SPSW] |= SPSW_MOD_RK; + RUU &= ~RUU_MOD_RK; + } + PC = 0500; + RUU &= ~RUU_RIGHT_INSTR; + RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT); +} + +/* + * Операция прерывания 2: внешнее прерывание. + * Описана в 9-м томе технического описания БЭСМ-6, страница 129. + */ +void op_int_2 () +{ + /*besm6_okno ("Внешнее прерывание");*/ + M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE | + PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU); + M[IRET] = PC; + M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE; + if (RUU & RUU_MOD_RK) { + M[SPSW] |= SPSW_MOD_RK; + RUU &= ~RUU_MOD_RK; + } + PC = 0501; + RUU &= ~RUU_RIGHT_INSTR; + RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT); +} + +/* + * Main instruction fetch/decode loop + */ +t_stat sim_instr (void) +{ + t_stat r; + int iintr = 0; + + /* Restore register state */ + PC = PC & BITS(15); /* mask PC */ + sim_cancel_step (); /* defang SCP step */ + mmu_setup (); /* copy RP to TLB */ + + /* An internal interrupt or user intervention */ + r = setjmp (cpu_halt); + if (r) { + M[017] += corr_stack; + if (cpu_dev.dctrl) { + const char *message = (r >= SCPE_BASE) ? + scp_errors [r - SCPE_BASE] : + sim_stop_messages [r]; + besm6_debug ("/// %05o%s: %s", PC, + (RUU & RUU_RIGHT_INSTR) ? "п" : "л", + message); + } + + /* + * ПоП и ПоК вызывают останов при любом внутреннем прерывании + * или прерывании по контролю, соответственно. + * Если произошёл останов по ПоП или ПоК, + * то продолжение выполнения начнётся с команды, следующей + * за вызвавшей прерывание. Как если бы кнопка "ТП" (тип + * перехода) была включена. Подробнее на странице 119 ТО9. + */ + switch (r) { + default: +ret: besm6_draw_panel(); + return r; + case STOP_RWATCH: + case STOP_WWATCH: + /* Step back one insn to reexecute it */ + if (! (RUU & RUU_RIGHT_INSTR)) { + --PC; + } + RUU ^= RUU_RIGHT_INSTR; + goto ret; + case STOP_BADCMD: + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; + op_int_1 (sim_stop_messages[r]); + // SPSW_NEXT_RK is not important for this interrupt + GRP |= GRP_ILL_INSN; + break; + case STOP_INSN_CHECK: + if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ + goto ret; + op_int_1 (sim_stop_messages[r]); + // SPSW_NEXT_RK must be 0 for this interrupt; it is already + GRP |= GRP_INSN_CHECK; + break; + case STOP_INSN_PROT: + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; + if (RUU & RUU_RIGHT_INSTR) { + ++PC; + } + RUU ^= RUU_RIGHT_INSTR; + op_int_1 (sim_stop_messages[r]); + // SPSW_NEXT_RK must be 1 for this interrupt + M[SPSW] |= SPSW_NEXT_RK; + GRP |= GRP_INSN_PROT; + break; + case STOP_OPERAND_PROT: +#if 0 +/* ДИСПАК держит признак ПоП установленным. + * При запуске СЕРП возникает обращение к чужому листу. */ + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; +#endif + if (RUU & RUU_RIGHT_INSTR) { + ++PC; + } + RUU ^= RUU_RIGHT_INSTR; + op_int_1 (sim_stop_messages[r]); + M[SPSW] |= SPSW_NEXT_RK; + // The offending virtual page is in bits 5-9 + GRP |= GRP_OPRND_PROT; + GRP = GRP_SET_PAGE (GRP, iintr_data); + break; + case STOP_RAM_CHECK: + if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ + goto ret; + op_int_1 (sim_stop_messages[r]); + // The offending interleaved block # is in bits 1-3. + GRP |= GRP_CHECK | GRP_RAM_CHECK; + GRP = GRP_SET_BLOCK (GRP, iintr_data); + break; + case STOP_CACHE_CHECK: + if (M[PSW] & PSW_CHECK_HALT) /* ПоК */ + goto ret; + op_int_1 (sim_stop_messages[r]); + // The offending BRZ # is in bits 1-3. + GRP |= GRP_CHECK; + GRP &= ~GRP_RAM_CHECK; + GRP = GRP_SET_BLOCK (GRP, iintr_data); + break; + case STOP_INSN_ADDR_MATCH: + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; + if (RUU & RUU_RIGHT_INSTR) { + ++PC; + } + RUU ^= RUU_RIGHT_INSTR; + op_int_1 (sim_stop_messages[r]); + M[SPSW] |= SPSW_NEXT_RK; + GRP |= GRP_BREAKPOINT; + break; + case STOP_LOAD_ADDR_MATCH: + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; + if (RUU & RUU_RIGHT_INSTR) { + ++PC; + } + RUU ^= RUU_RIGHT_INSTR; + op_int_1 (sim_stop_messages[r]); + M[SPSW] |= SPSW_NEXT_RK; + GRP |= GRP_WATCHPT_R; + break; + case STOP_STORE_ADDR_MATCH: + if (M[PSW] & PSW_INTR_HALT) /* ПоП */ + goto ret; + if (RUU & RUU_RIGHT_INSTR) { + ++PC; + } + RUU ^= RUU_RIGHT_INSTR; + op_int_1 (sim_stop_messages[r]); + M[SPSW] |= SPSW_NEXT_RK; + GRP |= GRP_WATCHPT_W; + break; + case STOP_OVFL: + /* Прерывание по АУ вызывает останов, если БРО=0 + * и установлен ПоП или ПоК. + * Страница 118 ТО9.*/ + if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */ + ((M[PSW] & PSW_INTR_HALT) || /* ПоП */ + (M[PSW] & PSW_CHECK_HALT))) /* ПоК */ + goto ret; + op_int_1 (sim_stop_messages[r]); + GRP |= GRP_OVERFLOW|GRP_RAM_CHECK; + break; + case STOP_DIVZERO: + if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */ + ((M[PSW] & PSW_INTR_HALT) || /* ПоП */ + (M[PSW] & PSW_CHECK_HALT))) /* ПоК */ + goto ret; + op_int_1 (sim_stop_messages[r]); + GRP |= GRP_DIVZERO|GRP_RAM_CHECK; + break; + } + ++iintr; + } + + if (iintr > 1) { + besm6_draw_panel(); + return STOP_DOUBLE_INTR; + } + /* Main instruction fetch/decode loop */ + for (;;) { + if (sim_interval <= 0) { /* check clock queue */ + r = sim_process_event (); + if (r) { + besm6_draw_panel(); + return r; + } + } + + if (PC > BITS(15)) { /* выход за пределы памяти */ + besm6_draw_panel(); + return STOP_RUNOUT; /* stop simulation */ + } + + if (sim_brk_summ & SWMASK('E') && /* breakpoint? */ + sim_brk_test (PC, SWMASK ('E'))) { + besm6_draw_panel(); + return STOP_IBKPT; /* stop simulation */ + } + + if (PRP & MPRP) { + /* регистр хранящий, сбрасывается программно */ + GRP |= GRP_SLAVE; + } + + if (! iintr && ! (RUU & RUU_RIGHT_INSTR) && + ! (M[PSW] & PSW_INTR_DISABLE) && (GRP & MGRP)) { + /* external interrupt */ + op_int_2(); + } + cpu_one_inst (); /* one instr */ + iintr = 0; + if (redraw_panel) { + besm6_draw_panel(); + redraw_panel = 0; + } + + if (delay < 1) + delay = 1; + sim_interval -= delay; /* count down delay */ + if (sim_step && (--sim_step <= 0)) { /* do step count */ + besm6_draw_panel(); + return SCPE_STOP; + } + } +} + +t_stat slow_clk (UNIT * this) +{ + /*besm6_debug ("*** таймер 80 мсек");*/ + GRP |= GRP_SLOW_CLK; + return sim_activate (this, MSEC*125/2); +} + +/* + * В 9-й части частота таймера 250 Гц (4 мс), + * в жизни - 50 Гц (20 мс). + */ +t_stat fast_clk (UNIT * this) +{ + /*besm6_debug ("*** таймер 20 мсек");*/ + GRP |= GRP_TIMER; + return sim_activate (this, 20*MSEC); +} + +UNIT clocks[] = { + { UDATA(slow_clk, 0, 0) }, /* 10 р, 16 Гц */ + { UDATA(fast_clk, 0, 0) }, /* 40 р, 50 Гц */ +}; + +t_stat clk_reset (DEVICE * dev) +{ + /* Схема автозапуска включается по нереализованной кнопке "МР" */ +#ifdef SOFT_CLOCK + sim_activate (&clocks[0], MSEC*125/2); + return sim_activate (&clocks[1], 20*MSEC); +#else + return SCPE_OK; +#endif +} + +DEVICE clock_dev = { + "CLK", clocks, NULL, NULL, + 2, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG +}; diff --git a/BESM6/besm6_defs.h b/BESM6/besm6_defs.h index d782109a..3da2fa62 100644 --- a/BESM6/besm6_defs.h +++ b/BESM6/besm6_defs.h @@ -1,426 +1,426 @@ -/* - * besm6_defs.h: BESM-6 simulator definitions - * - * Copyright (c) 2009, Serge Vakulenko - * Copyright (c) 2009, Leonid Broukhis - * - * 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. - */ -#ifndef _BESM6_DEFS_H_ -#define _BESM6_DEFS_H_ 0 - -#include "sim_defs.h" /* simulator defns */ -#include - -/* - * Memory. - */ -#define NREGS 30 /* number of registers-modifiers */ -#define MEMSIZE (512 * 1024) /* memory size, words */ - -/* - * Drums and disks. - * - * One zone contains 1024 words of user memory and 8 system data words. - * Every word (t_value) is stored as 8-byte record, low byte first. - * System data is stored first, then user data. - */ -#define ZONE_SIZE (8 + 1024) /* 1kword zone size, words */ -#define DRUM_SIZE (256 * ZONE_SIZE) /* drum size per controller, words */ -#define DISK_SIZE (1024 * ZONE_SIZE) /* disk size per unit, words */ - -/* - * Simulator stop codes - */ -enum { - STOP_STOP = 1, /* STOP */ - STOP_IBKPT, /* SIMH breakpoint */ - STOP_RWATCH, /* SIMH read watchpoint */ - STOP_WWATCH, /* SIMH write watchpoint */ - STOP_RUNOUT, /* run out end of memory limits */ - STOP_BADCMD, /* invalid instruction */ - STOP_INSN_CHECK, /* not an instruction */ - STOP_INSN_PROT, /* fetch from blocked page */ - STOP_OPERAND_PROT, /* load from blocked page */ - STOP_RAM_CHECK, /* RAM parity error */ - STOP_CACHE_CHECK, /* data cache parity error */ - STOP_OVFL, /* arith. overflow */ - STOP_DIVZERO, /* division by 0 or denorm */ - STOP_DOUBLE_INTR, /* double internal interrupt */ - STOP_DRUMINVDATA, /* reading unformatted drum */ - STOP_DISKINVDATA, /* reading unformatted disk */ - STOP_INSN_ADDR_MATCH, /* fetch address matched breakpt reg */ - STOP_LOAD_ADDR_MATCH, /* load address matched watchpt reg */ - STOP_STORE_ADDR_MATCH, /* store address matched watchpt reg */ - STOP_UNIMPLEMENTED, /* unimplemented 033 or 002 insn feature */ -}; - -/* - * Разряды машинного слова, справа налево, начиная с 1. - */ -#define BBIT(n) (1 << (n-1)) /* один бит, от 1 до 32 */ -#define BIT40 000010000000000000LL /* 40-й бит - старший разряд мантиссы */ -#define BIT41 000020000000000000LL /* 41-й бит - знак */ -#define BIT42 000040000000000000LL /* 42-й бит - дубль-знак в мантиссе */ -#define BIT48 004000000000000000LL /* 48-й бит - знак порядка */ -#define BIT49 010000000000000000LL /* бит 49 */ -#define BITS(n) (~0U >> (32-n)) /* маска битов n..1 */ -#define BITS40 00017777777777777LL /* биты 41..1 - мантисса */ -#define BITS41 00037777777777777LL /* биты 41..1 - мантисса и знак */ -#define BITS42 00077777777777777LL /* биты 42..1 - мантисса и оба знака */ -#define BITS48 07777777777777777LL /* биты 48..1 */ -#define BITS48_42 07740000000000000LL /* биты 48..42 - порядок */ -#define ADDR(x) ((x) & BITS(15)) /* адрес слова */ - -/* - * Работа со сверткой. Значение разрядов свертки слова равно значению - * регистров ПКЛ и ПКП при записи слова. - * 00 - командная свертка - * 01 или 10 - контроль числа - * 11 - числовая свертка - * В памяти биты свертки имитируют четность полуслов. - */ -#define CONVOL_INSN 1 -#define CONVOL_NUMBER 2 -#define SET_CONVOL(x, c) (((x) & BITS48) | (((c) & 3LL) << 48)) -#define IS_INSN(x) (((x) >> 48) == CONVOL_INSN) -#define IS_NUMBER(x) (((x) >> 48) == CONVOL_INSN || \ - ((x) >> 48) == CONVOL_NUMBER) - -/* - * Вычисление правдоподобного времени выполнения команды, - * зная количество тактов в УУ и среднее в АУ. - * Предполагаем, что в 50% случаев происходит совмещение - * выполнения, поэтому суммируем большее и половину - * от меньшего значения. - */ -#define MEAN_TIME(x,y) (x>y ? x+y/2 : x/2+y) - -/* - * Считаем, что моделируеммая машина имеет опорную частоту 10 МГц. - */ -#define USEC 10 /* одна микросекунда - десять тактов */ -#define MSEC (1000*USEC) /* одна миллисекунда */ - -extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ -extern int32 sim_interval, sim_step; -extern FILE *sim_deb, *sim_log; -extern int32 sim_switches; - -extern UNIT cpu_unit; -extern t_value memory [MEMSIZE]; -extern t_value pult [8]; -extern uint32 PC, RAU, RUU; -extern uint32 M[NREGS]; -extern t_value BRZ[8], RP[8], GRP, MGRP; -extern uint32 PRP, MPRP; -extern t_value ACC, RMR; -extern uint32 BAZ[8], TABST, RZ; -extern uint32 READY; /* read by ext 4031 */ -extern uint32 READY2; /* read by ext 4102 */ -extern DEVICE cpu_dev, drum_dev, mmu_dev, disk_dev; -extern DEVICE clock_dev; -extern DEVICE printer_dev; -extern DEVICE tty_dev; -extern DEVICE fs_dev; -extern jmp_buf cpu_halt; - -/* - * Разряды режима АУ. - */ -#define RAU_NORM_DISABLE 001 /* блокировка нормализации */ -#define RAU_ROUND_DISABLE 002 /* блокировка округления */ -#define RAU_LOG 004 /* признак логической группы */ -#define RAU_MULT 010 /* признак группы умножения */ -#define RAU_ADD 020 /* признак группы слодения */ -#define RAU_OVF_DISABLE 040 /* блокировка переполнения */ - -#define RAU_MODE (RAU_LOG | RAU_MULT | RAU_ADD) -#define SET_MODE(x,m) (((x) & ~RAU_MODE) | (m)) -#define SET_LOGICAL(x) (((x) & ~RAU_MODE) | RAU_LOG) -#define SET_MULTIPLICATIVE(x) (((x) & ~RAU_MODE) | RAU_MULT) -#define SET_ADDITIVE(x) (((x) & ~RAU_MODE) | RAU_ADD) -#define IS_LOGICAL(x) (((x) & RAU_MODE) == RAU_LOG) -#define IS_MULTIPLICATIVE(x) (((x) & (RAU_ADD | RAU_MULT)) == RAU_MULT) -#define IS_ADDITIVE(x) ((x) & RAU_ADD) - -/* - * Искусственный регистр режимов УУ, в реальной машине отсутствует. - */ -#define RUU_CONVOL_RIGHT 000001 /* ПКП - признак контроля правой половины */ -#define RUU_CONVOL_LEFT 000002 /* ПКЛ - признак контроля левой половины */ -#define RUU_EXTRACODE 000004 /* РежЭ - режим экстракода */ -#define RUU_INTERRUPT 000010 /* РежПр - режим прерывания */ -#define RUU_MOD_RK 000020 /* ПрИК - модификация регистром М[16] */ -#define RUU_AVOST_DISABLE 000040 /* БРО - блокировка режима останова */ -#define RUU_RIGHT_INSTR 000400 /* ПрК - признак правой команды */ - -#define IS_SUPERVISOR(x) ((x) & (RUU_EXTRACODE | RUU_INTERRUPT)) -#define SET_SUPERVISOR(x,m) (((x) & ~(RUU_EXTRACODE | RUU_INTERRUPT)) | (m)) - -/* - * Специальные регистры. - */ -#define MOD 020 /* модификатор адреса */ -#define PSW 021 /* режимы УУ */ -#define SPSW 027 /* упрятывание режимов УУ */ -#define ERET 032 /* адрес возврата из экстракода */ -#define IRET 033 /* адрес возврата из прерывания */ -#define IBP 034 /* адрес прерывания по выполнению */ -#define DWP 035 /* адрес прерывания по чтению/записи */ - -/* - * Регистр 021: режимы УУ. - * PSW: program status word. - */ -#define PSW_MMAP_DISABLE 000001 /* БлП - блокировка приписки */ -#define PSW_PROT_DISABLE 000002 /* БлЗ - блокировка защиты */ -#define PSW_INTR_HALT 000004 /* ПоП - признак останова при - любом внутреннем прерывании */ -#define PSW_CHECK_HALT 000010 /* ПоК - признак останова при - прерывании по контролю */ -#define PSW_WRITE_WATCH 000020 /* Зп(М29) - признак совпадения адреса - операнда прии записи в память - с содержанием регистра М29 */ -#define PSW_INTR_DISABLE 002000 /* БлПр - блокировка внешнего прерывания */ -#define PSW_AUT_B 004000 /* АвтБ - признак режима Автомат Б */ - -/* - * Регистр 027: сохранённые режимы УУ. - * SPSW: saved program status word. - */ -#define SPSW_MMAP_DISABLE 000001 /* БлП - блокировка приписки */ -#define SPSW_PROT_DISABLE 000002 /* БлЗ - блокировка защиты */ -#define SPSW_EXTRACODE 000004 /* РежЭ - режим экстракода */ -#define SPSW_INTERRUPT 000010 /* РежПр - режим прерывания */ -#define SPSW_MOD_RK 000020 /* ПрИК(РК) - на регистр РК принята - команда, которая должна быть - модифицирована регистром М[16] */ -#define SPSW_MOD_RR 000040 /* ПрИК(РР) - на регистре РР находится - команда, выполненная с модификацией */ -#define SPSW_UNKNOWN 000100 /* НОК? вписано карандашом в 9 томе */ -#define SPSW_RIGHT_INSTR 000400 /* ПрК - признак правой команды */ -#define SPSW_NEXT_RK 001000 /* ГД./ДК2 - на регистр РК принята - команда, следующая после вызвавшей - прерывание */ -#define SPSW_INTR_DISABLE 002000 /* БлПр - блокировка внешнего прерывания */ - -/* - * Кириллица Unicode. - */ -#define CYRILLIC_CAPITAL_LETTER_A 0x0410 -#define CYRILLIC_CAPITAL_LETTER_BE 0x0411 -#define CYRILLIC_CAPITAL_LETTER_VE 0x0412 -#define CYRILLIC_CAPITAL_LETTER_GHE 0x0413 -#define CYRILLIC_CAPITAL_LETTER_DE 0x0414 -#define CYRILLIC_CAPITAL_LETTER_IE 0x0415 -#define CYRILLIC_CAPITAL_LETTER_ZHE 0x0416 -#define CYRILLIC_CAPITAL_LETTER_ZE 0x0417 -#define CYRILLIC_CAPITAL_LETTER_I 0x0418 -#define CYRILLIC_CAPITAL_LETTER_SHORT_I 0x0419 -#define CYRILLIC_CAPITAL_LETTER_KA 0x041a -#define CYRILLIC_CAPITAL_LETTER_EL 0x041b -#define CYRILLIC_CAPITAL_LETTER_EM 0x041c -#define CYRILLIC_CAPITAL_LETTER_EN 0x041d -#define CYRILLIC_CAPITAL_LETTER_O 0x041e -#define CYRILLIC_CAPITAL_LETTER_PE 0x041f -#define CYRILLIC_CAPITAL_LETTER_ER 0x0420 -#define CYRILLIC_CAPITAL_LETTER_ES 0x0421 -#define CYRILLIC_CAPITAL_LETTER_TE 0x0422 -#define CYRILLIC_CAPITAL_LETTER_U 0x0423 -#define CYRILLIC_CAPITAL_LETTER_EF 0x0424 -#define CYRILLIC_CAPITAL_LETTER_HA 0x0425 -#define CYRILLIC_CAPITAL_LETTER_TSE 0x0426 -#define CYRILLIC_CAPITAL_LETTER_CHE 0x0427 -#define CYRILLIC_CAPITAL_LETTER_SHA 0x0428 -#define CYRILLIC_CAPITAL_LETTER_SHCHA 0x0429 -#define CYRILLIC_CAPITAL_LETTER_HARD_SIGN 0x042a -#define CYRILLIC_CAPITAL_LETTER_YERU 0x042b -#define CYRILLIC_CAPITAL_LETTER_SOFT_SIGN 0x042c -#define CYRILLIC_CAPITAL_LETTER_E 0x042d -#define CYRILLIC_CAPITAL_LETTER_YU 0x042e -#define CYRILLIC_CAPITAL_LETTER_YA 0x042f -#define CYRILLIC_SMALL_LETTER_A 0x0430 -#define CYRILLIC_SMALL_LETTER_BE 0x0431 -#define CYRILLIC_SMALL_LETTER_VE 0x0432 -#define CYRILLIC_SMALL_LETTER_GHE 0x0433 -#define CYRILLIC_SMALL_LETTER_DE 0x0434 -#define CYRILLIC_SMALL_LETTER_IE 0x0435 -#define CYRILLIC_SMALL_LETTER_ZHE 0x0436 -#define CYRILLIC_SMALL_LETTER_ZE 0x0437 -#define CYRILLIC_SMALL_LETTER_I 0x0438 -#define CYRILLIC_SMALL_LETTER_SHORT_I 0x0439 -#define CYRILLIC_SMALL_LETTER_KA 0x043a -#define CYRILLIC_SMALL_LETTER_EL 0x043b -#define CYRILLIC_SMALL_LETTER_EM 0x043c -#define CYRILLIC_SMALL_LETTER_EN 0x043d -#define CYRILLIC_SMALL_LETTER_O 0x043e -#define CYRILLIC_SMALL_LETTER_PE 0x043f -#define CYRILLIC_SMALL_LETTER_ER 0x0440 -#define CYRILLIC_SMALL_LETTER_ES 0x0441 -#define CYRILLIC_SMALL_LETTER_TE 0x0442 -#define CYRILLIC_SMALL_LETTER_U 0x0443 -#define CYRILLIC_SMALL_LETTER_EF 0x0444 -#define CYRILLIC_SMALL_LETTER_HA 0x0445 -#define CYRILLIC_SMALL_LETTER_TSE 0x0446 -#define CYRILLIC_SMALL_LETTER_CHE 0x0447 -#define CYRILLIC_SMALL_LETTER_SHA 0x0448 -#define CYRILLIC_SMALL_LETTER_SHCHA 0x0449 -#define CYRILLIC_SMALL_LETTER_HARD_SIGN 0x044a -#define CYRILLIC_SMALL_LETTER_YERU 0x044b -#define CYRILLIC_SMALL_LETTER_SOFT_SIGN 0x044c -#define CYRILLIC_SMALL_LETTER_E 0x044d -#define CYRILLIC_SMALL_LETTER_YU 0x044e -#define CYRILLIC_SMALL_LETTER_YA 0x044f - -/* - * Процедуры работы с памятью - */ -extern void mmu_store (int addr, t_value word); -extern t_value mmu_load (int addr); -extern t_value mmu_fetch (int addr); -extern t_value mmu_prefetch (int addr, int actual); -extern void mmu_setcache (int idx, t_value word); -extern t_value mmu_getcache (int idx); -extern void mmu_setrp (int idx, t_value word); -extern void mmu_setup (void); -extern void mmu_setprotection (int idx, t_value word); -extern void mmu_print_brz (void); - -/* - * Выполнение обращения к барабану. - */ -void drum (int ctlr, uint32 cmd); -int drum_errors (void); - -/* - * Обращение к дискам. - */ -void disk_io (int ctlr, uint32 cmd); -void disk_ctl (int ctlr, uint32 cmd); -int disk_state (int ctlr); -int disk_errors (void); - -/* - * Печать на АЦПУ. - */ -void printer_control (int num, uint32 cmd); -void printer_hammer (int num, int pos, uint32 mask); -int printer_is_idle (void); - -/* - * Терминалы (телетайпы, видеотоны, "Консулы"). - */ -void tty_send (uint32 mask); -int tty_query (void); -void vt_print (void); -void vt_receive (void); -void consul_print (int num, uint32 cmd); -uint32 consul_read (int num); -int vt_is_idle (void); - -/* - * Ввод с перфоленты. - */ -void fs_control (int num, uint32 cmd); -int fs_read (int num); -int fs_is_idle (void); - -/* - * Отладочная выдача. - */ -void besm6_fprint_cmd (FILE *of, uint32 cmd); -void besm6_log (const char *fmt, ...); -void besm6_log_cont (const char *fmt, ...); -void besm6_debug (const char *fmt, ...); -t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, - UNIT *uptr, int32 sw); -void besm6_draw_panel (void); - -/* - * Арифметика. - */ -double besm6_to_ieee (t_value word); -void besm6_add (t_value val, int negate_acc, int negate_val); -void besm6_divide (t_value val); -void besm6_multiply (t_value val); -void besm6_change_sign (int sign); -void besm6_add_exponent (int val); -int besm6_highest_bit (t_value val); -void besm6_shift (int toright); -int besm6_count_ones (t_value word); -t_value besm6_pack (t_value val, t_value mask); -t_value besm6_unpack (t_value val, t_value mask); - -/* - * Разряды главного регистра прерываний (ГРП) - * Внешние: - */ -#define GRP_PRN1_SYNC 04000000000000000LL /* 48 */ -#define GRP_PRN2_SYNC 02000000000000000LL /* 47 */ -#define GRP_DRUM1_FREE 01000000000000000LL /* 46 */ -#define GRP_DRUM2_FREE 00400000000000000LL /* 45 */ -#define GRP_VNIIEM 00300000000000000LL /* 44-43, placeholder */ -#define GRP_FS1_SYNC 00040000000000000LL /* 42 */ -#define GRP_FS2_SYNC 00020000000000000LL /* 41 */ -#define GRP_TIMER 00010000000000000LL /* 40 */ -#define GRP_PRN1_ZERO 00004000000000000LL /* 39 */ -#define GRP_PRN2_ZERO 00002000000000000LL /* 38 */ -#define GRP_SLAVE 00001000000000000LL /* 37 */ -#define GRP_CHAN3_DONE 00000400000000000LL /* 36 */ -#define GRP_CHAN4_DONE 00000200000000000LL /* 35 */ -#define GRP_CHAN5_DONE 00000100000000000LL /* 34 */ -#define GRP_CHAN6_DONE 00000040000000000LL /* 33 */ -#define GRP_PANEL_REQ 00000020000000000LL /* 32 */ -#define GRP_TTY_START 00000010000000000LL /* 31 */ -#define GRP_IMITATION 00000004000000000LL /* 30 */ -#define GRP_CHAN3_FREE 00000002000000000LL /* 29 */ -#define GRP_CHAN4_FREE 00000001000000000LL /* 28 */ -#define GRP_CHAN5_FREE 00000000400000000LL /* 27 */ -#define GRP_CHAN6_FREE 00000000200000000LL /* 26 */ -#define GRP_CHAN7_FREE 00000000100000000LL /* 25 */ -#define GRP_WATCHDOG 00000000000002000LL /* 11 */ -#define GRP_SLOW_CLK 00000000000001000LL /* 10 */ -/* Внутренние: */ -#define GRP_DIVZERO 00000000034000000LL /* 23-21 */ -#define GRP_OVERFLOW 00000000014000000LL /* 22-21 */ -#define GRP_CHECK 00000000004000000LL /* 21 */ -#define GRP_OPRND_PROT 00000000002000000LL /* 20 */ -#define GRP_WATCHPT_W 00000000000200000LL /* 17 */ -#define GRP_WATCHPT_R 00000000000100000LL /* 16 */ -#define GRP_INSN_CHECK 00000000000040000LL /* 15 */ -#define GRP_INSN_PROT 00000000000020000LL /* 14 */ -#define GRP_ILL_INSN 00000000000010000LL /* 13 */ -#define GRP_BREAKPOINT 00000000000004000LL /* 12 */ -#define GRP_PAGE_MASK 00000000000000760LL /* 9-5 */ -#define GRP_RAM_CHECK 00000000000000010LL /* 4 */ -#define GRP_BLOCK_MASK 00000000000000007LL /* 3-1 */ - -#define GRP_SET_BLOCK(x,m) (((x) & ~GRP_BLOCK_MASK) | ((m) & GRP_BLOCK_MASK)) -#define GRP_SET_PAGE(x,m) (((x) & ~GRP_PAGE_MASK) | (((m)<<4) & GRP_PAGE_MASK)) - -/* Номер блока ОЗУ или номер страницы, вызвавших прерывание */ -extern uint32 iintr_data; - -#endif +/* + * besm6_defs.h: BESM-6 simulator definitions + * + * Copyright (c) 2009, Serge Vakulenko + * Copyright (c) 2009, Leonid Broukhis + * + * 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. + */ +#ifndef _BESM6_DEFS_H_ +#define _BESM6_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ +#include + +/* + * Memory. + */ +#define NREGS 30 /* number of registers-modifiers */ +#define MEMSIZE (512 * 1024) /* memory size, words */ + +/* + * Drums and disks. + * + * One zone contains 1024 words of user memory and 8 system data words. + * Every word (t_value) is stored as 8-byte record, low byte first. + * System data is stored first, then user data. + */ +#define ZONE_SIZE (8 + 1024) /* 1kword zone size, words */ +#define DRUM_SIZE (256 * ZONE_SIZE) /* drum size per controller, words */ +#define DISK_SIZE (1024 * ZONE_SIZE) /* disk size per unit, words */ + +/* + * Simulator stop codes + */ +enum { + STOP_STOP = 1, /* STOP */ + STOP_IBKPT, /* SIMH breakpoint */ + STOP_RWATCH, /* SIMH read watchpoint */ + STOP_WWATCH, /* SIMH write watchpoint */ + STOP_RUNOUT, /* run out end of memory limits */ + STOP_BADCMD, /* invalid instruction */ + STOP_INSN_CHECK, /* not an instruction */ + STOP_INSN_PROT, /* fetch from blocked page */ + STOP_OPERAND_PROT, /* load from blocked page */ + STOP_RAM_CHECK, /* RAM parity error */ + STOP_CACHE_CHECK, /* data cache parity error */ + STOP_OVFL, /* arith. overflow */ + STOP_DIVZERO, /* division by 0 or denorm */ + STOP_DOUBLE_INTR, /* double internal interrupt */ + STOP_DRUMINVDATA, /* reading unformatted drum */ + STOP_DISKINVDATA, /* reading unformatted disk */ + STOP_INSN_ADDR_MATCH, /* fetch address matched breakpt reg */ + STOP_LOAD_ADDR_MATCH, /* load address matched watchpt reg */ + STOP_STORE_ADDR_MATCH, /* store address matched watchpt reg */ + STOP_UNIMPLEMENTED, /* unimplemented 033 or 002 insn feature */ +}; + +/* + * Разряды машинного слова, справа налево, начиная с 1. + */ +#define BBIT(n) (1 << (n-1)) /* один бит, от 1 до 32 */ +#define BIT40 000010000000000000LL /* 40-й бит - старший разряд мантиссы */ +#define BIT41 000020000000000000LL /* 41-й бит - знак */ +#define BIT42 000040000000000000LL /* 42-й бит - дубль-знак в мантиссе */ +#define BIT48 004000000000000000LL /* 48-й бит - знак порядка */ +#define BIT49 010000000000000000LL /* бит 49 */ +#define BITS(n) (~0U >> (32-n)) /* маска битов n..1 */ +#define BITS40 00017777777777777LL /* биты 41..1 - мантисса */ +#define BITS41 00037777777777777LL /* биты 41..1 - мантисса и знак */ +#define BITS42 00077777777777777LL /* биты 42..1 - мантисса и оба знака */ +#define BITS48 07777777777777777LL /* биты 48..1 */ +#define BITS48_42 07740000000000000LL /* биты 48..42 - порядок */ +#define ADDR(x) ((x) & BITS(15)) /* адрес слова */ + +/* + * Работа со сверткой. Значение разрядов свертки слова равно значению + * регистров ПКЛ и ПКП при записи слова. + * 00 - командная свертка + * 01 или 10 - контроль числа + * 11 - числовая свертка + * В памяти биты свертки имитируют четность полуслов. + */ +#define CONVOL_INSN 1 +#define CONVOL_NUMBER 2 +#define SET_CONVOL(x, c) (((x) & BITS48) | (((c) & 3LL) << 48)) +#define IS_INSN(x) (((x) >> 48) == CONVOL_INSN) +#define IS_NUMBER(x) (((x) >> 48) == CONVOL_INSN || \ + ((x) >> 48) == CONVOL_NUMBER) + +/* + * Вычисление правдоподобного времени выполнения команды, + * зная количество тактов в УУ и среднее в АУ. + * Предполагаем, что в 50% случаев происходит совмещение + * выполнения, поэтому суммируем большее и половину + * от меньшего значения. + */ +#define MEAN_TIME(x,y) (x>y ? x+y/2 : x/2+y) + +/* + * Считаем, что моделируеммая машина имеет опорную частоту 10 МГц. + */ +#define USEC 10 /* одна микросекунда - десять тактов */ +#define MSEC (1000*USEC) /* одна миллисекунда */ + +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern int32 sim_interval, sim_step; +extern FILE *sim_deb, *sim_log; +extern int32 sim_switches; + +extern UNIT cpu_unit; +extern t_value memory [MEMSIZE]; +extern t_value pult [8]; +extern uint32 PC, RAU, RUU; +extern uint32 M[NREGS]; +extern t_value BRZ[8], RP[8], GRP, MGRP; +extern uint32 PRP, MPRP; +extern t_value ACC, RMR; +extern uint32 BAZ[8], TABST, RZ; +extern uint32 READY; /* read by ext 4031 */ +extern uint32 READY2; /* read by ext 4102 */ +extern DEVICE cpu_dev, drum_dev, mmu_dev, disk_dev; +extern DEVICE clock_dev; +extern DEVICE printer_dev; +extern DEVICE tty_dev; +extern DEVICE fs_dev; +extern jmp_buf cpu_halt; + +/* + * Разряды режима АУ. + */ +#define RAU_NORM_DISABLE 001 /* блокировка нормализации */ +#define RAU_ROUND_DISABLE 002 /* блокировка округления */ +#define RAU_LOG 004 /* признак логической группы */ +#define RAU_MULT 010 /* признак группы умножения */ +#define RAU_ADD 020 /* признак группы слодения */ +#define RAU_OVF_DISABLE 040 /* блокировка переполнения */ + +#define RAU_MODE (RAU_LOG | RAU_MULT | RAU_ADD) +#define SET_MODE(x,m) (((x) & ~RAU_MODE) | (m)) +#define SET_LOGICAL(x) (((x) & ~RAU_MODE) | RAU_LOG) +#define SET_MULTIPLICATIVE(x) (((x) & ~RAU_MODE) | RAU_MULT) +#define SET_ADDITIVE(x) (((x) & ~RAU_MODE) | RAU_ADD) +#define IS_LOGICAL(x) (((x) & RAU_MODE) == RAU_LOG) +#define IS_MULTIPLICATIVE(x) (((x) & (RAU_ADD | RAU_MULT)) == RAU_MULT) +#define IS_ADDITIVE(x) ((x) & RAU_ADD) + +/* + * Искусственный регистр режимов УУ, в реальной машине отсутствует. + */ +#define RUU_CONVOL_RIGHT 000001 /* ПКП - признак контроля правой половины */ +#define RUU_CONVOL_LEFT 000002 /* ПКЛ - признак контроля левой половины */ +#define RUU_EXTRACODE 000004 /* РежЭ - режим экстракода */ +#define RUU_INTERRUPT 000010 /* РежПр - режим прерывания */ +#define RUU_MOD_RK 000020 /* ПрИК - модификация регистром М[16] */ +#define RUU_AVOST_DISABLE 000040 /* БРО - блокировка режима останова */ +#define RUU_RIGHT_INSTR 000400 /* ПрК - признак правой команды */ + +#define IS_SUPERVISOR(x) ((x) & (RUU_EXTRACODE | RUU_INTERRUPT)) +#define SET_SUPERVISOR(x,m) (((x) & ~(RUU_EXTRACODE | RUU_INTERRUPT)) | (m)) + +/* + * Специальные регистры. + */ +#define MOD 020 /* модификатор адреса */ +#define PSW 021 /* режимы УУ */ +#define SPSW 027 /* упрятывание режимов УУ */ +#define ERET 032 /* адрес возврата из экстракода */ +#define IRET 033 /* адрес возврата из прерывания */ +#define IBP 034 /* адрес прерывания по выполнению */ +#define DWP 035 /* адрес прерывания по чтению/записи */ + +/* + * Регистр 021: режимы УУ. + * PSW: program status word. + */ +#define PSW_MMAP_DISABLE 000001 /* БлП - блокировка приписки */ +#define PSW_PROT_DISABLE 000002 /* БлЗ - блокировка защиты */ +#define PSW_INTR_HALT 000004 /* ПоП - признак останова при + любом внутреннем прерывании */ +#define PSW_CHECK_HALT 000010 /* ПоК - признак останова при + прерывании по контролю */ +#define PSW_WRITE_WATCH 000020 /* Зп(М29) - признак совпадения адреса + операнда прии записи в память + с содержанием регистра М29 */ +#define PSW_INTR_DISABLE 002000 /* БлПр - блокировка внешнего прерывания */ +#define PSW_AUT_B 004000 /* АвтБ - признак режима Автомат Б */ + +/* + * Регистр 027: сохранённые режимы УУ. + * SPSW: saved program status word. + */ +#define SPSW_MMAP_DISABLE 000001 /* БлП - блокировка приписки */ +#define SPSW_PROT_DISABLE 000002 /* БлЗ - блокировка защиты */ +#define SPSW_EXTRACODE 000004 /* РежЭ - режим экстракода */ +#define SPSW_INTERRUPT 000010 /* РежПр - режим прерывания */ +#define SPSW_MOD_RK 000020 /* ПрИК(РК) - на регистр РК принята + команда, которая должна быть + модифицирована регистром М[16] */ +#define SPSW_MOD_RR 000040 /* ПрИК(РР) - на регистре РР находится + команда, выполненная с модификацией */ +#define SPSW_UNKNOWN 000100 /* НОК? вписано карандашом в 9 томе */ +#define SPSW_RIGHT_INSTR 000400 /* ПрК - признак правой команды */ +#define SPSW_NEXT_RK 001000 /* ГД./ДК2 - на регистр РК принята + команда, следующая после вызвавшей + прерывание */ +#define SPSW_INTR_DISABLE 002000 /* БлПр - блокировка внешнего прерывания */ + +/* + * Кириллица Unicode. + */ +#define CYRILLIC_CAPITAL_LETTER_A 0x0410 +#define CYRILLIC_CAPITAL_LETTER_BE 0x0411 +#define CYRILLIC_CAPITAL_LETTER_VE 0x0412 +#define CYRILLIC_CAPITAL_LETTER_GHE 0x0413 +#define CYRILLIC_CAPITAL_LETTER_DE 0x0414 +#define CYRILLIC_CAPITAL_LETTER_IE 0x0415 +#define CYRILLIC_CAPITAL_LETTER_ZHE 0x0416 +#define CYRILLIC_CAPITAL_LETTER_ZE 0x0417 +#define CYRILLIC_CAPITAL_LETTER_I 0x0418 +#define CYRILLIC_CAPITAL_LETTER_SHORT_I 0x0419 +#define CYRILLIC_CAPITAL_LETTER_KA 0x041a +#define CYRILLIC_CAPITAL_LETTER_EL 0x041b +#define CYRILLIC_CAPITAL_LETTER_EM 0x041c +#define CYRILLIC_CAPITAL_LETTER_EN 0x041d +#define CYRILLIC_CAPITAL_LETTER_O 0x041e +#define CYRILLIC_CAPITAL_LETTER_PE 0x041f +#define CYRILLIC_CAPITAL_LETTER_ER 0x0420 +#define CYRILLIC_CAPITAL_LETTER_ES 0x0421 +#define CYRILLIC_CAPITAL_LETTER_TE 0x0422 +#define CYRILLIC_CAPITAL_LETTER_U 0x0423 +#define CYRILLIC_CAPITAL_LETTER_EF 0x0424 +#define CYRILLIC_CAPITAL_LETTER_HA 0x0425 +#define CYRILLIC_CAPITAL_LETTER_TSE 0x0426 +#define CYRILLIC_CAPITAL_LETTER_CHE 0x0427 +#define CYRILLIC_CAPITAL_LETTER_SHA 0x0428 +#define CYRILLIC_CAPITAL_LETTER_SHCHA 0x0429 +#define CYRILLIC_CAPITAL_LETTER_HARD_SIGN 0x042a +#define CYRILLIC_CAPITAL_LETTER_YERU 0x042b +#define CYRILLIC_CAPITAL_LETTER_SOFT_SIGN 0x042c +#define CYRILLIC_CAPITAL_LETTER_E 0x042d +#define CYRILLIC_CAPITAL_LETTER_YU 0x042e +#define CYRILLIC_CAPITAL_LETTER_YA 0x042f +#define CYRILLIC_SMALL_LETTER_A 0x0430 +#define CYRILLIC_SMALL_LETTER_BE 0x0431 +#define CYRILLIC_SMALL_LETTER_VE 0x0432 +#define CYRILLIC_SMALL_LETTER_GHE 0x0433 +#define CYRILLIC_SMALL_LETTER_DE 0x0434 +#define CYRILLIC_SMALL_LETTER_IE 0x0435 +#define CYRILLIC_SMALL_LETTER_ZHE 0x0436 +#define CYRILLIC_SMALL_LETTER_ZE 0x0437 +#define CYRILLIC_SMALL_LETTER_I 0x0438 +#define CYRILLIC_SMALL_LETTER_SHORT_I 0x0439 +#define CYRILLIC_SMALL_LETTER_KA 0x043a +#define CYRILLIC_SMALL_LETTER_EL 0x043b +#define CYRILLIC_SMALL_LETTER_EM 0x043c +#define CYRILLIC_SMALL_LETTER_EN 0x043d +#define CYRILLIC_SMALL_LETTER_O 0x043e +#define CYRILLIC_SMALL_LETTER_PE 0x043f +#define CYRILLIC_SMALL_LETTER_ER 0x0440 +#define CYRILLIC_SMALL_LETTER_ES 0x0441 +#define CYRILLIC_SMALL_LETTER_TE 0x0442 +#define CYRILLIC_SMALL_LETTER_U 0x0443 +#define CYRILLIC_SMALL_LETTER_EF 0x0444 +#define CYRILLIC_SMALL_LETTER_HA 0x0445 +#define CYRILLIC_SMALL_LETTER_TSE 0x0446 +#define CYRILLIC_SMALL_LETTER_CHE 0x0447 +#define CYRILLIC_SMALL_LETTER_SHA 0x0448 +#define CYRILLIC_SMALL_LETTER_SHCHA 0x0449 +#define CYRILLIC_SMALL_LETTER_HARD_SIGN 0x044a +#define CYRILLIC_SMALL_LETTER_YERU 0x044b +#define CYRILLIC_SMALL_LETTER_SOFT_SIGN 0x044c +#define CYRILLIC_SMALL_LETTER_E 0x044d +#define CYRILLIC_SMALL_LETTER_YU 0x044e +#define CYRILLIC_SMALL_LETTER_YA 0x044f + +/* + * Процедуры работы с памятью + */ +extern void mmu_store (int addr, t_value word); +extern t_value mmu_load (int addr); +extern t_value mmu_fetch (int addr); +extern t_value mmu_prefetch (int addr, int actual); +extern void mmu_setcache (int idx, t_value word); +extern t_value mmu_getcache (int idx); +extern void mmu_setrp (int idx, t_value word); +extern void mmu_setup (void); +extern void mmu_setprotection (int idx, t_value word); +extern void mmu_print_brz (void); + +/* + * Выполнение обращения к барабану. + */ +void drum (int ctlr, uint32 cmd); +int drum_errors (void); + +/* + * Обращение к дискам. + */ +void disk_io (int ctlr, uint32 cmd); +void disk_ctl (int ctlr, uint32 cmd); +int disk_state (int ctlr); +int disk_errors (void); + +/* + * Печать на АЦПУ. + */ +void printer_control (int num, uint32 cmd); +void printer_hammer (int num, int pos, uint32 mask); +int printer_is_idle (void); + +/* + * Терминалы (телетайпы, видеотоны, "Консулы"). + */ +void tty_send (uint32 mask); +int tty_query (void); +void vt_print (void); +void vt_receive (void); +void consul_print (int num, uint32 cmd); +uint32 consul_read (int num); +int vt_is_idle (void); + +/* + * Ввод с перфоленты. + */ +void fs_control (int num, uint32 cmd); +int fs_read (int num); +int fs_is_idle (void); + +/* + * Отладочная выдача. + */ +void besm6_fprint_cmd (FILE *of, uint32 cmd); +void besm6_log (const char *fmt, ...); +void besm6_log_cont (const char *fmt, ...); +void besm6_debug (const char *fmt, ...); +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +void besm6_draw_panel (void); + +/* + * Арифметика. + */ +double besm6_to_ieee (t_value word); +void besm6_add (t_value val, int negate_acc, int negate_val); +void besm6_divide (t_value val); +void besm6_multiply (t_value val); +void besm6_change_sign (int sign); +void besm6_add_exponent (int val); +int besm6_highest_bit (t_value val); +void besm6_shift (int toright); +int besm6_count_ones (t_value word); +t_value besm6_pack (t_value val, t_value mask); +t_value besm6_unpack (t_value val, t_value mask); + +/* + * Разряды главного регистра прерываний (ГРП) + * Внешние: + */ +#define GRP_PRN1_SYNC 04000000000000000LL /* 48 */ +#define GRP_PRN2_SYNC 02000000000000000LL /* 47 */ +#define GRP_DRUM1_FREE 01000000000000000LL /* 46 */ +#define GRP_DRUM2_FREE 00400000000000000LL /* 45 */ +#define GRP_VNIIEM 00300000000000000LL /* 44-43, placeholder */ +#define GRP_FS1_SYNC 00040000000000000LL /* 42 */ +#define GRP_FS2_SYNC 00020000000000000LL /* 41 */ +#define GRP_TIMER 00010000000000000LL /* 40 */ +#define GRP_PRN1_ZERO 00004000000000000LL /* 39 */ +#define GRP_PRN2_ZERO 00002000000000000LL /* 38 */ +#define GRP_SLAVE 00001000000000000LL /* 37 */ +#define GRP_CHAN3_DONE 00000400000000000LL /* 36 */ +#define GRP_CHAN4_DONE 00000200000000000LL /* 35 */ +#define GRP_CHAN5_DONE 00000100000000000LL /* 34 */ +#define GRP_CHAN6_DONE 00000040000000000LL /* 33 */ +#define GRP_PANEL_REQ 00000020000000000LL /* 32 */ +#define GRP_TTY_START 00000010000000000LL /* 31 */ +#define GRP_IMITATION 00000004000000000LL /* 30 */ +#define GRP_CHAN3_FREE 00000002000000000LL /* 29 */ +#define GRP_CHAN4_FREE 00000001000000000LL /* 28 */ +#define GRP_CHAN5_FREE 00000000400000000LL /* 27 */ +#define GRP_CHAN6_FREE 00000000200000000LL /* 26 */ +#define GRP_CHAN7_FREE 00000000100000000LL /* 25 */ +#define GRP_WATCHDOG 00000000000002000LL /* 11 */ +#define GRP_SLOW_CLK 00000000000001000LL /* 10 */ +/* Внутренние: */ +#define GRP_DIVZERO 00000000034000000LL /* 23-21 */ +#define GRP_OVERFLOW 00000000014000000LL /* 22-21 */ +#define GRP_CHECK 00000000004000000LL /* 21 */ +#define GRP_OPRND_PROT 00000000002000000LL /* 20 */ +#define GRP_WATCHPT_W 00000000000200000LL /* 17 */ +#define GRP_WATCHPT_R 00000000000100000LL /* 16 */ +#define GRP_INSN_CHECK 00000000000040000LL /* 15 */ +#define GRP_INSN_PROT 00000000000020000LL /* 14 */ +#define GRP_ILL_INSN 00000000000010000LL /* 13 */ +#define GRP_BREAKPOINT 00000000000004000LL /* 12 */ +#define GRP_PAGE_MASK 00000000000000760LL /* 9-5 */ +#define GRP_RAM_CHECK 00000000000000010LL /* 4 */ +#define GRP_BLOCK_MASK 00000000000000007LL /* 3-1 */ + +#define GRP_SET_BLOCK(x,m) (((x) & ~GRP_BLOCK_MASK) | ((m) & GRP_BLOCK_MASK)) +#define GRP_SET_PAGE(x,m) (((x) & ~GRP_PAGE_MASK) | (((m)<<4) & GRP_PAGE_MASK)) + +/* Номер блока ОЗУ или номер страницы, вызвавших прерывание */ +extern uint32 iintr_data; + +#endif diff --git a/BESM6/besm6_disk.c b/BESM6/besm6_disk.c index 37fbd23c..1ff0ed32 100644 --- a/BESM6/besm6_disk.c +++ b/BESM6/besm6_disk.c @@ -1,648 +1,648 @@ -/* - * BESM-6 magnetic disk device - * - * Copyright (c) 2009, Serge Vakulenko - * Copyright (c) 2009, Leonid Broukhis - * - * 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 "besm6_defs.h" - -/* - * Управляющее слово обмена с магнитным диском. - */ -#define DISK_BLOCK 0740000000 /* номер блока памяти - 27-24 рр */ -#define DISK_READ_SYSDATA 004000000 /* считывание только служебных слов */ -#define DISK_PAGE_MODE 001000000 /* обмен целой страницей */ -#define DISK_READ 000400000 /* чтение с диска в память */ -#define DISK_PAGE 000370000 /* номер страницы памяти */ -#define DISK_HALFPAGE 000004000 /* выбор половины листа */ -#define DISK_UNIT 000001600 /* номер устройства */ -#define DISK_HALFZONE 000000001 /* выбор половины зоны */ - -/* - * "Хороший" статус чтения/записи. - * Вычислено по текстам ОС Дубна. - * Диспак доволен. - */ -#define STATUS_GOOD 014000400 - -/* - * Параметры обмена с внешним устройством. - */ -typedef struct { - int op; /* Условное слово обмена */ - int dev; /* Номер устройства, 0..7 */ - int zone; /* Номер зоны на диске */ - int track; /* Выбор половины зоны на диске */ - int memory; /* Начальный адрес памяти */ - int format; /* Флаг разметки */ - int status; /* Регистр состояния */ - t_value mask_grp; /* Маска готовности для ГРП */ - int mask_fail; /* Маска ошибки обмена */ - t_value *sysdata; /* Буфер системных данных */ -} KMD; - -static KMD controller [2]; /* Две стойки КМД */ -int disk_fail; /* Маска ошибок по направлениям */ - -t_stat disk_event (UNIT *u); - -/* - * DISK data structures - * - * disk_dev DISK device descriptor - * disk_unit DISK unit descriptor - * disk_reg DISK register list - */ -UNIT disk_unit [16] = { - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, - { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, -}; - -REG disk_reg[] = { -{ "КУС_0", &controller[0].op, 8, 24, 0, 1 }, -{ "УСТР_0", &controller[0].dev, 8, 3, 0, 1 }, -{ "ЗОНА_0", &controller[0].zone, 8, 10, 0, 1 }, -{ "ДОРОЖКА_0", &controller[0].track, 8, 2, 0, 1 }, -{ "МОЗУ_0", &controller[0].memory, 8, 20, 0, 1 }, -{ "РС_0", &controller[0].status, 8, 24, 0, 1 }, -{ "КУС_1", &controller[1].op, 8, 24, 0, 1 }, -{ "УСТР_1", &controller[1].dev, 8, 3, 0, 1 }, -{ "ЗОНА_1", &controller[1].zone, 8, 10, 0, 1 }, -{ "ДОРОЖКА_1", &controller[1].track, 8, 2, 0, 1 }, -{ "МОЗУ_1", &controller[1].memory, 8, 20, 0, 1 }, -{ "РС_1", &controller[1].status, 8, 24, 0, 1 }, -{ "ОШ", &disk_fail, 8, 6, 0, 1 }, -{ 0 } -}; - -MTAB disk_mod[] = { - { 0 } -}; - -t_stat disk_reset (DEVICE *dptr); -t_stat disk_attach (UNIT *uptr, char *cptr); -t_stat disk_detach (UNIT *uptr); - -DEVICE disk_dev = { - "DISK", disk_unit, disk_reg, disk_mod, - 16, 8, 21, 1, 8, 50, - NULL, NULL, &disk_reset, NULL, &disk_attach, &disk_detach, - NULL, DEV_DISABLE | DEV_DEBUG -}; - -/* - * Определение контроллера по устройству. - */ -static KMD *unit_to_ctlr (UNIT *u) -{ - if (u < &disk_unit[8]) - return &controller[0]; - else - return &controller[1]; -} - -/* - * Reset routine - */ -t_stat disk_reset (DEVICE *dptr) -{ - int i; - - memset (&controller, 0, sizeof (controller)); - controller[0].sysdata = &memory [030]; - controller[1].sysdata = &memory [040]; - controller[0].mask_grp = GRP_CHAN3_FREE; - controller[1].mask_grp = GRP_CHAN4_FREE; - controller[0].mask_fail = 020; - controller[1].mask_fail = 010; - for (i=0; i<16; ++i) - sim_cancel (&disk_unit[i]); - return SCPE_OK; -} - -t_stat disk_attach (UNIT *u, char *cptr) -{ - t_stat s; - - s = attach_unit (u, cptr); - if (s != SCPE_OK) - return s; - return SCPE_OK; -} - -t_stat disk_detach (UNIT *u) -{ - /* TODO: сброс бита ГРП готовности направления при отключении последнего диска. */ - return detach_unit (u); -} - -t_value spread (t_value val) -{ - int i, j; - t_value res = 0; - - for (i = 0; i < 5; i++) for (j = 0; j < 9; j++) - if (val & (1LL<<(i+j*5))) - res |= 1LL << (i*9+j); - return res & BITS48; -} - -/* - * Отладочная печать массива данных обмена. - */ -static void log_data (t_value *data, int nwords) -{ - int i; - t_value val; - - for (i=0; i> 36) & 07777, - (int) (val >> 24) & 07777, - (int) (val >> 12) & 07777, - (int) val & 07777); - if ((i & 3) == 3) - fprintf (sim_log, "\n"); - } - if ((i & 3) != 0) - fprintf (sim_log, "\n"); -} - -/* - * Сложение с переносом вправо. - */ -static unsigned sum_with_right_carry (unsigned a, unsigned b) -{ - unsigned c; - - while (b) { - c = a & b; - a ^= b; - b = c >> 1; - } - return a; -} - -/* - * Запись на диск. - */ -void disk_write (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - - if (disk_dev.dctrl) - besm6_debug ("::: запись МД %o зона %04o память %05o-%05o", - c->dev, c->zone, c->memory, c->memory + 1023); - fseek (u->fileref, ZONE_SIZE * c->zone * 8, SEEK_SET); - sim_fwrite (c->sysdata, 8, 8, u->fileref); - sim_fwrite (&memory [c->memory], 8, 1024, u->fileref); - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -void disk_write_track (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - - if (disk_dev.dctrl) - besm6_debug ("::: запись МД %o полузона %04o.%d память %05o-%05o", - c->dev, c->zone, c->track, c->memory, c->memory + 511); - fseek (u->fileref, (ZONE_SIZE*c->zone + 4*c->track) * 8, SEEK_SET); - sim_fwrite (c->sysdata + 4*c->track, 8, 4, u->fileref); - fseek (u->fileref, (8 + ZONE_SIZE*c->zone + 512*c->track) * 8, - SEEK_SET); - sim_fwrite (&memory [c->memory], 8, 512, u->fileref); - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -/* - * Форматирование дорожки. - */ -void disk_format (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - t_value fmtbuf[5], *ptr; - int i; - - /* По сути, эмулятору ничего делать не надо. */ - if (! disk_dev.dctrl) - return; - - /* Находим начало записываемого заголовка. */ - ptr = &memory [c->memory]; - while ((*ptr & BITS48) == 0) - ptr++; - - /* Декодируем из гребенки в нормальный вид. */ - for (i = 0; i < 5; i++) - fmtbuf[i] = spread (ptr[i]); - - /* При первой попытке разметки адресный маркер начинается в старшем 5-разрядном слоге, - * пропускаем первый слог. */ - for (i=0; i<4; i++) - fmtbuf[i] = ((fmtbuf[i] & BITS48) << 5) | - ((fmtbuf[i+1] >> 40) & BITS(5)); - - /* Печатаем идентификатор, адрес и контрольную сумму адреса. */ - besm6_debug ("::: формат МД %o полузона %04o.%d память %05o и-а-кса %010o %010o", - c->dev, c->zone, c->track, c->memory, - (int) (fmtbuf[0] >> 8 & BITS(30)), - (int) (fmtbuf[2] >> 14 & BITS(30))); - /* log_data (fmtbuf, 4); */ -} - -/* - * Чтение с диска. - */ -void disk_read (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - - if (disk_dev.dctrl) - besm6_debug ((c->op & DISK_READ_SYSDATA) ? - "::: чтение МД %o зона %04o служебные слова" : - "::: чтение МД %o зона %04o память %05o-%05o", - c->dev, c->zone, c->memory, c->memory + 1023); - fseek (u->fileref, ZONE_SIZE * c->zone * 8, SEEK_SET); - if (sim_fread (c->sysdata, 8, 8, u->fileref) != 8) { - /* Чтение неинициализированного диска */ - disk_fail |= c->mask_fail; - return; - } - if (! (c->op & DISK_READ_SYSDATA) && - sim_fread (&memory [c->memory], 8, 1024, u->fileref) != 1024) { - /* Чтение неинициализированного диска */ - disk_fail |= c->mask_fail; - return; - } - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -t_value collect (t_value val) -{ - int i, j; - t_value res = 0; - - for (i = 0; i < 5; i++) for (j = 0; j < 9; j++) - if (val & (1LL<<(i*9+j))) - res |= 1LL << (i+j*5); - return res & BITS48; -} - -void disk_read_track (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - - if (disk_dev.dctrl) - besm6_debug ((c->op & DISK_READ_SYSDATA) ? - "::: чтение МД %o полузона %04o.%d служебные слова" : - "::: чтение МД %o полузона %04o.%d память %05o-%05o", - c->dev, c->zone, c->track, c->memory, c->memory + 511); - fseek (u->fileref, (ZONE_SIZE*c->zone + 4*c->track) * 8, SEEK_SET); - if (sim_fread (c->sysdata + 4*c->track, 8, 4, u->fileref) != 4) { - /* Чтение неинициализированного диска */ - disk_fail |= c->mask_fail; - return; - } - if (! (c->op & DISK_READ_SYSDATA)) { - fseek (u->fileref, (8 + ZONE_SIZE*c->zone + 512*c->track) * 8, - SEEK_SET); - if (sim_fread (&memory [c->memory], 8, 512, u->fileref) != 512) { - /* Чтение неинициализированного диска */ - disk_fail |= c->mask_fail; - return; - } - } - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -/* - * Чтение заголовка дорожки. - */ -void disk_read_header (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - t_value *sysdata = c->sysdata + 4*c->track; - int iaksa, i, cyl, head; - - /* Адрес: номер цилиндра и головки. */ - head = (c->zone << 1) + c->track; - cyl = head / 10; - head %= 10; - iaksa = (cyl << 20) | (head << 16); - - /* Идентификатор дорожки замены. */ - if (c->zone >= 01750) - iaksa |= BBIT(30); - - /* Контрольная сумма адреса с переносом вправо. */ - iaksa |= BITS(12) & ~sum_with_right_carry (iaksa >> 12, iaksa >> 24); - - /* Амиакса, 42 нуля, амиакса, много единиц. */ - sysdata[0] = 07404000000000000LL | (t_value) iaksa << 8; - sysdata[1] = 03740LL; - sysdata[2] = 00400000000037777LL | (t_value) iaksa << 14; - sysdata[3] = BITS48; - if (disk_dev.dctrl) - log_data (sysdata, 4); - - /* Кодируем гребенку. */ - for (i=0; i<4; i++) - sysdata[i] = SET_CONVOL (collect (sysdata[i]), CONVOL_NUMBER); -} - -/* - * Задание адреса памяти и длины массива для последующего обращения к диску. - * Номера дисковода и дорожки будут выданы позже, командой 033 0023(0024). - */ -void disk_io (int ctlr, uint32 cmd) -{ - KMD *c = &controller [ctlr]; - - c->op = cmd; - c->format = 0; - if (c->op & DISK_PAGE_MODE) { - /* Обмен страницей */ - c->memory = (cmd & DISK_PAGE) >> 2 | (cmd & DISK_BLOCK) >> 8; - } else { - /* Обмен половиной страницы (дорожкой) */ - c->memory = (cmd & (DISK_PAGE | DISK_HALFPAGE)) >> 2 | (cmd & DISK_BLOCK) >> 8; - } -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: задание на %s %08o", ctlr + '3', - (c->op & DISK_READ) ? "чтение" : "запись", cmd); -#endif - disk_fail &= ~c->mask_fail; - - /* Гасим главный регистр прерываний. */ - GRP &= ~c->mask_grp; -} - -/* - * Управление диском: команда 00 033 0023(0024). - */ -void disk_ctl (int ctlr, uint32 cmd) -{ - KMD *c = &controller [ctlr]; - UNIT *u = &disk_unit [c->dev]; - - if (cmd & BBIT(12)) { - /* Выдача в КМД адреса дорожки. - * Здесь же выполняем обмен с диском. - * Номер дисковода к этому моменту уже известен. */ - if ((disk_dev.flags & DEV_DIS) || ! (u->flags & UNIT_ATT)) { - /* Device not attached. */ - disk_fail |= c->mask_fail; - return; - } - c->zone = (cmd >> 1) & BITS(10); - c->track = cmd & 1; -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: выдача адреса дорожки %04o.%d", - ctlr + '3', c->zone, c->track); -#endif - disk_fail &= ~c->mask_fail; - if (c->op & DISK_READ) { - if (c->op & DISK_PAGE_MODE) - disk_read (u); - else - disk_read_track (u); - } else { - if (u->flags & UNIT_RO) { - /* Read only. */ - /*longjmp (cpu_halt, SCPE_RO);*/ - disk_fail |= c->mask_fail; - return; - } - if (c->format) - disk_format (u); - else if (c->op & DISK_PAGE_MODE) - disk_write (u); - else - disk_write_track (u); - } - - /* Ждём события от устройства. */ - sim_activate (u, 20*USEC); /* Ускорим для отладки. */ - - } else if (cmd & BBIT(11)) { - /* Выбора номера устройства и занесение в регистр маски КМД. - * Бит 8 - устройство 0, бит 7 - устройство 1, ... бит 1 - устройство 7. - * Также установлен бит 9 - что он означает? */ - if (cmd & BBIT(8)) c->dev = 7; - else if (cmd & BBIT(7)) c->dev = 6; - else if (cmd & BBIT(6)) c->dev = 5; - else if (cmd & BBIT(5)) c->dev = 4; - else if (cmd & BBIT(4)) c->dev = 3; - else if (cmd & BBIT(3)) c->dev = 2; - else if (cmd & BBIT(2)) c->dev = 1; - else if (cmd & BBIT(1)) c->dev = 0; - else { - /* Неверная маска выбора устройства. */ - c->dev = -1; - return; - } - c->dev += ctlr << 3; - u = &disk_unit[c->dev]; -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: выбор устройства %d", - ctlr + '3', c->dev); -#endif - if ((disk_dev.flags & DEV_DIS) || ! (u->flags & UNIT_ATT)) { - /* Device not attached. */ - disk_fail |= c->mask_fail; - GRP &= ~c->mask_grp; - } - GRP |= c->mask_grp; - - } else if (cmd & BBIT(9)) { - /* Проверка прерывания от КМД? */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: проверка готовности", - ctlr + '3'); -#endif - GRP |= c->mask_grp; - - } else { - /* Команда, выдаваемая в КМД. */ - switch (cmd & 077) { - case 000: /* диспак выдаёт эту команду один раз в начале загрузки */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: недокументированная команда 00", - ctlr + '3'); -#endif - break; - case 001: /* сброс на 0 цилиндр */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: сброс на 0 цилиндр", - ctlr + '3'); -#endif - break; - case 002: /* подвод */ - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: подвод", ctlr + '3'); - break; - case 003: /* чтение (НСМД-МОЗУ) */ - case 043: /* резервной дорожки */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: чтение", ctlr + '3'); -#endif - break; - case 004: /* запись (МОЗУ-НСМД) */ - case 044: /* резервной дорожки */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: запись", ctlr + '3'); -#endif - break; - case 005: /* разметка */ - c->format = 1; - break; - case 006: /* сравнение кодов (МОЗУ-НСМД) */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: сравнение кодов", ctlr + '3'); -#endif - break; - case 007: /* чтение заголовка */ - case 047: /* резервной дорожки */ - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: чтение %s заголовка", ctlr + '3', - cmd & 040 ? "резервного" : ""); - disk_fail &= ~c->mask_fail; - disk_read_header (u); - - /* Ждём события от устройства. */ - sim_activate (u, 20*USEC); /* Ускорим для отладки. */ - break; - case 010: /* гашение PC */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: гашение регистра состояния", - ctlr + '3'); -#endif - c->status = 0; - break; - case 011: /* опрос 1÷12 разрядов PC */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: опрос младших разрядов состояния", - ctlr + '3'); -#endif - if (disk_unit[c->dev].flags & UNIT_ATT) - c->status = STATUS_GOOD & BITS(12); - else - c->status = 0; - break; - case 031: /* опрос 13÷24 разрядов РС */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: опрос старших разрядов состояния", - ctlr + '3'); -#endif - if (disk_unit[c->dev].flags & UNIT_ATT) - c->status = (STATUS_GOOD >> 12) & BITS(12); - else - c->status = 0; - break; - case 050: /* освобождение НМД */ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: освобождение накопителя", - ctlr + '3'); -#endif - break; - default: - besm6_debug ("::: КМД %c: неизвестная команда %02o", - ctlr + '3', cmd & 077); - GRP |= c->mask_grp; /* чтобы не зависало */ - break; - } - } -} - -/* - * Запрос состояния контроллера. - */ -int disk_state (int ctlr) -{ - KMD *c = &controller [ctlr]; -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД %c: опрос состояния = %04o", - ctlr + '3', c->status); -#endif - return c->status; -} - -/* - * Событие: закончен обмен с МД. - * Устанавливаем флаг прерывания. - */ -t_stat disk_event (UNIT *u) -{ - KMD *c = unit_to_ctlr (u); - - GRP |= c->mask_grp; - return SCPE_OK; -} - -/* - * Опрос ошибок обмена командой 033 4035. - */ -int disk_errors () -{ -#if 0 - if (disk_dev.dctrl) - besm6_debug ("::: КМД: опрос шкалы ошибок = %04o", disk_fail); -#endif - return disk_fail; -} +/* + * BESM-6 magnetic disk device + * + * Copyright (c) 2009, Serge Vakulenko + * Copyright (c) 2009, Leonid Broukhis + * + * 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 "besm6_defs.h" + +/* + * Управляющее слово обмена с магнитным диском. + */ +#define DISK_BLOCK 0740000000 /* номер блока памяти - 27-24 рр */ +#define DISK_READ_SYSDATA 004000000 /* считывание только служебных слов */ +#define DISK_PAGE_MODE 001000000 /* обмен целой страницей */ +#define DISK_READ 000400000 /* чтение с диска в память */ +#define DISK_PAGE 000370000 /* номер страницы памяти */ +#define DISK_HALFPAGE 000004000 /* выбор половины листа */ +#define DISK_UNIT 000001600 /* номер устройства */ +#define DISK_HALFZONE 000000001 /* выбор половины зоны */ + +/* + * "Хороший" статус чтения/записи. + * Вычислено по текстам ОС Дубна. + * Диспак доволен. + */ +#define STATUS_GOOD 014000400 + +/* + * Параметры обмена с внешним устройством. + */ +typedef struct { + int op; /* Условное слово обмена */ + int dev; /* Номер устройства, 0..7 */ + int zone; /* Номер зоны на диске */ + int track; /* Выбор половины зоны на диске */ + int memory; /* Начальный адрес памяти */ + int format; /* Флаг разметки */ + int status; /* Регистр состояния */ + t_value mask_grp; /* Маска готовности для ГРП */ + int mask_fail; /* Маска ошибки обмена */ + t_value *sysdata; /* Буфер системных данных */ +} KMD; + +static KMD controller [2]; /* Две стойки КМД */ +int disk_fail; /* Маска ошибок по направлениям */ + +t_stat disk_event (UNIT *u); + +/* + * DISK data structures + * + * disk_dev DISK device descriptor + * disk_unit DISK unit descriptor + * disk_reg DISK register list + */ +UNIT disk_unit [16] = { + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, + { UDATA (disk_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DISK_SIZE) }, +}; + +REG disk_reg[] = { +{ "КУС_0", &controller[0].op, 8, 24, 0, 1 }, +{ "УСТР_0", &controller[0].dev, 8, 3, 0, 1 }, +{ "ЗОНА_0", &controller[0].zone, 8, 10, 0, 1 }, +{ "ДОРОЖКА_0", &controller[0].track, 8, 2, 0, 1 }, +{ "МОЗУ_0", &controller[0].memory, 8, 20, 0, 1 }, +{ "РС_0", &controller[0].status, 8, 24, 0, 1 }, +{ "КУС_1", &controller[1].op, 8, 24, 0, 1 }, +{ "УСТР_1", &controller[1].dev, 8, 3, 0, 1 }, +{ "ЗОНА_1", &controller[1].zone, 8, 10, 0, 1 }, +{ "ДОРОЖКА_1", &controller[1].track, 8, 2, 0, 1 }, +{ "МОЗУ_1", &controller[1].memory, 8, 20, 0, 1 }, +{ "РС_1", &controller[1].status, 8, 24, 0, 1 }, +{ "ОШ", &disk_fail, 8, 6, 0, 1 }, +{ 0 } +}; + +MTAB disk_mod[] = { + { 0 } +}; + +t_stat disk_reset (DEVICE *dptr); +t_stat disk_attach (UNIT *uptr, char *cptr); +t_stat disk_detach (UNIT *uptr); + +DEVICE disk_dev = { + "DISK", disk_unit, disk_reg, disk_mod, + 16, 8, 21, 1, 8, 50, + NULL, NULL, &disk_reset, NULL, &disk_attach, &disk_detach, + NULL, DEV_DISABLE | DEV_DEBUG +}; + +/* + * Определение контроллера по устройству. + */ +static KMD *unit_to_ctlr (UNIT *u) +{ + if (u < &disk_unit[8]) + return &controller[0]; + else + return &controller[1]; +} + +/* + * Reset routine + */ +t_stat disk_reset (DEVICE *dptr) +{ + int i; + + memset (&controller, 0, sizeof (controller)); + controller[0].sysdata = &memory [030]; + controller[1].sysdata = &memory [040]; + controller[0].mask_grp = GRP_CHAN3_FREE; + controller[1].mask_grp = GRP_CHAN4_FREE; + controller[0].mask_fail = 020; + controller[1].mask_fail = 010; + for (i=0; i<16; ++i) + sim_cancel (&disk_unit[i]); + return SCPE_OK; +} + +t_stat disk_attach (UNIT *u, char *cptr) +{ + t_stat s; + + s = attach_unit (u, cptr); + if (s != SCPE_OK) + return s; + return SCPE_OK; +} + +t_stat disk_detach (UNIT *u) +{ + /* TODO: сброс бита ГРП готовности направления при отключении последнего диска. */ + return detach_unit (u); +} + +t_value spread (t_value val) +{ + int i, j; + t_value res = 0; + + for (i = 0; i < 5; i++) for (j = 0; j < 9; j++) + if (val & (1LL<<(i+j*5))) + res |= 1LL << (i*9+j); + return res & BITS48; +} + +/* + * Отладочная печать массива данных обмена. + */ +static void log_data (t_value *data, int nwords) +{ + int i; + t_value val; + + for (i=0; i> 36) & 07777, + (int) (val >> 24) & 07777, + (int) (val >> 12) & 07777, + (int) val & 07777); + if ((i & 3) == 3) + fprintf (sim_log, "\n"); + } + if ((i & 3) != 0) + fprintf (sim_log, "\n"); +} + +/* + * Сложение с переносом вправо. + */ +static unsigned sum_with_right_carry (unsigned a, unsigned b) +{ + unsigned c; + + while (b) { + c = a & b; + a ^= b; + b = c >> 1; + } + return a; +} + +/* + * Запись на диск. + */ +void disk_write (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + + if (disk_dev.dctrl) + besm6_debug ("::: запись МД %o зона %04o память %05o-%05o", + c->dev, c->zone, c->memory, c->memory + 1023); + fseek (u->fileref, ZONE_SIZE * c->zone * 8, SEEK_SET); + sim_fwrite (c->sysdata, 8, 8, u->fileref); + sim_fwrite (&memory [c->memory], 8, 1024, u->fileref); + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +void disk_write_track (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + + if (disk_dev.dctrl) + besm6_debug ("::: запись МД %o полузона %04o.%d память %05o-%05o", + c->dev, c->zone, c->track, c->memory, c->memory + 511); + fseek (u->fileref, (ZONE_SIZE*c->zone + 4*c->track) * 8, SEEK_SET); + sim_fwrite (c->sysdata + 4*c->track, 8, 4, u->fileref); + fseek (u->fileref, (8 + ZONE_SIZE*c->zone + 512*c->track) * 8, + SEEK_SET); + sim_fwrite (&memory [c->memory], 8, 512, u->fileref); + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +/* + * Форматирование дорожки. + */ +void disk_format (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + t_value fmtbuf[5], *ptr; + int i; + + /* По сути, эмулятору ничего делать не надо. */ + if (! disk_dev.dctrl) + return; + + /* Находим начало записываемого заголовка. */ + ptr = &memory [c->memory]; + while ((*ptr & BITS48) == 0) + ptr++; + + /* Декодируем из гребенки в нормальный вид. */ + for (i = 0; i < 5; i++) + fmtbuf[i] = spread (ptr[i]); + + /* При первой попытке разметки адресный маркер начинается в старшем 5-разрядном слоге, + * пропускаем первый слог. */ + for (i=0; i<4; i++) + fmtbuf[i] = ((fmtbuf[i] & BITS48) << 5) | + ((fmtbuf[i+1] >> 40) & BITS(5)); + + /* Печатаем идентификатор, адрес и контрольную сумму адреса. */ + besm6_debug ("::: формат МД %o полузона %04o.%d память %05o и-а-кса %010o %010o", + c->dev, c->zone, c->track, c->memory, + (int) (fmtbuf[0] >> 8 & BITS(30)), + (int) (fmtbuf[2] >> 14 & BITS(30))); + /* log_data (fmtbuf, 4); */ +} + +/* + * Чтение с диска. + */ +void disk_read (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + + if (disk_dev.dctrl) + besm6_debug ((c->op & DISK_READ_SYSDATA) ? + "::: чтение МД %o зона %04o служебные слова" : + "::: чтение МД %o зона %04o память %05o-%05o", + c->dev, c->zone, c->memory, c->memory + 1023); + fseek (u->fileref, ZONE_SIZE * c->zone * 8, SEEK_SET); + if (sim_fread (c->sysdata, 8, 8, u->fileref) != 8) { + /* Чтение неинициализированного диска */ + disk_fail |= c->mask_fail; + return; + } + if (! (c->op & DISK_READ_SYSDATA) && + sim_fread (&memory [c->memory], 8, 1024, u->fileref) != 1024) { + /* Чтение неинициализированного диска */ + disk_fail |= c->mask_fail; + return; + } + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +t_value collect (t_value val) +{ + int i, j; + t_value res = 0; + + for (i = 0; i < 5; i++) for (j = 0; j < 9; j++) + if (val & (1LL<<(i*9+j))) + res |= 1LL << (i+j*5); + return res & BITS48; +} + +void disk_read_track (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + + if (disk_dev.dctrl) + besm6_debug ((c->op & DISK_READ_SYSDATA) ? + "::: чтение МД %o полузона %04o.%d служебные слова" : + "::: чтение МД %o полузона %04o.%d память %05o-%05o", + c->dev, c->zone, c->track, c->memory, c->memory + 511); + fseek (u->fileref, (ZONE_SIZE*c->zone + 4*c->track) * 8, SEEK_SET); + if (sim_fread (c->sysdata + 4*c->track, 8, 4, u->fileref) != 4) { + /* Чтение неинициализированного диска */ + disk_fail |= c->mask_fail; + return; + } + if (! (c->op & DISK_READ_SYSDATA)) { + fseek (u->fileref, (8 + ZONE_SIZE*c->zone + 512*c->track) * 8, + SEEK_SET); + if (sim_fread (&memory [c->memory], 8, 512, u->fileref) != 512) { + /* Чтение неинициализированного диска */ + disk_fail |= c->mask_fail; + return; + } + } + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +/* + * Чтение заголовка дорожки. + */ +void disk_read_header (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + t_value *sysdata = c->sysdata + 4*c->track; + int iaksa, i, cyl, head; + + /* Адрес: номер цилиндра и головки. */ + head = (c->zone << 1) + c->track; + cyl = head / 10; + head %= 10; + iaksa = (cyl << 20) | (head << 16); + + /* Идентификатор дорожки замены. */ + if (c->zone >= 01750) + iaksa |= BBIT(30); + + /* Контрольная сумма адреса с переносом вправо. */ + iaksa |= BITS(12) & ~sum_with_right_carry (iaksa >> 12, iaksa >> 24); + + /* Амиакса, 42 нуля, амиакса, много единиц. */ + sysdata[0] = 07404000000000000LL | (t_value) iaksa << 8; + sysdata[1] = 03740LL; + sysdata[2] = 00400000000037777LL | (t_value) iaksa << 14; + sysdata[3] = BITS48; + if (disk_dev.dctrl) + log_data (sysdata, 4); + + /* Кодируем гребенку. */ + for (i=0; i<4; i++) + sysdata[i] = SET_CONVOL (collect (sysdata[i]), CONVOL_NUMBER); +} + +/* + * Задание адреса памяти и длины массива для последующего обращения к диску. + * Номера дисковода и дорожки будут выданы позже, командой 033 0023(0024). + */ +void disk_io (int ctlr, uint32 cmd) +{ + KMD *c = &controller [ctlr]; + + c->op = cmd; + c->format = 0; + if (c->op & DISK_PAGE_MODE) { + /* Обмен страницей */ + c->memory = (cmd & DISK_PAGE) >> 2 | (cmd & DISK_BLOCK) >> 8; + } else { + /* Обмен половиной страницы (дорожкой) */ + c->memory = (cmd & (DISK_PAGE | DISK_HALFPAGE)) >> 2 | (cmd & DISK_BLOCK) >> 8; + } +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: задание на %s %08o", ctlr + '3', + (c->op & DISK_READ) ? "чтение" : "запись", cmd); +#endif + disk_fail &= ~c->mask_fail; + + /* Гасим главный регистр прерываний. */ + GRP &= ~c->mask_grp; +} + +/* + * Управление диском: команда 00 033 0023(0024). + */ +void disk_ctl (int ctlr, uint32 cmd) +{ + KMD *c = &controller [ctlr]; + UNIT *u = &disk_unit [c->dev]; + + if (cmd & BBIT(12)) { + /* Выдача в КМД адреса дорожки. + * Здесь же выполняем обмен с диском. + * Номер дисковода к этому моменту уже известен. */ + if ((disk_dev.flags & DEV_DIS) || ! (u->flags & UNIT_ATT)) { + /* Device not attached. */ + disk_fail |= c->mask_fail; + return; + } + c->zone = (cmd >> 1) & BITS(10); + c->track = cmd & 1; +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: выдача адреса дорожки %04o.%d", + ctlr + '3', c->zone, c->track); +#endif + disk_fail &= ~c->mask_fail; + if (c->op & DISK_READ) { + if (c->op & DISK_PAGE_MODE) + disk_read (u); + else + disk_read_track (u); + } else { + if (u->flags & UNIT_RO) { + /* Read only. */ + /*longjmp (cpu_halt, SCPE_RO);*/ + disk_fail |= c->mask_fail; + return; + } + if (c->format) + disk_format (u); + else if (c->op & DISK_PAGE_MODE) + disk_write (u); + else + disk_write_track (u); + } + + /* Ждём события от устройства. */ + sim_activate (u, 20*USEC); /* Ускорим для отладки. */ + + } else if (cmd & BBIT(11)) { + /* Выбора номера устройства и занесение в регистр маски КМД. + * Бит 8 - устройство 0, бит 7 - устройство 1, ... бит 1 - устройство 7. + * Также установлен бит 9 - что он означает? */ + if (cmd & BBIT(8)) c->dev = 7; + else if (cmd & BBIT(7)) c->dev = 6; + else if (cmd & BBIT(6)) c->dev = 5; + else if (cmd & BBIT(5)) c->dev = 4; + else if (cmd & BBIT(4)) c->dev = 3; + else if (cmd & BBIT(3)) c->dev = 2; + else if (cmd & BBIT(2)) c->dev = 1; + else if (cmd & BBIT(1)) c->dev = 0; + else { + /* Неверная маска выбора устройства. */ + c->dev = -1; + return; + } + c->dev += ctlr << 3; + u = &disk_unit[c->dev]; +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: выбор устройства %d", + ctlr + '3', c->dev); +#endif + if ((disk_dev.flags & DEV_DIS) || ! (u->flags & UNIT_ATT)) { + /* Device not attached. */ + disk_fail |= c->mask_fail; + GRP &= ~c->mask_grp; + } + GRP |= c->mask_grp; + + } else if (cmd & BBIT(9)) { + /* Проверка прерывания от КМД? */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: проверка готовности", + ctlr + '3'); +#endif + GRP |= c->mask_grp; + + } else { + /* Команда, выдаваемая в КМД. */ + switch (cmd & 077) { + case 000: /* диспак выдаёт эту команду один раз в начале загрузки */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: недокументированная команда 00", + ctlr + '3'); +#endif + break; + case 001: /* сброс на 0 цилиндр */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: сброс на 0 цилиндр", + ctlr + '3'); +#endif + break; + case 002: /* подвод */ + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: подвод", ctlr + '3'); + break; + case 003: /* чтение (НСМД-МОЗУ) */ + case 043: /* резервной дорожки */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: чтение", ctlr + '3'); +#endif + break; + case 004: /* запись (МОЗУ-НСМД) */ + case 044: /* резервной дорожки */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: запись", ctlr + '3'); +#endif + break; + case 005: /* разметка */ + c->format = 1; + break; + case 006: /* сравнение кодов (МОЗУ-НСМД) */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: сравнение кодов", ctlr + '3'); +#endif + break; + case 007: /* чтение заголовка */ + case 047: /* резервной дорожки */ + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: чтение %s заголовка", ctlr + '3', + cmd & 040 ? "резервного" : ""); + disk_fail &= ~c->mask_fail; + disk_read_header (u); + + /* Ждём события от устройства. */ + sim_activate (u, 20*USEC); /* Ускорим для отладки. */ + break; + case 010: /* гашение PC */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: гашение регистра состояния", + ctlr + '3'); +#endif + c->status = 0; + break; + case 011: /* опрос 1÷12 разрядов PC */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: опрос младших разрядов состояния", + ctlr + '3'); +#endif + if (disk_unit[c->dev].flags & UNIT_ATT) + c->status = STATUS_GOOD & BITS(12); + else + c->status = 0; + break; + case 031: /* опрос 13÷24 разрядов РС */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: опрос старших разрядов состояния", + ctlr + '3'); +#endif + if (disk_unit[c->dev].flags & UNIT_ATT) + c->status = (STATUS_GOOD >> 12) & BITS(12); + else + c->status = 0; + break; + case 050: /* освобождение НМД */ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: освобождение накопителя", + ctlr + '3'); +#endif + break; + default: + besm6_debug ("::: КМД %c: неизвестная команда %02o", + ctlr + '3', cmd & 077); + GRP |= c->mask_grp; /* чтобы не зависало */ + break; + } + } +} + +/* + * Запрос состояния контроллера. + */ +int disk_state (int ctlr) +{ + KMD *c = &controller [ctlr]; +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД %c: опрос состояния = %04o", + ctlr + '3', c->status); +#endif + return c->status; +} + +/* + * Событие: закончен обмен с МД. + * Устанавливаем флаг прерывания. + */ +t_stat disk_event (UNIT *u) +{ + KMD *c = unit_to_ctlr (u); + + GRP |= c->mask_grp; + return SCPE_OK; +} + +/* + * Опрос ошибок обмена командой 033 4035. + */ +int disk_errors () +{ +#if 0 + if (disk_dev.dctrl) + besm6_debug ("::: КМД: опрос шкалы ошибок = %04o", disk_fail); +#endif + return disk_fail; +} diff --git a/BESM6/besm6_drum.c b/BESM6/besm6_drum.c index 2766baa2..6dedcb5a 100644 --- a/BESM6/besm6_drum.c +++ b/BESM6/besm6_drum.c @@ -1,372 +1,372 @@ -/* - * besm6_drum.c: BESM-6 magnetic drum device - * - * 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 "besm6_defs.h" - -/* - * Управляющее слово обмена с магнитным барабаном. - */ -#define DRUM_READ_OVERLAY 020000000 /* считывание с наложением */ -#define DRUM_PARITY_FLAG 010000000 /* блокировка считывания слов с неверной - * чётностью или запись с неверной чётностью */ -#define DRUM_READ_SYSDATA 004000000 /* считывание только служебных слов */ -#define DRUM_PAGE_MODE 001000000 /* обмен целой страницей */ -#define DRUM_READ 000400000 /* чтение с барабана в память */ -#define DRUM_PAGE 000370000 /* номер страницы памяти */ -#define DRUM_BLOCK 0740000000 /* номер блока памяти - 27-24 рр */ -#define DRUM_PARAGRAF 000006000 /* номер абзаца */ -#define DRUM_UNIT 000001600 /* номер барабана */ -#define DRUM_CYLINDER 000000174 /* номер тракта на барабане */ -#define DRUM_SECTOR 000000003 /* номер сектора */ - -/* - * Параметры обмена с внешним устройством. - */ -int drum_op; /* Условное слово обмена */ -int drum_zone; /* Номер зоны на барабане */ -int drum_sector; /* Начальный номер сектора на барабане */ -int drum_memory; /* Начальный адрес памяти */ -int drum_nwords; /* Количество слов обмена */ -int drum_fail; /* Маска ошибок по направлениям */ - -t_stat drum_event (UNIT *u); - -/* - * DRUM data structures - * - * drum_dev DRUM device descriptor - * drum_unit DRUM unit descriptor - * drum_reg DRUM register list - */ -UNIT drum_unit [] = { - { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, - { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, -}; - -REG drum_reg[] = { -{ "УС", &drum_op, 8, 24, 0, 1 }, -{ "ЗОНА", &drum_zone, 8, 10, 0, 1 }, -{ "СЕКТОР", &drum_sector, 8, 2, 0, 1 }, -{ "МОЗУ", &drum_memory, 8, 15, 0, 1 }, -{ "СЧСЛОВ", &drum_nwords, 8, 11, 0, 1 }, -{ 0 } -}; - -MTAB drum_mod[] = { - { 0 } -}; - -t_stat drum_reset (DEVICE *dptr); -t_stat drum_attach (UNIT *uptr, char *cptr); -t_stat drum_detach (UNIT *uptr); - -DEVICE drum_dev = { - "DRUM", drum_unit, drum_reg, drum_mod, - 2, 8, 19, 1, 8, 50, - NULL, NULL, &drum_reset, NULL, &drum_attach, &drum_detach, - NULL, DEV_DISABLE | DEV_DEBUG -}; - -/* - * Reset routine - */ -t_stat drum_reset (DEVICE *dptr) -{ - drum_op = 0; - drum_zone = 0; - drum_sector = 0; - drum_memory = 0; - drum_nwords = 0; - sim_cancel (&drum_unit[0]); - sim_cancel (&drum_unit[1]); - return SCPE_OK; -} - -t_stat drum_attach (UNIT *u, char *cptr) -{ - t_stat s; - - s = attach_unit (u, cptr); - if (s != SCPE_OK) - return s; - if (u == &drum_unit[0]) - GRP |= GRP_DRUM1_FREE; - else - GRP |= GRP_DRUM2_FREE; - return SCPE_OK; -} - -t_stat drum_detach (UNIT *u) -{ - if (u == &drum_unit[0]) - GRP &= ~GRP_DRUM1_FREE; - else - GRP &= ~GRP_DRUM2_FREE; - return detach_unit (u); -} - -/* - * Отладочная печать массива данных обмена. - */ -#if 0 -static void log_io (UNIT *u) -{ - t_value *data, *sysdata; - int i; - void print_word (t_value val) { - fprintf (sim_log, " %o-%04o-%04o-%04o-%04o", - (int) (val >> 48) & 07, - (int) (val >> 36) & 07777, - (int) (val >> 24) & 07777, - (int) (val >> 12) & 07777, (int) val & 07777); - } - - data = &memory [drum_memory]; - sysdata = (u == &drum_unit[0]) ? &memory [010] : &memory [020]; - if (drum_nwords == 1024) { - fprintf (sim_log, "=== зона МБ %d.%03o:", - (u == &drum_unit[0]) ? 1 : 2, drum_zone); - for (i=0; i<8; ++i) - print_word (sysdata[i]); - } else { - sysdata += drum_sector*2; - fprintf (sim_log, "=== сектор МБ %d.%03o.%o:", - (u == &drum_unit[0]) ? 1 : 2, - drum_zone, drum_sector); - for (i=0; i<2; ++i) - print_word (sysdata[i]); - } - if (! (drum_op & DRUM_READ_SYSDATA)) { - fprintf (sim_log, "\n\t\t "); - for (i=0; ifileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); - sim_fwrite (sysdata, 8, 8, u->fileref); - sim_fwrite (&memory [drum_memory], 8, 1024, u->fileref); - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -void drum_write_sector (UNIT *u) -{ - int ctlr; - t_value *sysdata; - - ctlr = (u == &drum_unit[1]); - sysdata = ctlr ? &memory [020] : &memory [010]; - fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, - SEEK_SET); - sim_fwrite (&sysdata [drum_sector*2], 8, 2, u->fileref); - fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, - SEEK_SET); - sim_fwrite (&memory [drum_memory], 8, 256, u->fileref); - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -/* - * Чтение с барабана. - */ -void drum_read (UNIT *u) -{ - int ctlr; - t_value *sysdata; - - ctlr = (u == &drum_unit[1]); - sysdata = ctlr ? &memory [020] : &memory [010]; - fseek (u->fileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); - if (sim_fread (sysdata, 8, 8, u->fileref) != 8) { - /* Чтение неинициализированного барабана */ - drum_fail |= 0100 >> ctlr; - return; - } - if (! (drum_op & DRUM_READ_SYSDATA) && - sim_fread (&memory[drum_memory], 8, 1024, u->fileref) != 1024) { - /* Чтение неинициализированного барабана */ - drum_fail |= 0100 >> ctlr; - return; - } - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -void drum_read_sector (UNIT *u) -{ - int ctlr; - t_value *sysdata; - - ctlr = (u == &drum_unit[1]); - sysdata = ctlr ? &memory [020] : &memory [010]; - fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, SEEK_SET); - if (sim_fread (&sysdata [drum_sector*2], 8, 2, u->fileref) != 2) { - /* Чтение неинициализированного барабана */ - drum_fail |= 0100 >> ctlr; - return; - } - if (! (drum_op & DRUM_READ_SYSDATA)) { - fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, - SEEK_SET); - if (sim_fread (&memory[drum_memory], 8, 256, u->fileref) != 256) { - /* Чтение неинициализированного барабана */ - drum_fail |= 0100 >> ctlr; - return; - } - } - if (ferror (u->fileref)) - longjmp (cpu_halt, SCPE_IOERR); -} - -static void clear_memory (t_value *p, int nwords) -{ - while (nwords-- > 0) - *p++ = SET_CONVOL (0, CONVOL_NUMBER); -} - -/* - * Выполнение обращения к барабану. - */ -void drum (int ctlr, uint32 cmd) -{ - UNIT *u = &drum_unit[ctlr]; - - drum_op = cmd; - if (drum_op & DRUM_PAGE_MODE) { - /* Обмен страницей */ - drum_nwords = 1024; - drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; - drum_sector = 0; - drum_memory = (cmd & DRUM_PAGE) >> 2 | (cmd & DRUM_BLOCK) >> 8; - if (drum_dev.dctrl) - besm6_debug ("### %s МБ %c%d зона %02o память %05o-%05o", - (drum_op & DRUM_READ) ? "чтение" : "запись", - ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, - drum_memory, drum_memory + drum_nwords - 1); - if (drum_op & DRUM_READ) { - clear_memory (ctlr ? &memory [020] : &memory [010], 8); - if (! (drum_op & DRUM_READ_SYSDATA)) - clear_memory (&memory[drum_memory], 1024); - } - } else { - /* Обмен сектором */ - drum_nwords = 256; - drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; - drum_sector = cmd & DRUM_SECTOR; - drum_memory = (cmd & (DRUM_PAGE | DRUM_PARAGRAF)) >> 2 | (cmd & DRUM_BLOCK) >> 8; - if (drum_dev.dctrl) - besm6_debug ("### %s МБ %c%d зона %02o сектор %d память %05o-%05o", - (drum_op & DRUM_READ) ? "чтение" : "запись", - ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, - drum_sector & 3, - drum_memory, drum_memory + drum_nwords - 1); - if (drum_op & DRUM_READ) { - clear_memory (ctlr ? &memory [020 + drum_sector*2] : - &memory [010 + drum_sector*2], 2); - if (! (drum_op & DRUM_READ_SYSDATA)) - clear_memory (&memory[drum_memory], 256); - } - } - if ((drum_dev.flags & DEV_DIS) || ! u->fileref) { - /* Device not attached. */ - drum_fail |= 0100 >> ctlr; - return; - } - drum_fail &= ~(0100 >> ctlr); - if (drum_op & DRUM_READ_OVERLAY) { - /* Not implemented. */ - longjmp (cpu_halt, SCPE_NOFNC); - } - if (drum_op & DRUM_READ) { - if (drum_op & DRUM_PAGE_MODE) - drum_read (u); - else - drum_read_sector (u); - } else { - if (drum_op & DRUM_PARITY_FLAG) { - besm6_log ("### запись МБ с неправильной чётностью не реализована"); - longjmp (cpu_halt, SCPE_NOFNC); - } - if (u->flags & UNIT_RO) { - /* Read only. */ - longjmp (cpu_halt, SCPE_RO); - } - if (drum_op & DRUM_PAGE_MODE) - drum_write (u); - else - drum_write_sector (u); - } - /*if (drum_dev.dctrl && sim_log) - log_io (u);*/ - - /* Гасим главный регистр прерываний. */ - if (u == &drum_unit[0]) - GRP &= ~GRP_DRUM1_FREE; - else - GRP &= ~GRP_DRUM2_FREE; - - /* Ждём события от устройства. - * Согласно данным из книжки Мазного Г.Л., - * даём 20 мсек на обмен, или 200 тыс.тактов. */ - /*sim_activate (u, 20*MSEC);*/ - sim_activate (u, 20*USEC); /* Ускорим для отладки. */ -} - -/* - * Событие: закончен обмен с МБ. - * Устанавливаем флаг прерывания. - */ -t_stat drum_event (UNIT *u) -{ - if (u == &drum_unit[0]) - GRP |= GRP_DRUM1_FREE; - else - GRP |= GRP_DRUM2_FREE; - return SCPE_OK; -} - -/* - * Опрос ошибок обмена командой 033 4035. - */ -int drum_errors () -{ - return drum_fail; -} +/* + * besm6_drum.c: BESM-6 magnetic drum device + * + * 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 "besm6_defs.h" + +/* + * Управляющее слово обмена с магнитным барабаном. + */ +#define DRUM_READ_OVERLAY 020000000 /* считывание с наложением */ +#define DRUM_PARITY_FLAG 010000000 /* блокировка считывания слов с неверной + * чётностью или запись с неверной чётностью */ +#define DRUM_READ_SYSDATA 004000000 /* считывание только служебных слов */ +#define DRUM_PAGE_MODE 001000000 /* обмен целой страницей */ +#define DRUM_READ 000400000 /* чтение с барабана в память */ +#define DRUM_PAGE 000370000 /* номер страницы памяти */ +#define DRUM_BLOCK 0740000000 /* номер блока памяти - 27-24 рр */ +#define DRUM_PARAGRAF 000006000 /* номер абзаца */ +#define DRUM_UNIT 000001600 /* номер барабана */ +#define DRUM_CYLINDER 000000174 /* номер тракта на барабане */ +#define DRUM_SECTOR 000000003 /* номер сектора */ + +/* + * Параметры обмена с внешним устройством. + */ +int drum_op; /* Условное слово обмена */ +int drum_zone; /* Номер зоны на барабане */ +int drum_sector; /* Начальный номер сектора на барабане */ +int drum_memory; /* Начальный адрес памяти */ +int drum_nwords; /* Количество слов обмена */ +int drum_fail; /* Маска ошибок по направлениям */ + +t_stat drum_event (UNIT *u); + +/* + * DRUM data structures + * + * drum_dev DRUM device descriptor + * drum_unit DRUM unit descriptor + * drum_reg DRUM register list + */ +UNIT drum_unit [] = { + { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, + { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, +}; + +REG drum_reg[] = { +{ "УС", &drum_op, 8, 24, 0, 1 }, +{ "ЗОНА", &drum_zone, 8, 10, 0, 1 }, +{ "СЕКТОР", &drum_sector, 8, 2, 0, 1 }, +{ "МОЗУ", &drum_memory, 8, 15, 0, 1 }, +{ "СЧСЛОВ", &drum_nwords, 8, 11, 0, 1 }, +{ 0 } +}; + +MTAB drum_mod[] = { + { 0 } +}; + +t_stat drum_reset (DEVICE *dptr); +t_stat drum_attach (UNIT *uptr, char *cptr); +t_stat drum_detach (UNIT *uptr); + +DEVICE drum_dev = { + "DRUM", drum_unit, drum_reg, drum_mod, + 2, 8, 19, 1, 8, 50, + NULL, NULL, &drum_reset, NULL, &drum_attach, &drum_detach, + NULL, DEV_DISABLE | DEV_DEBUG +}; + +/* + * Reset routine + */ +t_stat drum_reset (DEVICE *dptr) +{ + drum_op = 0; + drum_zone = 0; + drum_sector = 0; + drum_memory = 0; + drum_nwords = 0; + sim_cancel (&drum_unit[0]); + sim_cancel (&drum_unit[1]); + return SCPE_OK; +} + +t_stat drum_attach (UNIT *u, char *cptr) +{ + t_stat s; + + s = attach_unit (u, cptr); + if (s != SCPE_OK) + return s; + if (u == &drum_unit[0]) + GRP |= GRP_DRUM1_FREE; + else + GRP |= GRP_DRUM2_FREE; + return SCPE_OK; +} + +t_stat drum_detach (UNIT *u) +{ + if (u == &drum_unit[0]) + GRP &= ~GRP_DRUM1_FREE; + else + GRP &= ~GRP_DRUM2_FREE; + return detach_unit (u); +} + +/* + * Отладочная печать массива данных обмена. + */ +#if 0 +static void log_io (UNIT *u) +{ + t_value *data, *sysdata; + int i; + void print_word (t_value val) { + fprintf (sim_log, " %o-%04o-%04o-%04o-%04o", + (int) (val >> 48) & 07, + (int) (val >> 36) & 07777, + (int) (val >> 24) & 07777, + (int) (val >> 12) & 07777, (int) val & 07777); + } + + data = &memory [drum_memory]; + sysdata = (u == &drum_unit[0]) ? &memory [010] : &memory [020]; + if (drum_nwords == 1024) { + fprintf (sim_log, "=== зона МБ %d.%03o:", + (u == &drum_unit[0]) ? 1 : 2, drum_zone); + for (i=0; i<8; ++i) + print_word (sysdata[i]); + } else { + sysdata += drum_sector*2; + fprintf (sim_log, "=== сектор МБ %d.%03o.%o:", + (u == &drum_unit[0]) ? 1 : 2, + drum_zone, drum_sector); + for (i=0; i<2; ++i) + print_word (sysdata[i]); + } + if (! (drum_op & DRUM_READ_SYSDATA)) { + fprintf (sim_log, "\n\t\t "); + for (i=0; ifileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); + sim_fwrite (sysdata, 8, 8, u->fileref); + sim_fwrite (&memory [drum_memory], 8, 1024, u->fileref); + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +void drum_write_sector (UNIT *u) +{ + int ctlr; + t_value *sysdata; + + ctlr = (u == &drum_unit[1]); + sysdata = ctlr ? &memory [020] : &memory [010]; + fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, + SEEK_SET); + sim_fwrite (&sysdata [drum_sector*2], 8, 2, u->fileref); + fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, + SEEK_SET); + sim_fwrite (&memory [drum_memory], 8, 256, u->fileref); + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +/* + * Чтение с барабана. + */ +void drum_read (UNIT *u) +{ + int ctlr; + t_value *sysdata; + + ctlr = (u == &drum_unit[1]); + sysdata = ctlr ? &memory [020] : &memory [010]; + fseek (u->fileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); + if (sim_fread (sysdata, 8, 8, u->fileref) != 8) { + /* Чтение неинициализированного барабана */ + drum_fail |= 0100 >> ctlr; + return; + } + if (! (drum_op & DRUM_READ_SYSDATA) && + sim_fread (&memory[drum_memory], 8, 1024, u->fileref) != 1024) { + /* Чтение неинициализированного барабана */ + drum_fail |= 0100 >> ctlr; + return; + } + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +void drum_read_sector (UNIT *u) +{ + int ctlr; + t_value *sysdata; + + ctlr = (u == &drum_unit[1]); + sysdata = ctlr ? &memory [020] : &memory [010]; + fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, SEEK_SET); + if (sim_fread (&sysdata [drum_sector*2], 8, 2, u->fileref) != 2) { + /* Чтение неинициализированного барабана */ + drum_fail |= 0100 >> ctlr; + return; + } + if (! (drum_op & DRUM_READ_SYSDATA)) { + fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, + SEEK_SET); + if (sim_fread (&memory[drum_memory], 8, 256, u->fileref) != 256) { + /* Чтение неинициализированного барабана */ + drum_fail |= 0100 >> ctlr; + return; + } + } + if (ferror (u->fileref)) + longjmp (cpu_halt, SCPE_IOERR); +} + +static void clear_memory (t_value *p, int nwords) +{ + while (nwords-- > 0) + *p++ = SET_CONVOL (0, CONVOL_NUMBER); +} + +/* + * Выполнение обращения к барабану. + */ +void drum (int ctlr, uint32 cmd) +{ + UNIT *u = &drum_unit[ctlr]; + + drum_op = cmd; + if (drum_op & DRUM_PAGE_MODE) { + /* Обмен страницей */ + drum_nwords = 1024; + drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; + drum_sector = 0; + drum_memory = (cmd & DRUM_PAGE) >> 2 | (cmd & DRUM_BLOCK) >> 8; + if (drum_dev.dctrl) + besm6_debug ("### %s МБ %c%d зона %02o память %05o-%05o", + (drum_op & DRUM_READ) ? "чтение" : "запись", + ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, + drum_memory, drum_memory + drum_nwords - 1); + if (drum_op & DRUM_READ) { + clear_memory (ctlr ? &memory [020] : &memory [010], 8); + if (! (drum_op & DRUM_READ_SYSDATA)) + clear_memory (&memory[drum_memory], 1024); + } + } else { + /* Обмен сектором */ + drum_nwords = 256; + drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; + drum_sector = cmd & DRUM_SECTOR; + drum_memory = (cmd & (DRUM_PAGE | DRUM_PARAGRAF)) >> 2 | (cmd & DRUM_BLOCK) >> 8; + if (drum_dev.dctrl) + besm6_debug ("### %s МБ %c%d зона %02o сектор %d память %05o-%05o", + (drum_op & DRUM_READ) ? "чтение" : "запись", + ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, + drum_sector & 3, + drum_memory, drum_memory + drum_nwords - 1); + if (drum_op & DRUM_READ) { + clear_memory (ctlr ? &memory [020 + drum_sector*2] : + &memory [010 + drum_sector*2], 2); + if (! (drum_op & DRUM_READ_SYSDATA)) + clear_memory (&memory[drum_memory], 256); + } + } + if ((drum_dev.flags & DEV_DIS) || ! u->fileref) { + /* Device not attached. */ + drum_fail |= 0100 >> ctlr; + return; + } + drum_fail &= ~(0100 >> ctlr); + if (drum_op & DRUM_READ_OVERLAY) { + /* Not implemented. */ + longjmp (cpu_halt, SCPE_NOFNC); + } + if (drum_op & DRUM_READ) { + if (drum_op & DRUM_PAGE_MODE) + drum_read (u); + else + drum_read_sector (u); + } else { + if (drum_op & DRUM_PARITY_FLAG) { + besm6_log ("### запись МБ с неправильной чётностью не реализована"); + longjmp (cpu_halt, SCPE_NOFNC); + } + if (u->flags & UNIT_RO) { + /* Read only. */ + longjmp (cpu_halt, SCPE_RO); + } + if (drum_op & DRUM_PAGE_MODE) + drum_write (u); + else + drum_write_sector (u); + } + /*if (drum_dev.dctrl && sim_log) + log_io (u);*/ + + /* Гасим главный регистр прерываний. */ + if (u == &drum_unit[0]) + GRP &= ~GRP_DRUM1_FREE; + else + GRP &= ~GRP_DRUM2_FREE; + + /* Ждём события от устройства. + * Согласно данным из книжки Мазного Г.Л., + * даём 20 мсек на обмен, или 200 тыс.тактов. */ + /*sim_activate (u, 20*MSEC);*/ + sim_activate (u, 20*USEC); /* Ускорим для отладки. */ +} + +/* + * Событие: закончен обмен с МБ. + * Устанавливаем флаг прерывания. + */ +t_stat drum_event (UNIT *u) +{ + if (u == &drum_unit[0]) + GRP |= GRP_DRUM1_FREE; + else + GRP |= GRP_DRUM2_FREE; + return SCPE_OK; +} + +/* + * Опрос ошибок обмена командой 033 4035. + */ +int drum_errors () +{ + return drum_fail; +} diff --git a/BESM6/besm6_mmu.c b/BESM6/besm6_mmu.c index beed6dc2..c9d2c2e5 100644 --- a/BESM6/besm6_mmu.c +++ b/BESM6/besm6_mmu.c @@ -1,652 +1,652 @@ -/* - * besm6_mmu.c: BESM-6 fast write cache and TLB registers - *(стойка БРУС) - * - * Copyright (c) 2009, Leonid Broukhis - * - * 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 "besm6_defs.h" - -/* - * MMU data structures - * - * mmu_dev MMU device descriptor - * mmu_unit MMU unit descriptor - * mmu_reg MMU register list - */ -UNIT mmu_unit = { - UDATA (NULL, UNIT_FIX, 8) -}; - -t_value BRZ[8]; -uint32 BAZ[8], TABST, RZ, OLDEST, FLUSH; - -t_value BRS[4]; -uint32 BAS[4]; -uint32 BRSLRU; - -/* - * 64-битные регистры RP0-RP7 - для отображения регистров приписки, - * группами по 4 ради компактности, 12 бит на страницу. - * TLB0-TLB31 - постраничные регистры приписки, копии RPi. - * Обращение к памяти должно вестись через TLBi. - */ -t_value RP[8]; -uint32 TLB[32]; - -unsigned iintr_data; /* protected page number or parity check location */ - -t_value pult[8]; - -REG mmu_reg[] = { -{ "БРЗ0", &BRZ[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Буферные регистры записи */ -{ "БРЗ1", &BRZ[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ2", &BRZ[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ3", &BRZ[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ4", &BRZ[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ5", &BRZ[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ6", &BRZ[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРЗ7", &BRZ[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БАЗ0", &BAZ[0], 8, 16, 0, 1 }, /* Буферные адреса записи */ -{ "БАЗ1", &BAZ[1], 8, 16, 0, 1 }, -{ "БАЗ2", &BAZ[2], 8, 16, 0, 1 }, -{ "БАЗ3", &BAZ[3], 8, 16, 0, 1 }, -{ "БАЗ4", &BAZ[4], 8, 16, 0, 1 }, -{ "БАЗ5", &BAZ[5], 8, 16, 0, 1 }, -{ "БАЗ6", &BAZ[6], 8, 16, 0, 1 }, -{ "БАЗ7", &BAZ[7], 8, 16, 0, 1 }, -{ "ТАБСТ", &TABST, 8, 28, 0, 1, NULL, NULL, REG_HIDDEN },/* Таблица старшинства БРЗ */ -{ "ЗпТР", &FLUSH, 8, 4, 0, 1, NULL, NULL, REG_HIDDEN },/* Признак выталкивания БРЗ */ -{ "Старш", &OLDEST, 8, 3, 0, 1 }, /* Номер вытолкнутого БРЗ */ -{ "РП0", &RP[0], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* Регистры приписки, по 12 бит */ -{ "РП1", &RP[1], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП2", &RP[2], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП3", &RP[3], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП4", &RP[4], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП5", &RP[5], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП6", &RP[6], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РП7", &RP[7], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, -{ "РЗ", &RZ, 8, 32, 0, 1 }, /* Регистр защиты */ -{ "ТР1", &pult[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Тумблерные регистры */ -{ "ТР2", &pult[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "ТР3", &pult[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "ТР4", &pult[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "ТР5", &pult[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "ТР6", &pult[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "ТР7", &pult[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРС0", &BRS[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Буферные регистры слов */ -{ "БРС1", &BRS[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРС2", &BRS[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БРС3", &BRS[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, -{ "БАС0", &BAS[0], 8, 16, 0, 1 }, /* Буферные адреса слов */ -{ "БАС1", &BAS[1], 8, 16, 0, 1 }, -{ "БАС2", &BAS[2], 8, 16, 0, 1 }, -{ "БАС3", &BAS[3], 8, 16, 0, 1 }, -{ "БРСст", &BRSLRU, 8, 6, 0, 1, NULL, NULL, REG_HIDDEN}, -{ 0 } -}; - -#define CACHE_ENB 1 - -MTAB mmu_mod[] = { - { 1, 0, "NOCACHE", "NOCACHE" }, - { 1, 1, "CACHE", "CACHE" }, - { 0 } -}; - -t_stat mmu_reset (DEVICE *dptr); - -t_stat mmu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) -{ - mmu_print_brz(); - return SCPE_NOFNC; -} - -DEVICE mmu_dev = { - "MMU", &mmu_unit, mmu_reg, mmu_mod, - 1, 8, 3, 1, 8, 50, - &mmu_examine, NULL, &mmu_reset, - NULL, NULL, NULL, NULL, - DEV_DEBUG -}; - -/* - * Reset routine - */ -t_stat mmu_reset (DEVICE *dptr) -{ - int i; - for (i = 0; i < 8; ++i) { - BRZ[i] = BAZ[i] = RP[i] = 0; - } - TABST = 0; - OLDEST = 0; - FLUSH = 0; - RZ = 0; - /* - * Front panel switches survive the reset - */ - sim_cancel (&mmu_unit); - return SCPE_OK; -} - -#define loses_to_all(i) ((TABST & win_mask[i]) == 0 && \ - (TABST & lose_mask[i]) == lose_mask[i]) - -/* - * N wins over M if the bit is set - * M=1 2 3 4 5 6 7 - * N ------------------------- - * 0| 0 1 2 3 4 5 6 - * 1| 7 8 9 10 11 12 - * 2| 13 14 15 16 17 - * 3| 18 19 20 21 - * 4| 22 23 24 - * 5| 25 26 - * 6| 27 - */ - -static unsigned win_mask[8] = { - 0177, - 0077 << 7, - 0037 << 13, - 0017 << 18, - 0007 << 22, - 0003 << 25, - 0001 << 27, - 0 -}; - -static unsigned lose_mask[8] = { - 0, - 1<<0, - 1<<1|1<<7, - 1<<2|1<<8|1<<13, - 1<<3|1<<9|1<<14|1<<18, - 1<<4|1<<10|1<<15|1<<19|1<<22, - 1<<5|1<<11|1<<16|1<<20|1<<23|1<<25, - 1<<6|1<<12|1<<17|1<<21|1<<24|1<<26|1<<27 -}; - -#define set_wins(i) TABST = (TABST & ~lose_mask[i]) | win_mask[i] - -void mmu_protection_check (int addr) -{ - /* Защита блокируется в режиме супервизора для физических (!) адресов 1-7 (ТО-8) - WTF? */ - int tmp_prot_disabled = (M[PSW] & PSW_PROT_DISABLE) || - (IS_SUPERVISOR (RUU) && (M[PSW] & PSW_MMAP_DISABLE) && addr < 010); - - /* Защита не заблокирована, а лист закрыт */ - if (! tmp_prot_disabled && (RZ & (1 << (addr >> 10)))) { - iintr_data = addr >> 10; - if (mmu_dev.dctrl) - besm6_debug ("--- (%05o) защита числа", addr); - longjmp (cpu_halt, STOP_OPERAND_PROT); - } -} - -void mmu_flush (int idx) -{ - if (! BAZ[idx]) { - /* Был пуст после сброса или выталкивания */ - return; - } - /* Вычисляем физический адрес выталкиваемого БРЗ */ - int waddr = BAZ[idx]; - waddr = (waddr > 0100000) ? (waddr - 0100000) : - (waddr & 01777) | (TLB[waddr >> 10] << 10); - memory[waddr] = BRZ[idx]; - BAZ[idx] = 0; - if (sim_log && mmu_dev.dctrl) { - fprintf (sim_log, "--- (%05o) запись ", waddr); - fprint_sym (sim_log, 0, &BRZ[idx], 0, 0); - fprintf (sim_log, " из БРЗ[%d]\n", idx); - } -} - -void mmu_update_oldest () -{ - int i; - - for (i = 0; i < 8; ++i) { - if (loses_to_all(i)) { - OLDEST = i; - // fprintf(stderr, "Oldest = %d\r\n", i); - return; - } - } -} - -int mmu_match (int addr, int fail) -{ - int i; - - for (i = 0; i < 8; ++i) { - if (addr == BAZ[i]) { - return i; - } - } - return fail; -} - -/* - * Разнообразные алгоритмы выталкивания БРЗ путем записи - * по адресам пультовых регистров. Тест УУ проходит дальше всего - * с mmu_flush_by_age(). - */ -void mmu_flush_by_age() -{ - switch (FLUSH) { - case 0: - break; - case 1 ... 8: - set_wins (OLDEST); - mmu_update_oldest (); - mmu_flush (OLDEST); - if (FLUSH == 7) { - TABST = 0; - OLDEST = 0; - } - break; - } - ++FLUSH; -} - -void mmu_flush_by_number() -{ - switch (FLUSH) { - case 0: - break; - case 1 ... 8: - mmu_flush (FLUSH-1); - set_wins (FLUSH-1); - if (FLUSH-1 == OLDEST) - mmu_update_oldest (); - if (FLUSH == 7) { - TABST = 0; - OLDEST = 0; - } - break; - } - ++FLUSH; -} - -/* - * Запись слова в память - */ -void mmu_store (int addr, t_value val) -{ - int matching; - - addr &= BITS(15); - if (addr == 0) - return; - if (sim_log && mmu_dev.dctrl) { - fprintf (sim_log, "--- (%05o) запись ", addr); - fprint_sym (sim_log, 0, &val, 0, 0); - fprintf (sim_log, "\n"); - } - - mmu_protection_check (addr); - - /* Различаем адреса с припиской и без */ - if (M[PSW] & PSW_MMAP_DISABLE) - addr |= 0100000; - - /* ЗПСЧ: ЗП */ - if (M[DWP] == addr && (M[PSW] & PSW_WRITE_WATCH)) - longjmp(cpu_halt, STOP_STORE_ADDR_MATCH); - - if (sim_brk_summ & SWMASK('W') && - sim_brk_test (addr, SWMASK('W'))) - longjmp(cpu_halt, STOP_WWATCH); - - if (!(mmu_unit.flags & CACHE_ENB)) { - static int roundrobin; - int faked = (++roundrobin ^ addr ^ val) & 7; - - if (addr > 0100000 && addr < 0100010) - return; - - BRZ[faked] = SET_CONVOL (val, RUU ^ CONVOL_INSN); - BAZ[faked] = addr; - mmu_flush (faked); - return; - } - - /* Запись в тумблерные регистры - выталкивание БРЗ */ - if (addr > 0100000 && addr < 0100010) { - mmu_flush_by_age(); - return; - } else - FLUSH = 0; - - matching = mmu_match(addr, OLDEST); - - BRZ[matching] = SET_CONVOL (val, RUU ^ CONVOL_INSN); - BAZ[matching] = addr; - set_wins (matching); - - if (matching == OLDEST) { - mmu_update_oldest (); - mmu_flush (OLDEST); - } -} - -t_value mmu_memaccess (int addr) -{ - t_value val; - - /* Вычисляем физический адрес слова */ - addr = (addr > 0100000) ? (addr - 0100000) : - (addr & 01777) | (TLB[addr >> 10] << 10); - if (addr >= 010) { - /* Из памяти */ - val = memory[addr]; - } else { - /* С тумблерных регистров */ - if (mmu_dev.dctrl) - besm6_debug("--- (%05o) чтение ТР%o", PC, addr); - val = pult[addr]; - } - if (sim_log && (mmu_dev.dctrl || (cpu_dev.dctrl && sim_deb))) { - fprintf (sim_log, "--- (%05o) чтение ", addr & BITS(15)); - fprint_sym (sim_log, 0, &val, 0, 0); - fprintf (sim_log, "\n"); - } - - /* На тумблерных регистрах контроля числа не бывает */ - if (addr >= 010 && ! IS_NUMBER (val)) { - iintr_data = addr & 7; - besm6_debug ("--- (%05o) контроль числа", addr); - longjmp (cpu_halt, STOP_RAM_CHECK); - } - return val; -} - -/* - * Чтение операнда - */ -t_value mmu_load (int addr) -{ - int matching = -1; - t_value val; - - addr &= BITS(15); - if (addr == 0) - return 0; - - mmu_protection_check (addr); - - /* Различаем адреса с припиской и без */ - if (M[PSW] & PSW_MMAP_DISABLE) - addr |= 0100000; - - /* ЗПСЧ: СЧ */ - if (M[DWP] == addr && !(M[PSW] & PSW_WRITE_WATCH)) - longjmp(cpu_halt, STOP_LOAD_ADDR_MATCH); - - if (sim_brk_summ & SWMASK('R') && - sim_brk_test (addr, SWMASK('R'))) - longjmp(cpu_halt, STOP_RWATCH); - - if (!(mmu_unit.flags & CACHE_ENB)) { - return mmu_memaccess (addr) & BITS48; - } - - matching = mmu_match(addr, -1); - - if (matching == -1) { - val = mmu_memaccess (addr); - } else { - /* старшинство обновляется, только если оно не затрагивает - * старший БРЗ (ТО-2). - */ - if (matching != OLDEST) - set_wins (matching); - val = BRZ[matching]; - if (sim_log && (mmu_dev.dctrl || (cpu_dev.dctrl && sim_deb))) { - fprintf (sim_log, "--- (%05o) чтение ", addr & BITS(15)); - fprint_sym (sim_log, 0, &val, 0, 0); - fprintf (sim_log, " из БРЗ\n"); - } - if (! IS_NUMBER (val)) { - iintr_data = matching; - besm6_debug ("--- (%05o) контроль числа БРЗ", addr); - longjmp (cpu_halt, STOP_CACHE_CHECK); - } - } - return val & BITS48; -} - -/* A little BRS LRU table */ -#define brs_loses_to_all(i) ((BRSLRU & brs_win_mask[i]) == 0 && \ - (BRSLRU & brs_lose_mask[i]) == brs_lose_mask[i]) - -/* - * N wins over M if the bit is set - * M=1 2 3 - * N --------- - * 0| 0 1 2 - * 1| 3 4 - * 2| 5 - */ - -static unsigned brs_win_mask[4] = { - 07, - 03 << 3, - 01 << 5, - 0 -}; - -static unsigned brs_lose_mask[8] = { - 0, - 1<<0, - 1<<1|1<<3, - 1<<2|1<<4|1<<5 -}; - -#define brs_set_wins(i) BRSLRU = (BRSLRU & ~brs_lose_mask[i]) | brs_win_mask[i] - -void mmu_fetch_check (int addr) -{ - /* В режиме супервизора защиты нет */ - if (! IS_SUPERVISOR(RUU)) { - int page = TLB[addr >> 10]; - /* - * Для команд в режиме пользователя признак защиты - - * 0 в регистре приписки. - */ - if (page == 0) { - iintr_data = addr >> 10; - if (mmu_dev.dctrl) - besm6_debug ("--- (%05o) защита команды", addr); - longjmp (cpu_halt, STOP_INSN_PROT); - } - } -} - -/* - * Предвыборка команды на БРС - */ -t_value mmu_prefetch (int addr, int actual) -{ - t_value val; - int i; - - if (mmu_unit.flags & CACHE_ENB) { - for (i = 0; i < 4; ++i) { - if (BAS[i] == addr) { - if (actual) { - brs_set_wins (i); - } - return BRS[i]; - } - } - - for (i = 0; i < 4; ++i) { - if (brs_loses_to_all (i)) { - BAS[i] = addr; - if (actual) { - brs_set_wins (i); - } - break; - } - } - } else if (!actual) { - return 0; - } else { - /* Чтобы лампочки мигали */ - i = addr & 3; - } - - if (addr < 0100000) { - int page = TLB[addr >> 10]; - - /* Вычисляем физический адрес слова */ - addr = (addr & 01777) | (page << 10); - } else { - addr = addr & BITS(15); - } - - if (addr < 010) - val = pult[addr]; - else - val = memory[addr]; - BRS[i] = val; - return val; -} - -/* - * Выборка команды - */ -t_value mmu_fetch (int addr) -{ - t_value val; - - if (addr == 0) { - if (mmu_dev.dctrl) - besm6_debug ("--- передача управления на 0"); - longjmp (cpu_halt, STOP_INSN_CHECK); - } - - mmu_fetch_check(addr); - - /* Различаем адреса с припиской и без */ - if (IS_SUPERVISOR (RUU)) - addr |= 0100000; - - /* КРА */ - if (M[IBP] == addr) - longjmp(cpu_halt, STOP_INSN_ADDR_MATCH); - - val = mmu_prefetch(addr, 1); - - if (sim_log && mmu_dev.dctrl) { - fprintf (sim_log, "--- (%05o) выборка ", addr); - fprint_sym (sim_log, 0, &val, 0, SWMASK ('I')); - fprintf (sim_log, "\n"); - } - - /* Тумблерные регистры пока только с командной сверткой */ - if (addr >= 010 && ! IS_INSN (val)) { - besm6_debug ("--- (%05o) контроль команды", addr); - longjmp (cpu_halt, STOP_INSN_CHECK); - } - return val & BITS48; -} - -void mmu_setrp (int idx, t_value val) -{ - uint32 p0, p1, p2, p3; - const uint32 mask = (MEMSIZE >> 10) - 1; - - /* Младшие 5 разрядов 4-х регистров приписки упакованы - * по 5 в 1-20 рр, 6-е разряды - в 29-32 рр, 7-е разряды - в 33-36 рр и т.п. - */ - p0 = (val & 037) | (((val>>28) & 1) << 5) | (((val>>32) & 1) << 6) | - (((val>>36) & 1) << 7) | (((val>>40) & 1) << 8) | (((val>>44) & 1) << 9); - p1 = ((val>>5) & 037) | (((val>>29) & 1) << 5) | (((val>>33) & 1) << 6) | - (((val>>37) & 1) << 7) | (((val>>41) & 1) << 8) | (((val>>45) & 1) << 9); - p2 = ((val>>10) & 037) | (((val>>30) & 1) << 5) | (((val>>34) & 1) << 6) | - (((val>>38) & 1) << 7) | (((val>>42) & 1) << 8) | (((val>>46) & 1) << 9); - p3 = ((val>>15) & 037) | (((val>>31) & 1) << 5) | (((val>>35) & 1) << 6) | - (((val>>39) & 1) << 7) | (((val>>43) & 1) << 8) | (((val>>47) & 1) << 9); - - p0 &= mask; - p1 &= mask; - p2 &= mask; - p3 &= mask; - - RP[idx] = p0 | p1 << 12 | p2 << 24 | (t_value) p3 << 36; - TLB[idx*4] = p0; - TLB[idx*4+1] = p1; - TLB[idx*4+2] = p2; - TLB[idx*4+3] = p3; -} - -void mmu_setup () -{ - const uint32 mask = (MEMSIZE >> 10) - 1; - int i; - - /* Перепись РПi в TLBj. */ - for (i=0; i<8; ++i) { - TLB[i*4] = RP[i] & mask; - TLB[i*4+1] = RP[i] >> 12 & mask; - TLB[i*4+2] = RP[i] >> 24 & mask; - TLB[i*4+3] = RP[i] >> 36 & mask; - } -} - -void mmu_setprotection (int idx, t_value val) -{ - /* Разряды сумматора, записываемые в регистр защиты - 21-28 */ - int mask = 0xff << (idx * 8); - val = ((val >> 20) & 0xff) << (idx * 8); - RZ = (RZ & ~mask) | val; -} - -void mmu_setcache (int idx, t_value val) -{ - BRZ[idx] = SET_CONVOL (val, RUU ^ CONVOL_INSN); -} - -t_value mmu_getcache (int idx) -{ - return BRZ[idx] & BITS48; -} - -void mmu_print_brz () -{ - int i, k; - - for (i=7; i>=0; --i) { - besm6_log_cont ("БРЗ [%d] = '", i); - for (k=47; k>=0; --k) - besm6_log_cont ("%c", (BRZ[i] >> k & 1) ? '*' : ' '); - besm6_log ("'"); - } -} +/* + * besm6_mmu.c: BESM-6 fast write cache and TLB registers + *(стойка БРУС) + * + * Copyright (c) 2009, Leonid Broukhis + * + * 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 "besm6_defs.h" + +/* + * MMU data structures + * + * mmu_dev MMU device descriptor + * mmu_unit MMU unit descriptor + * mmu_reg MMU register list + */ +UNIT mmu_unit = { + UDATA (NULL, UNIT_FIX, 8) +}; + +t_value BRZ[8]; +uint32 BAZ[8], TABST, RZ, OLDEST, FLUSH; + +t_value BRS[4]; +uint32 BAS[4]; +uint32 BRSLRU; + +/* + * 64-битные регистры RP0-RP7 - для отображения регистров приписки, + * группами по 4 ради компактности, 12 бит на страницу. + * TLB0-TLB31 - постраничные регистры приписки, копии RPi. + * Обращение к памяти должно вестись через TLBi. + */ +t_value RP[8]; +uint32 TLB[32]; + +unsigned iintr_data; /* protected page number or parity check location */ + +t_value pult[8]; + +REG mmu_reg[] = { +{ "БРЗ0", &BRZ[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Буферные регистры записи */ +{ "БРЗ1", &BRZ[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ2", &BRZ[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ3", &BRZ[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ4", &BRZ[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ5", &BRZ[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ6", &BRZ[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРЗ7", &BRZ[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БАЗ0", &BAZ[0], 8, 16, 0, 1 }, /* Буферные адреса записи */ +{ "БАЗ1", &BAZ[1], 8, 16, 0, 1 }, +{ "БАЗ2", &BAZ[2], 8, 16, 0, 1 }, +{ "БАЗ3", &BAZ[3], 8, 16, 0, 1 }, +{ "БАЗ4", &BAZ[4], 8, 16, 0, 1 }, +{ "БАЗ5", &BAZ[5], 8, 16, 0, 1 }, +{ "БАЗ6", &BAZ[6], 8, 16, 0, 1 }, +{ "БАЗ7", &BAZ[7], 8, 16, 0, 1 }, +{ "ТАБСТ", &TABST, 8, 28, 0, 1, NULL, NULL, REG_HIDDEN },/* Таблица старшинства БРЗ */ +{ "ЗпТР", &FLUSH, 8, 4, 0, 1, NULL, NULL, REG_HIDDEN },/* Признак выталкивания БРЗ */ +{ "Старш", &OLDEST, 8, 3, 0, 1 }, /* Номер вытолкнутого БРЗ */ +{ "РП0", &RP[0], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* Регистры приписки, по 12 бит */ +{ "РП1", &RP[1], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП2", &RP[2], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП3", &RP[3], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП4", &RP[4], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП5", &RP[5], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП6", &RP[6], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РП7", &RP[7], 8, 48, 0, 1, NULL, NULL, REG_VMIO}, +{ "РЗ", &RZ, 8, 32, 0, 1 }, /* Регистр защиты */ +{ "ТР1", &pult[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Тумблерные регистры */ +{ "ТР2", &pult[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "ТР3", &pult[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "ТР4", &pult[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "ТР5", &pult[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "ТР6", &pult[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "ТР7", &pult[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРС0", &BRS[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, /* Буферные регистры слов */ +{ "БРС1", &BRS[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРС2", &BRS[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БРС3", &BRS[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO}, +{ "БАС0", &BAS[0], 8, 16, 0, 1 }, /* Буферные адреса слов */ +{ "БАС1", &BAS[1], 8, 16, 0, 1 }, +{ "БАС2", &BAS[2], 8, 16, 0, 1 }, +{ "БАС3", &BAS[3], 8, 16, 0, 1 }, +{ "БРСст", &BRSLRU, 8, 6, 0, 1, NULL, NULL, REG_HIDDEN}, +{ 0 } +}; + +#define CACHE_ENB 1 + +MTAB mmu_mod[] = { + { 1, 0, "NOCACHE", "NOCACHE" }, + { 1, 1, "CACHE", "CACHE" }, + { 0 } +}; + +t_stat mmu_reset (DEVICE *dptr); + +t_stat mmu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + mmu_print_brz(); + return SCPE_NOFNC; +} + +DEVICE mmu_dev = { + "MMU", &mmu_unit, mmu_reg, mmu_mod, + 1, 8, 3, 1, 8, 50, + &mmu_examine, NULL, &mmu_reset, + NULL, NULL, NULL, NULL, + DEV_DEBUG +}; + +/* + * Reset routine + */ +t_stat mmu_reset (DEVICE *dptr) +{ + int i; + for (i = 0; i < 8; ++i) { + BRZ[i] = BAZ[i] = RP[i] = 0; + } + TABST = 0; + OLDEST = 0; + FLUSH = 0; + RZ = 0; + /* + * Front panel switches survive the reset + */ + sim_cancel (&mmu_unit); + return SCPE_OK; +} + +#define loses_to_all(i) ((TABST & win_mask[i]) == 0 && \ + (TABST & lose_mask[i]) == lose_mask[i]) + +/* + * N wins over M if the bit is set + * M=1 2 3 4 5 6 7 + * N ------------------------- + * 0| 0 1 2 3 4 5 6 + * 1| 7 8 9 10 11 12 + * 2| 13 14 15 16 17 + * 3| 18 19 20 21 + * 4| 22 23 24 + * 5| 25 26 + * 6| 27 + */ + +static unsigned win_mask[8] = { + 0177, + 0077 << 7, + 0037 << 13, + 0017 << 18, + 0007 << 22, + 0003 << 25, + 0001 << 27, + 0 +}; + +static unsigned lose_mask[8] = { + 0, + 1<<0, + 1<<1|1<<7, + 1<<2|1<<8|1<<13, + 1<<3|1<<9|1<<14|1<<18, + 1<<4|1<<10|1<<15|1<<19|1<<22, + 1<<5|1<<11|1<<16|1<<20|1<<23|1<<25, + 1<<6|1<<12|1<<17|1<<21|1<<24|1<<26|1<<27 +}; + +#define set_wins(i) TABST = (TABST & ~lose_mask[i]) | win_mask[i] + +void mmu_protection_check (int addr) +{ + /* Защита блокируется в режиме супервизора для физических (!) адресов 1-7 (ТО-8) - WTF? */ + int tmp_prot_disabled = (M[PSW] & PSW_PROT_DISABLE) || + (IS_SUPERVISOR (RUU) && (M[PSW] & PSW_MMAP_DISABLE) && addr < 010); + + /* Защита не заблокирована, а лист закрыт */ + if (! tmp_prot_disabled && (RZ & (1 << (addr >> 10)))) { + iintr_data = addr >> 10; + if (mmu_dev.dctrl) + besm6_debug ("--- (%05o) защита числа", addr); + longjmp (cpu_halt, STOP_OPERAND_PROT); + } +} + +void mmu_flush (int idx) +{ + if (! BAZ[idx]) { + /* Был пуст после сброса или выталкивания */ + return; + } + /* Вычисляем физический адрес выталкиваемого БРЗ */ + int waddr = BAZ[idx]; + waddr = (waddr > 0100000) ? (waddr - 0100000) : + (waddr & 01777) | (TLB[waddr >> 10] << 10); + memory[waddr] = BRZ[idx]; + BAZ[idx] = 0; + if (sim_log && mmu_dev.dctrl) { + fprintf (sim_log, "--- (%05o) запись ", waddr); + fprint_sym (sim_log, 0, &BRZ[idx], 0, 0); + fprintf (sim_log, " из БРЗ[%d]\n", idx); + } +} + +void mmu_update_oldest () +{ + int i; + + for (i = 0; i < 8; ++i) { + if (loses_to_all(i)) { + OLDEST = i; + // fprintf(stderr, "Oldest = %d\r\n", i); + return; + } + } +} + +int mmu_match (int addr, int fail) +{ + int i; + + for (i = 0; i < 8; ++i) { + if (addr == BAZ[i]) { + return i; + } + } + return fail; +} + +/* + * Разнообразные алгоритмы выталкивания БРЗ путем записи + * по адресам пультовых регистров. Тест УУ проходит дальше всего + * с mmu_flush_by_age(). + */ +void mmu_flush_by_age() +{ + switch (FLUSH) { + case 0: + break; + case 1 ... 8: + set_wins (OLDEST); + mmu_update_oldest (); + mmu_flush (OLDEST); + if (FLUSH == 7) { + TABST = 0; + OLDEST = 0; + } + break; + } + ++FLUSH; +} + +void mmu_flush_by_number() +{ + switch (FLUSH) { + case 0: + break; + case 1 ... 8: + mmu_flush (FLUSH-1); + set_wins (FLUSH-1); + if (FLUSH-1 == OLDEST) + mmu_update_oldest (); + if (FLUSH == 7) { + TABST = 0; + OLDEST = 0; + } + break; + } + ++FLUSH; +} + +/* + * Запись слова в память + */ +void mmu_store (int addr, t_value val) +{ + int matching; + + addr &= BITS(15); + if (addr == 0) + return; + if (sim_log && mmu_dev.dctrl) { + fprintf (sim_log, "--- (%05o) запись ", addr); + fprint_sym (sim_log, 0, &val, 0, 0); + fprintf (sim_log, "\n"); + } + + mmu_protection_check (addr); + + /* Различаем адреса с припиской и без */ + if (M[PSW] & PSW_MMAP_DISABLE) + addr |= 0100000; + + /* ЗПСЧ: ЗП */ + if (M[DWP] == addr && (M[PSW] & PSW_WRITE_WATCH)) + longjmp(cpu_halt, STOP_STORE_ADDR_MATCH); + + if (sim_brk_summ & SWMASK('W') && + sim_brk_test (addr, SWMASK('W'))) + longjmp(cpu_halt, STOP_WWATCH); + + if (!(mmu_unit.flags & CACHE_ENB)) { + static int roundrobin; + int faked = (++roundrobin ^ addr ^ val) & 7; + + if (addr > 0100000 && addr < 0100010) + return; + + BRZ[faked] = SET_CONVOL (val, RUU ^ CONVOL_INSN); + BAZ[faked] = addr; + mmu_flush (faked); + return; + } + + /* Запись в тумблерные регистры - выталкивание БРЗ */ + if (addr > 0100000 && addr < 0100010) { + mmu_flush_by_age(); + return; + } else + FLUSH = 0; + + matching = mmu_match(addr, OLDEST); + + BRZ[matching] = SET_CONVOL (val, RUU ^ CONVOL_INSN); + BAZ[matching] = addr; + set_wins (matching); + + if (matching == OLDEST) { + mmu_update_oldest (); + mmu_flush (OLDEST); + } +} + +t_value mmu_memaccess (int addr) +{ + t_value val; + + /* Вычисляем физический адрес слова */ + addr = (addr > 0100000) ? (addr - 0100000) : + (addr & 01777) | (TLB[addr >> 10] << 10); + if (addr >= 010) { + /* Из памяти */ + val = memory[addr]; + } else { + /* С тумблерных регистров */ + if (mmu_dev.dctrl) + besm6_debug("--- (%05o) чтение ТР%o", PC, addr); + val = pult[addr]; + } + if (sim_log && (mmu_dev.dctrl || (cpu_dev.dctrl && sim_deb))) { + fprintf (sim_log, "--- (%05o) чтение ", addr & BITS(15)); + fprint_sym (sim_log, 0, &val, 0, 0); + fprintf (sim_log, "\n"); + } + + /* На тумблерных регистрах контроля числа не бывает */ + if (addr >= 010 && ! IS_NUMBER (val)) { + iintr_data = addr & 7; + besm6_debug ("--- (%05o) контроль числа", addr); + longjmp (cpu_halt, STOP_RAM_CHECK); + } + return val; +} + +/* + * Чтение операнда + */ +t_value mmu_load (int addr) +{ + int matching = -1; + t_value val; + + addr &= BITS(15); + if (addr == 0) + return 0; + + mmu_protection_check (addr); + + /* Различаем адреса с припиской и без */ + if (M[PSW] & PSW_MMAP_DISABLE) + addr |= 0100000; + + /* ЗПСЧ: СЧ */ + if (M[DWP] == addr && !(M[PSW] & PSW_WRITE_WATCH)) + longjmp(cpu_halt, STOP_LOAD_ADDR_MATCH); + + if (sim_brk_summ & SWMASK('R') && + sim_brk_test (addr, SWMASK('R'))) + longjmp(cpu_halt, STOP_RWATCH); + + if (!(mmu_unit.flags & CACHE_ENB)) { + return mmu_memaccess (addr) & BITS48; + } + + matching = mmu_match(addr, -1); + + if (matching == -1) { + val = mmu_memaccess (addr); + } else { + /* старшинство обновляется, только если оно не затрагивает + * старший БРЗ (ТО-2). + */ + if (matching != OLDEST) + set_wins (matching); + val = BRZ[matching]; + if (sim_log && (mmu_dev.dctrl || (cpu_dev.dctrl && sim_deb))) { + fprintf (sim_log, "--- (%05o) чтение ", addr & BITS(15)); + fprint_sym (sim_log, 0, &val, 0, 0); + fprintf (sim_log, " из БРЗ\n"); + } + if (! IS_NUMBER (val)) { + iintr_data = matching; + besm6_debug ("--- (%05o) контроль числа БРЗ", addr); + longjmp (cpu_halt, STOP_CACHE_CHECK); + } + } + return val & BITS48; +} + +/* A little BRS LRU table */ +#define brs_loses_to_all(i) ((BRSLRU & brs_win_mask[i]) == 0 && \ + (BRSLRU & brs_lose_mask[i]) == brs_lose_mask[i]) + +/* + * N wins over M if the bit is set + * M=1 2 3 + * N --------- + * 0| 0 1 2 + * 1| 3 4 + * 2| 5 + */ + +static unsigned brs_win_mask[4] = { + 07, + 03 << 3, + 01 << 5, + 0 +}; + +static unsigned brs_lose_mask[8] = { + 0, + 1<<0, + 1<<1|1<<3, + 1<<2|1<<4|1<<5 +}; + +#define brs_set_wins(i) BRSLRU = (BRSLRU & ~brs_lose_mask[i]) | brs_win_mask[i] + +void mmu_fetch_check (int addr) +{ + /* В режиме супервизора защиты нет */ + if (! IS_SUPERVISOR(RUU)) { + int page = TLB[addr >> 10]; + /* + * Для команд в режиме пользователя признак защиты - + * 0 в регистре приписки. + */ + if (page == 0) { + iintr_data = addr >> 10; + if (mmu_dev.dctrl) + besm6_debug ("--- (%05o) защита команды", addr); + longjmp (cpu_halt, STOP_INSN_PROT); + } + } +} + +/* + * Предвыборка команды на БРС + */ +t_value mmu_prefetch (int addr, int actual) +{ + t_value val; + int i; + + if (mmu_unit.flags & CACHE_ENB) { + for (i = 0; i < 4; ++i) { + if (BAS[i] == addr) { + if (actual) { + brs_set_wins (i); + } + return BRS[i]; + } + } + + for (i = 0; i < 4; ++i) { + if (brs_loses_to_all (i)) { + BAS[i] = addr; + if (actual) { + brs_set_wins (i); + } + break; + } + } + } else if (!actual) { + return 0; + } else { + /* Чтобы лампочки мигали */ + i = addr & 3; + } + + if (addr < 0100000) { + int page = TLB[addr >> 10]; + + /* Вычисляем физический адрес слова */ + addr = (addr & 01777) | (page << 10); + } else { + addr = addr & BITS(15); + } + + if (addr < 010) + val = pult[addr]; + else + val = memory[addr]; + BRS[i] = val; + return val; +} + +/* + * Выборка команды + */ +t_value mmu_fetch (int addr) +{ + t_value val; + + if (addr == 0) { + if (mmu_dev.dctrl) + besm6_debug ("--- передача управления на 0"); + longjmp (cpu_halt, STOP_INSN_CHECK); + } + + mmu_fetch_check(addr); + + /* Различаем адреса с припиской и без */ + if (IS_SUPERVISOR (RUU)) + addr |= 0100000; + + /* КРА */ + if (M[IBP] == addr) + longjmp(cpu_halt, STOP_INSN_ADDR_MATCH); + + val = mmu_prefetch(addr, 1); + + if (sim_log && mmu_dev.dctrl) { + fprintf (sim_log, "--- (%05o) выборка ", addr); + fprint_sym (sim_log, 0, &val, 0, SWMASK ('I')); + fprintf (sim_log, "\n"); + } + + /* Тумблерные регистры пока только с командной сверткой */ + if (addr >= 010 && ! IS_INSN (val)) { + besm6_debug ("--- (%05o) контроль команды", addr); + longjmp (cpu_halt, STOP_INSN_CHECK); + } + return val & BITS48; +} + +void mmu_setrp (int idx, t_value val) +{ + uint32 p0, p1, p2, p3; + const uint32 mask = (MEMSIZE >> 10) - 1; + + /* Младшие 5 разрядов 4-х регистров приписки упакованы + * по 5 в 1-20 рр, 6-е разряды - в 29-32 рр, 7-е разряды - в 33-36 рр и т.п. + */ + p0 = (val & 037) | (((val>>28) & 1) << 5) | (((val>>32) & 1) << 6) | + (((val>>36) & 1) << 7) | (((val>>40) & 1) << 8) | (((val>>44) & 1) << 9); + p1 = ((val>>5) & 037) | (((val>>29) & 1) << 5) | (((val>>33) & 1) << 6) | + (((val>>37) & 1) << 7) | (((val>>41) & 1) << 8) | (((val>>45) & 1) << 9); + p2 = ((val>>10) & 037) | (((val>>30) & 1) << 5) | (((val>>34) & 1) << 6) | + (((val>>38) & 1) << 7) | (((val>>42) & 1) << 8) | (((val>>46) & 1) << 9); + p3 = ((val>>15) & 037) | (((val>>31) & 1) << 5) | (((val>>35) & 1) << 6) | + (((val>>39) & 1) << 7) | (((val>>43) & 1) << 8) | (((val>>47) & 1) << 9); + + p0 &= mask; + p1 &= mask; + p2 &= mask; + p3 &= mask; + + RP[idx] = p0 | p1 << 12 | p2 << 24 | (t_value) p3 << 36; + TLB[idx*4] = p0; + TLB[idx*4+1] = p1; + TLB[idx*4+2] = p2; + TLB[idx*4+3] = p3; +} + +void mmu_setup () +{ + const uint32 mask = (MEMSIZE >> 10) - 1; + int i; + + /* Перепись РПi в TLBj. */ + for (i=0; i<8; ++i) { + TLB[i*4] = RP[i] & mask; + TLB[i*4+1] = RP[i] >> 12 & mask; + TLB[i*4+2] = RP[i] >> 24 & mask; + TLB[i*4+3] = RP[i] >> 36 & mask; + } +} + +void mmu_setprotection (int idx, t_value val) +{ + /* Разряды сумматора, записываемые в регистр защиты - 21-28 */ + int mask = 0xff << (idx * 8); + val = ((val >> 20) & 0xff) << (idx * 8); + RZ = (RZ & ~mask) | val; +} + +void mmu_setcache (int idx, t_value val) +{ + BRZ[idx] = SET_CONVOL (val, RUU ^ CONVOL_INSN); +} + +t_value mmu_getcache (int idx) +{ + return BRZ[idx] & BITS48; +} + +void mmu_print_brz () +{ + int i, k; + + for (i=7; i>=0; --i) { + besm6_log_cont ("БРЗ [%d] = '", i); + for (k=47; k>=0; --k) + besm6_log_cont ("%c", (BRZ[i] >> k & 1) ? '*' : ' '); + besm6_log ("'"); + } +} diff --git a/BESM6/besm6_panel.c b/BESM6/besm6_panel.c index 6ed387e8..6b673ab1 100644 --- a/BESM6/besm6_panel.c +++ b/BESM6/besm6_panel.c @@ -1,594 +1,594 @@ -/* - * Panel of BESM-6, displayed as a graphics window. - * Using libSDL for graphics and libSDL_ttf for fonts. - * - * Copyright (c) 2009, Serge Vakulenko - * Copyright (c) 2014, Leonid Broukhis - * - * 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. - */ -#ifdef HAVE_LIBSDL - -#include "besm6_defs.h" -#include -#include - -/* - * Use a 640x480 window with 32 bit pixels. - */ -#define WIDTH 800 -#define HEIGHT 400 -#define DEPTH 32 - -#define STEPX 14 -#define STEPY 16 - -#define FONTNAME "LucidaSansRegular.ttf" -#define FONTPATH1 "/usr/share/fonts" -#define FONTPATH2 "/usr/lib/jvm" -#define FONTPATH3 "/System/Library/Frameworks/JavaVM.framework/Versions" - -#include -#include - -/* Data and functions that don't depend on SDL version */ -static char *font_path; -static TTF_Font *font_big; -static TTF_Font *font_small; -static SDL_Color foreground; -static SDL_Color background; -static const SDL_Color white = { 255, 255, 255 }; -static const SDL_Color black = { 0, 0, 0 }; -static const SDL_Color cyan = { 0, 128, 128 }; -static const SDL_Color grey = { 64, 64, 64 }; -static t_value old_BRZ [8], old_GRP [2]; -static t_value old_M [NREGS]; - -static const int regnum[] = { - 013, 012, 011, 010, 7, 6, 5, 4, - 027, 016, 015, 014, 3, 2, 1, 020, -}; - -static SDL_Surface *screen; - -/* - * Рисование текста в кодировке UTF-8, с антиалиасингом. - * Параметр halign задаёт выравнивание по горизонтали. - * Цвета заданы глобальными переменными foreground и background. - */ -static void render_utf8 (TTF_Font *font, int x, int y, int halign, char *message) -{ - SDL_Surface *text; - SDL_Rect area; - - /* Build image from text */ - text = TTF_RenderUTF8_Shaded (font, message, foreground, background); - - area.x = x; - if (halign < 0) - area.x -= text->w; /* align left */ - else if (halign == 0) - area.x -= text->w / 2; /* center */ - area.y = y; - area.w = text->w; - area.h = text->h; - - /* Put text image to screen */ - SDL_BlitSurface (text, 0, screen, &area); - SDL_FreeSurface (text); -} - -static SDL_Surface *sprite_from_data (int width, int height, - const unsigned char *data) -{ - SDL_Surface *sprite, *optimized; - unsigned *s, r, g, b, y, x; - - sprite = SDL_CreateRGBSurface (SDL_SWSURFACE, - width, height, DEPTH, 0, 0, 0, 0); - /* - optimized = SDL_DisplayFormat (sprite); - SDL_FreeSurface (sprite); - sprite = optimized; - */ - SDL_LockSurface (sprite); - for (y=0; ypixels + y * sprite->pitch); - for (x=0; xformat, r, g, b); - } - } - SDL_UnlockSurface (sprite); - return sprite; -} - -/* - * Рисуем неонку. - */ -static void draw_lamp (int left, int top, int on) -{ - /* Images created by GIMP: save as C file without alpha channel. */ - static const int lamp_width = 12; - static const int lamp_height = 12; - static const unsigned char lamp_on [12 * 12 * 3 + 1] = - "\0\0\0\0\0\0\0\0\0\13\2\2-\14\14e\31\31e\31\31-\14\14\13\2\2\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0\0D\20\20\313,,\377??\377CC\377CC\377DD\31333D\21\21\0\0" - "\0\0\0\0\0\0\0D\20\20\357LL\377\243\243\376~~\37699\376@@\376@@\377AA\357" - "<>\377@@\377@@\377AA\31333\13\2\2-\14\14\377??\376~~\377\356\356\377\321" - "\321\377<<\377??\377@@\377@@\376@@\377DD-\14\14e\31\31\377CC\37699\377NN" - "\377<<\377??\377@@\377@@\377@@\376??\377CCe\31\31e\31\31\377CC\376@@\377" - ">>\377??\377@@\377@@\377@@\377@@\376??\377CCe\31\31-\14\14\377DD\376@@\377" - "@@\377@@\377@@\377@@\377@@\377@@\376@@\377DD-\14\14\13\2\2\31333\377AA\377" - "@@\377@@\377@@\377@@\377@@\377@@\377AA\31333\13\2\2\0\0\0D\21\21\357<<\377" - "AA\376@@\376??\376??\376@@\377AA\357<> (14-x) & 1); - } - } -} - -/* - * Отрисовка лампочек ГРП и МГРП. - */ -static void draw_grp_periodic (int top) -{ - int x, y; - t_value val; - - for (y=0; y<2; ++y) { - val = y ? MGRP : GRP; - if (val == old_GRP [y]) - continue; - old_GRP [y] = val; - for (x=0; x<48; ++x) { - draw_lamp (100 + x*STEPX, top+28 + y*STEPY, val >> (47-x) & 1); - } - } -} - -/* - * Отрисовка лампочек БРЗ. - */ -static void draw_brz_periodic (int top) -{ - int x, y; - t_value val; - - for (y=0; y<8; ++y) { - val = BRZ [7-y]; - if (val == old_BRZ [7-y]) - continue; - old_BRZ [7-y] = val; - for (x=0; x<48; ++x) { - draw_lamp (100 + x*STEPX, top+28 + y*STEPY, val >> (47-x) & 1); - } - } -} - -/* - * Отрисовка статичной части регистров-модификаторов. - */ -static void draw_modifiers_static (int group, int left, int top) -{ - int x, y, color, reg; - char message [40]; - SDL_Rect area; - - background = black; - foreground = cyan; - - /* Оттеняем группы разрядов. */ - color = grey.r << 16 | grey.g << 8 | grey.b; - for (x=3; x<15; x+=3) { - area.x = left + 74 + x*STEPX; - area.y = top + 26; - area.w = 2; - area.h = 8*STEPY + 2; - SDL_FillRect (screen, &area, color); - } - /* Названия регистров. */ - for (y=0; y<8; ++y) { - reg = regnum [y + group*8]; - sprintf (message, "М%2o", reg); - render_utf8 (font_big, left, top + 24 + y*STEPY, 1, message); - old_M [reg] = ~0; - } - /* Номера битов. */ - for (x=0; x<15; ++x) { - sprintf (message, "%d", 15-x); - render_utf8 (font_small, left+82 + x*STEPX, - (x & 1) ? top+4 : top+10, 0, message); - } -} - -/* - * Отрисовка статичной части регистров ГРП и МГРП. - */ -static void draw_grp_static (int top) -{ - int x, y, color; - char message [40]; - SDL_Rect area; - - background = black; - foreground = cyan; - - /* Оттеняем группы разрядов. */ - color = grey.r << 16 | grey.g << 8 | grey.b; - for (x=3; x<48; x+=3) { - area.x = 98 + x*STEPX; - area.y = top + 26; - area.w = 2; - area.h = 2*STEPY + 2; - SDL_FillRect (screen, &area, color); - } - /* Названия регистров. */ - for (y=0; y<2; ++y) { - render_utf8 (font_big, 24, top + 24 + y*STEPY, 1, y ? "МГРП" : "ГРП"); - old_GRP[y] = ~0; - } - /* Номера битов. */ - for (x=0; x<48; ++x) { - sprintf (message, "%d", 48-x); - render_utf8 (font_small, 106 + x*STEPX, - (x & 1) ? top+10 : top+4, 0, message); - } -} - -/* - * Отрисовка статичной части регистров БРЗ. - */ -static void draw_brz_static (int top) -{ - int x, y, color; - char message [40]; - SDL_Rect area; - - background = black; - foreground = cyan; - - /* Оттеняем группы разрядов. */ - color = grey.r << 16 | grey.g << 8 | grey.b; - for (x=3; x<48; x+=3) { - area.x = 98 + x*STEPX; - area.y = top + 26; - area.w = 2; - area.h = 8*STEPY + 2; - SDL_FillRect (screen, &area, color); - } - /* Названия регистров. */ - for (y=7; y>=0; --y) { - sprintf (message, "БРЗ %d", 7-y); - render_utf8 (font_big, 24, top + 24 + y*STEPY, 1, message); - old_BRZ[y] = ~0; - } -} - -/* - * Поиск файла шрифта по имени. - */ -static int probe_font (const char *path, const struct stat *st, int flag) -{ - const char *p; - - if (flag != FTW_F) - return 0; - p = path + strlen (path) - strlen (FONTNAME); - if (p < path || strcmp (p, FONTNAME) != 0) - return 0; - font_path = strdup (path); - return 1; -} - -/* - * Закрываем графическое окно. - */ -void besm6_close_panel () -{ - if (! screen) - return; - TTF_Quit(); - SDL_Quit(); - screen = 0; -} - -#if SDL_MAJOR_VERSION == 2 - -static SDL_Window *sdlWindow; -static SDL_Renderer *sdlRenderer; -static SDL_Texture *sdlTexture; - - -/* - * Начальная инициализация графического окна и шрифтов. - */ -static void init_panel () -{ - if (sim_switches & SWMASK('Q')) - return; - - /* Initialize SDL subsystems - in this case, only video. */ - if (SDL_Init (SDL_INIT_VIDEO) < 0) { - fprintf (stderr, "SDL: unable to init: %s\n", - SDL_GetError ()); - exit (1); - } - sdlWindow = SDL_CreateWindow ("BESM-6 panel", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - WIDTH, HEIGHT, 0 /* regular window */); - if (! sdlWindow) { - fprintf (stderr, "SDL: unable to set %dx%dx%d mode: %s\n", - WIDTH, HEIGHT, DEPTH, SDL_GetError ()); - exit (1); - } - - sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0); - /* Make black background */ - SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255); - SDL_RenderClear(sdlRenderer); - - /* Initialize the TTF library */ - if (TTF_Init() < 0) { - fprintf (stderr, "SDL: couldn't initialize TTF: %s\n", - SDL_GetError()); - SDL_Quit(); - exit (1); - } - - /* Find font file */ - if (ftw (FONTPATH1, probe_font, 255) <= 0 && - ftw (FONTPATH2, probe_font, 255) <= 0 && - ftw (FONTPATH3, probe_font, 255) <= 0) { - fprintf(stderr, "SDL: couldn't find font %s in directory %s\n", - FONTNAME, FONTPATH1); - besm6_close_panel(); - exit (1); - } - - /* Open the font file with the requested point size */ - font_big = TTF_OpenFont (font_path, 16); - font_small = TTF_OpenFont (font_path, 9); - if (! font_big || ! font_small) { - fprintf(stderr, "SDL: couldn't load font %s: %s\n", - font_path, SDL_GetError()); - besm6_close_panel(); - exit (1); - } - atexit (besm6_close_panel); - - screen = SDL_CreateRGBSurface(0, WIDTH, HEIGHT, 32, - 0x00FF0000, - 0x0000FF00, - 0x000000FF, - 0xFF000000); - - sdlTexture = SDL_CreateTexture(sdlRenderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, - WIDTH, HEIGHT); - - /* Отрисовка статичной части панели БЭСМ-6. */ - draw_modifiers_static (0, 24, 10); - draw_modifiers_static (1, 400, 10); - draw_grp_static (180); - draw_brz_static (230); - - /* Tell SDL to update the whole screen */ - SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch); - SDL_RenderClear(sdlRenderer); - SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); - SDL_RenderPresent (sdlRenderer); -} - -void (*sim_vm_init)() = init_panel; - -/* - * Обновляем графическое окно. - */ -void besm6_draw_panel () -{ - if (! screen) - return; - - /* Периодическая отрисовка: мигание лампочек. */ - draw_modifiers_periodic (0, 24, 10); - draw_modifiers_periodic (1, 400, 10); - draw_grp_periodic (180); - draw_brz_periodic (230); - - /* Tell SDL to update the whole screen */ - SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch); - SDL_RenderClear(sdlRenderer); - SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); - SDL_RenderPresent (sdlRenderer); - - /* Exit SIMH when window closed.*/ - SDL_Event event; - if (SDL_PollEvent (&event) && event.type == SDL_QUIT) - longjmp (cpu_halt, SCPE_STOP); -} - -#else - -/* - * Начальная инициализация графического окна и шрифтов. - */ -static void init_panel () -{ - if (sim_switches & SWMASK('Q')) - return; - - /* Initialize SDL subsystems - in this case, only video. */ - if (SDL_Init (SDL_INIT_VIDEO) < 0) { - fprintf (stderr, "SDL: unable to init: %s\n", - SDL_GetError ()); - exit (1); - } - screen = SDL_SetVideoMode (WIDTH, HEIGHT, DEPTH, SDL_SWSURFACE); - if (! screen) { - fprintf (stderr, "SDL: unable to set %dx%dx%d mode: %s\n", - WIDTH, HEIGHT, DEPTH, SDL_GetError ()); - exit (1); - } - - /* Initialize the TTF library */ - if (TTF_Init() < 0) { - fprintf (stderr, "SDL: couldn't initialize TTF: %s\n", - SDL_GetError()); - SDL_Quit(); - exit (1); - } - - /* Find font file */ - if (ftw (FONTPATH1, probe_font, 255) <= 0 && - ftw (FONTPATH2, probe_font, 255) <= 0 && - ftw (FONTPATH3, probe_font, 255) <= 0) { - fprintf(stderr, "SDL: couldn't find font %s in directory %s\n", - FONTNAME, FONTPATH1); - besm6_close_panel(); - exit (1); - } - - /* Open the font file with the requested point size */ - font_big = TTF_OpenFont (font_path, 16); - font_small = TTF_OpenFont (font_path, 9); - if (! font_big || ! font_small) { - fprintf(stderr, "SDL: couldn't load font %s: %s\n", - FONTNAME, TTF_GetError()); - besm6_close_panel(); - exit (1); - } - atexit (besm6_close_panel); - - /* Отрисовка статичной части панели БЭСМ-6. */ - draw_modifiers_static (0, 24, 10); - draw_modifiers_static (1, 400, 10); - draw_grp_static (180); - draw_brz_static (230); - - /* Tell SDL to update the whole screen */ - SDL_UpdateRect (screen, 0, 0, WIDTH, HEIGHT); -} - -void (*sim_vm_init)() = init_panel; - -/* - * Обновляем графическое окно. - */ -void besm6_draw_panel () -{ - if (! screen) - return; - - /* Периодическая отрисовка: мигание лампочек. */ - draw_modifiers_periodic (0, 24, 10); - draw_modifiers_periodic (1, 400, 10); - draw_grp_periodic (180); - draw_brz_periodic (230); - - /* Tell SDL to update the whole screen */ - SDL_UpdateRect (screen, 0, 0, WIDTH, HEIGHT); - - /* Exit SIMH when window closed.*/ - SDL_Event event; - if (SDL_PollEvent (&event) && event.type == SDL_QUIT) - longjmp (cpu_halt, SCPE_STOP); -} - -#if !defined(__WIN32__) && \ - !(defined(__MWERKS__) && !defined(__BEOS__)) && \ - !defined(__MACOS__) && !defined(__MACOSX__) && \ - !defined(__SYMBIAN32__) && !defined(QWS) -#undef main - -/* - * Вот так всё непросто. - */ -int main (int argc, char *argv[]) -{ - extern int SDL_main (int, char**); - - return SDL_main (argc, argv); -} -#endif -#endif /* SDL_MAJOR_VERSION */ - -#else /* HAVE_LIBSDL */ -void besm6_draw_panel () -{ -} -#endif /* HAVE_LIBSDL */ +/* + * Panel of BESM-6, displayed as a graphics window. + * Using libSDL for graphics and libSDL_ttf for fonts. + * + * Copyright (c) 2009, Serge Vakulenko + * Copyright (c) 2014, Leonid Broukhis + * + * 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. + */ +#ifdef HAVE_LIBSDL + +#include "besm6_defs.h" +#include +#include + +/* + * Use a 640x480 window with 32 bit pixels. + */ +#define WIDTH 800 +#define HEIGHT 400 +#define DEPTH 32 + +#define STEPX 14 +#define STEPY 16 + +#define FONTNAME "LucidaSansRegular.ttf" +#define FONTPATH1 "/usr/share/fonts" +#define FONTPATH2 "/usr/lib/jvm" +#define FONTPATH3 "/System/Library/Frameworks/JavaVM.framework/Versions" + +#include +#include + +/* Data and functions that don't depend on SDL version */ +static char *font_path; +static TTF_Font *font_big; +static TTF_Font *font_small; +static SDL_Color foreground; +static SDL_Color background; +static const SDL_Color white = { 255, 255, 255 }; +static const SDL_Color black = { 0, 0, 0 }; +static const SDL_Color cyan = { 0, 128, 128 }; +static const SDL_Color grey = { 64, 64, 64 }; +static t_value old_BRZ [8], old_GRP [2]; +static t_value old_M [NREGS]; + +static const int regnum[] = { + 013, 012, 011, 010, 7, 6, 5, 4, + 027, 016, 015, 014, 3, 2, 1, 020, +}; + +static SDL_Surface *screen; + +/* + * Рисование текста в кодировке UTF-8, с антиалиасингом. + * Параметр halign задаёт выравнивание по горизонтали. + * Цвета заданы глобальными переменными foreground и background. + */ +static void render_utf8 (TTF_Font *font, int x, int y, int halign, char *message) +{ + SDL_Surface *text; + SDL_Rect area; + + /* Build image from text */ + text = TTF_RenderUTF8_Shaded (font, message, foreground, background); + + area.x = x; + if (halign < 0) + area.x -= text->w; /* align left */ + else if (halign == 0) + area.x -= text->w / 2; /* center */ + area.y = y; + area.w = text->w; + area.h = text->h; + + /* Put text image to screen */ + SDL_BlitSurface (text, 0, screen, &area); + SDL_FreeSurface (text); +} + +static SDL_Surface *sprite_from_data (int width, int height, + const unsigned char *data) +{ + SDL_Surface *sprite, *optimized; + unsigned *s, r, g, b, y, x; + + sprite = SDL_CreateRGBSurface (SDL_SWSURFACE, + width, height, DEPTH, 0, 0, 0, 0); + /* + optimized = SDL_DisplayFormat (sprite); + SDL_FreeSurface (sprite); + sprite = optimized; + */ + SDL_LockSurface (sprite); + for (y=0; ypixels + y * sprite->pitch); + for (x=0; xformat, r, g, b); + } + } + SDL_UnlockSurface (sprite); + return sprite; +} + +/* + * Рисуем неонку. + */ +static void draw_lamp (int left, int top, int on) +{ + /* Images created by GIMP: save as C file without alpha channel. */ + static const int lamp_width = 12; + static const int lamp_height = 12; + static const unsigned char lamp_on [12 * 12 * 3 + 1] = + "\0\0\0\0\0\0\0\0\0\13\2\2-\14\14e\31\31e\31\31-\14\14\13\2\2\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0D\20\20\313,,\377??\377CC\377CC\377DD\31333D\21\21\0\0" + "\0\0\0\0\0\0\0D\20\20\357LL\377\243\243\376~~\37699\376@@\376@@\377AA\357" + "<>\377@@\377@@\377AA\31333\13\2\2-\14\14\377??\376~~\377\356\356\377\321" + "\321\377<<\377??\377@@\377@@\376@@\377DD-\14\14e\31\31\377CC\37699\377NN" + "\377<<\377??\377@@\377@@\377@@\376??\377CCe\31\31e\31\31\377CC\376@@\377" + ">>\377??\377@@\377@@\377@@\377@@\376??\377CCe\31\31-\14\14\377DD\376@@\377" + "@@\377@@\377@@\377@@\377@@\377@@\376@@\377DD-\14\14\13\2\2\31333\377AA\377" + "@@\377@@\377@@\377@@\377@@\377@@\377AA\31333\13\2\2\0\0\0D\21\21\357<<\377" + "AA\376@@\376??\376??\376@@\377AA\357<> (14-x) & 1); + } + } +} + +/* + * Отрисовка лампочек ГРП и МГРП. + */ +static void draw_grp_periodic (int top) +{ + int x, y; + t_value val; + + for (y=0; y<2; ++y) { + val = y ? MGRP : GRP; + if (val == old_GRP [y]) + continue; + old_GRP [y] = val; + for (x=0; x<48; ++x) { + draw_lamp (100 + x*STEPX, top+28 + y*STEPY, val >> (47-x) & 1); + } + } +} + +/* + * Отрисовка лампочек БРЗ. + */ +static void draw_brz_periodic (int top) +{ + int x, y; + t_value val; + + for (y=0; y<8; ++y) { + val = BRZ [7-y]; + if (val == old_BRZ [7-y]) + continue; + old_BRZ [7-y] = val; + for (x=0; x<48; ++x) { + draw_lamp (100 + x*STEPX, top+28 + y*STEPY, val >> (47-x) & 1); + } + } +} + +/* + * Отрисовка статичной части регистров-модификаторов. + */ +static void draw_modifiers_static (int group, int left, int top) +{ + int x, y, color, reg; + char message [40]; + SDL_Rect area; + + background = black; + foreground = cyan; + + /* Оттеняем группы разрядов. */ + color = grey.r << 16 | grey.g << 8 | grey.b; + for (x=3; x<15; x+=3) { + area.x = left + 74 + x*STEPX; + area.y = top + 26; + area.w = 2; + area.h = 8*STEPY + 2; + SDL_FillRect (screen, &area, color); + } + /* Названия регистров. */ + for (y=0; y<8; ++y) { + reg = regnum [y + group*8]; + sprintf (message, "М%2o", reg); + render_utf8 (font_big, left, top + 24 + y*STEPY, 1, message); + old_M [reg] = ~0; + } + /* Номера битов. */ + for (x=0; x<15; ++x) { + sprintf (message, "%d", 15-x); + render_utf8 (font_small, left+82 + x*STEPX, + (x & 1) ? top+4 : top+10, 0, message); + } +} + +/* + * Отрисовка статичной части регистров ГРП и МГРП. + */ +static void draw_grp_static (int top) +{ + int x, y, color; + char message [40]; + SDL_Rect area; + + background = black; + foreground = cyan; + + /* Оттеняем группы разрядов. */ + color = grey.r << 16 | grey.g << 8 | grey.b; + for (x=3; x<48; x+=3) { + area.x = 98 + x*STEPX; + area.y = top + 26; + area.w = 2; + area.h = 2*STEPY + 2; + SDL_FillRect (screen, &area, color); + } + /* Названия регистров. */ + for (y=0; y<2; ++y) { + render_utf8 (font_big, 24, top + 24 + y*STEPY, 1, y ? "МГРП" : "ГРП"); + old_GRP[y] = ~0; + } + /* Номера битов. */ + for (x=0; x<48; ++x) { + sprintf (message, "%d", 48-x); + render_utf8 (font_small, 106 + x*STEPX, + (x & 1) ? top+10 : top+4, 0, message); + } +} + +/* + * Отрисовка статичной части регистров БРЗ. + */ +static void draw_brz_static (int top) +{ + int x, y, color; + char message [40]; + SDL_Rect area; + + background = black; + foreground = cyan; + + /* Оттеняем группы разрядов. */ + color = grey.r << 16 | grey.g << 8 | grey.b; + for (x=3; x<48; x+=3) { + area.x = 98 + x*STEPX; + area.y = top + 26; + area.w = 2; + area.h = 8*STEPY + 2; + SDL_FillRect (screen, &area, color); + } + /* Названия регистров. */ + for (y=7; y>=0; --y) { + sprintf (message, "БРЗ %d", 7-y); + render_utf8 (font_big, 24, top + 24 + y*STEPY, 1, message); + old_BRZ[y] = ~0; + } +} + +/* + * Поиск файла шрифта по имени. + */ +static int probe_font (const char *path, const struct stat *st, int flag) +{ + const char *p; + + if (flag != FTW_F) + return 0; + p = path + strlen (path) - strlen (FONTNAME); + if (p < path || strcmp (p, FONTNAME) != 0) + return 0; + font_path = strdup (path); + return 1; +} + +/* + * Закрываем графическое окно. + */ +void besm6_close_panel () +{ + if (! screen) + return; + TTF_Quit(); + SDL_Quit(); + screen = 0; +} + +#if SDL_MAJOR_VERSION == 2 + +static SDL_Window *sdlWindow; +static SDL_Renderer *sdlRenderer; +static SDL_Texture *sdlTexture; + + +/* + * Начальная инициализация графического окна и шрифтов. + */ +static void init_panel () +{ + if (sim_switches & SWMASK('Q')) + return; + + /* Initialize SDL subsystems - in this case, only video. */ + if (SDL_Init (SDL_INIT_VIDEO) < 0) { + fprintf (stderr, "SDL: unable to init: %s\n", + SDL_GetError ()); + exit (1); + } + sdlWindow = SDL_CreateWindow ("BESM-6 panel", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + WIDTH, HEIGHT, 0 /* regular window */); + if (! sdlWindow) { + fprintf (stderr, "SDL: unable to set %dx%dx%d mode: %s\n", + WIDTH, HEIGHT, DEPTH, SDL_GetError ()); + exit (1); + } + + sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0); + /* Make black background */ + SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255); + SDL_RenderClear(sdlRenderer); + + /* Initialize the TTF library */ + if (TTF_Init() < 0) { + fprintf (stderr, "SDL: couldn't initialize TTF: %s\n", + SDL_GetError()); + SDL_Quit(); + exit (1); + } + + /* Find font file */ + if (ftw (FONTPATH1, probe_font, 255) <= 0 && + ftw (FONTPATH2, probe_font, 255) <= 0 && + ftw (FONTPATH3, probe_font, 255) <= 0) { + fprintf(stderr, "SDL: couldn't find font %s in directory %s\n", + FONTNAME, FONTPATH1); + besm6_close_panel(); + exit (1); + } + + /* Open the font file with the requested point size */ + font_big = TTF_OpenFont (font_path, 16); + font_small = TTF_OpenFont (font_path, 9); + if (! font_big || ! font_small) { + fprintf(stderr, "SDL: couldn't load font %s: %s\n", + font_path, SDL_GetError()); + besm6_close_panel(); + exit (1); + } + atexit (besm6_close_panel); + + screen = SDL_CreateRGBSurface(0, WIDTH, HEIGHT, 32, + 0x00FF0000, + 0x0000FF00, + 0x000000FF, + 0xFF000000); + + sdlTexture = SDL_CreateTexture(sdlRenderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + WIDTH, HEIGHT); + + /* Отрисовка статичной части панели БЭСМ-6. */ + draw_modifiers_static (0, 24, 10); + draw_modifiers_static (1, 400, 10); + draw_grp_static (180); + draw_brz_static (230); + + /* Tell SDL to update the whole screen */ + SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch); + SDL_RenderClear(sdlRenderer); + SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); + SDL_RenderPresent (sdlRenderer); +} + +void (*sim_vm_init)() = init_panel; + +/* + * Обновляем графическое окно. + */ +void besm6_draw_panel () +{ + if (! screen) + return; + + /* Периодическая отрисовка: мигание лампочек. */ + draw_modifiers_periodic (0, 24, 10); + draw_modifiers_periodic (1, 400, 10); + draw_grp_periodic (180); + draw_brz_periodic (230); + + /* Tell SDL to update the whole screen */ + SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch); + SDL_RenderClear(sdlRenderer); + SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL); + SDL_RenderPresent (sdlRenderer); + + /* Exit SIMH when window closed.*/ + SDL_Event event; + if (SDL_PollEvent (&event) && event.type == SDL_QUIT) + longjmp (cpu_halt, SCPE_STOP); +} + +#else + +/* + * Начальная инициализация графического окна и шрифтов. + */ +static void init_panel () +{ + if (sim_switches & SWMASK('Q')) + return; + + /* Initialize SDL subsystems - in this case, only video. */ + if (SDL_Init (SDL_INIT_VIDEO) < 0) { + fprintf (stderr, "SDL: unable to init: %s\n", + SDL_GetError ()); + exit (1); + } + screen = SDL_SetVideoMode (WIDTH, HEIGHT, DEPTH, SDL_SWSURFACE); + if (! screen) { + fprintf (stderr, "SDL: unable to set %dx%dx%d mode: %s\n", + WIDTH, HEIGHT, DEPTH, SDL_GetError ()); + exit (1); + } + + /* Initialize the TTF library */ + if (TTF_Init() < 0) { + fprintf (stderr, "SDL: couldn't initialize TTF: %s\n", + SDL_GetError()); + SDL_Quit(); + exit (1); + } + + /* Find font file */ + if (ftw (FONTPATH1, probe_font, 255) <= 0 && + ftw (FONTPATH2, probe_font, 255) <= 0 && + ftw (FONTPATH3, probe_font, 255) <= 0) { + fprintf(stderr, "SDL: couldn't find font %s in directory %s\n", + FONTNAME, FONTPATH1); + besm6_close_panel(); + exit (1); + } + + /* Open the font file with the requested point size */ + font_big = TTF_OpenFont (font_path, 16); + font_small = TTF_OpenFont (font_path, 9); + if (! font_big || ! font_small) { + fprintf(stderr, "SDL: couldn't load font %s: %s\n", + FONTNAME, TTF_GetError()); + besm6_close_panel(); + exit (1); + } + atexit (besm6_close_panel); + + /* Отрисовка статичной части панели БЭСМ-6. */ + draw_modifiers_static (0, 24, 10); + draw_modifiers_static (1, 400, 10); + draw_grp_static (180); + draw_brz_static (230); + + /* Tell SDL to update the whole screen */ + SDL_UpdateRect (screen, 0, 0, WIDTH, HEIGHT); +} + +void (*sim_vm_init)() = init_panel; + +/* + * Обновляем графическое окно. + */ +void besm6_draw_panel () +{ + if (! screen) + return; + + /* Периодическая отрисовка: мигание лампочек. */ + draw_modifiers_periodic (0, 24, 10); + draw_modifiers_periodic (1, 400, 10); + draw_grp_periodic (180); + draw_brz_periodic (230); + + /* Tell SDL to update the whole screen */ + SDL_UpdateRect (screen, 0, 0, WIDTH, HEIGHT); + + /* Exit SIMH when window closed.*/ + SDL_Event event; + if (SDL_PollEvent (&event) && event.type == SDL_QUIT) + longjmp (cpu_halt, SCPE_STOP); +} + +#if !defined(__WIN32__) && \ + !(defined(__MWERKS__) && !defined(__BEOS__)) && \ + !defined(__MACOS__) && !defined(__MACOSX__) && \ + !defined(__SYMBIAN32__) && !defined(QWS) +#undef main + +/* + * Вот так всё непросто. + */ +int main (int argc, char *argv[]) +{ + extern int SDL_main (int, char**); + + return SDL_main (argc, argv); +} +#endif +#endif /* SDL_MAJOR_VERSION */ + +#else /* HAVE_LIBSDL */ +void besm6_draw_panel () +{ +} +#endif /* HAVE_LIBSDL */ diff --git a/BESM6/besm6_printer.c b/BESM6/besm6_printer.c index 9618f76e..d6dee824 100644 --- a/BESM6/besm6_printer.c +++ b/BESM6/besm6_printer.c @@ -1,345 +1,345 @@ -/* - * besm6_printer.c: BESM-6 line printer device - * - * Copyright (c) 2009, Leonid Broukhis - * - * 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 "besm6_defs.h" - -t_stat printer_event (UNIT *u); -void offset_gost_write (int num, FILE *fout); - -/* - * Printer data structures - * - * printer_dev PRINTER device descriptor - * printer_unit PRINTER unit descriptor - * printer_reg PRINTER register list - */ -UNIT printer_unit [] = { - { UDATA (printer_event, UNIT_ATTABLE+UNIT_SEQ, 0) }, - { UDATA (printer_event, UNIT_ATTABLE+UNIT_SEQ, 0) }, -}; - - -#define MAX_STRIKES 10 -struct acpu_t { - int curchar, feed, rampup; - int strikes; - int length; - unsigned char line[128][MAX_STRIKES]; -} acpu[2]; - -int acpu_isatty[2]; - -#define PRN1_NOT_READY (1<<19) -#define PRN2_NOT_READY (1<<18) - -/* 1 = можно пользоваться молоточками, 0 - бумага в процессе протяжки */ -#define PRN1_LINEFEED (1<<23) -#define PRN2_LINEFEED (1<<22) - -#define SLOW_START 100*MSEC -#define FAST_START 1*MSEC -#define LINEFEED_SYNC 1 /* Чтобы быстрее печатало; в жизни 20-25 мс/1.4 мс ~= 17 */ - -REG printer_reg[] = { -{ "Готов", &READY, 2, 2, 18, 1 }, -{ "Прогон", &READY, 2, 2, 22, 1 }, -{ 0 } -}; - -MTAB printer_mod[] = { - { 0 } -}; - -t_stat printer_reset (DEVICE *dptr); -t_stat printer_attach (UNIT *uptr, char *cptr); -t_stat printer_detach (UNIT *uptr); - -DEVICE printer_dev = { - "PRN", printer_unit, printer_reg, printer_mod, - 2, 8, 19, 1, 8, 50, - NULL, NULL, &printer_reset, NULL, &printer_attach, &printer_detach, - NULL, DEV_DISABLE | DEV_DEBUG -}; - -/* - * Reset routine - */ -t_stat printer_reset (DEVICE *dptr) -{ - memset(acpu, 0, sizeof (acpu)); - acpu[0].rampup = acpu[1].rampup = SLOW_START; - sim_cancel (&printer_unit[0]); - sim_cancel (&printer_unit[1]); - READY |= PRN1_NOT_READY | PRN2_NOT_READY; - if (printer_unit[0].flags & UNIT_ATT) - READY &= ~PRN1_NOT_READY; - if (printer_unit[1].flags & UNIT_ATT) - READY &= ~PRN2_NOT_READY; - return SCPE_OK; -} - -t_stat printer_attach (UNIT *u, char *cptr) -{ - t_stat s; - int num = u - printer_unit; - - if (u->flags & UNIT_ATT) { - /* Switching files cleanly */ - detach_unit (u); - } - s = attach_unit (u, cptr); - if (s != SCPE_OK) - return s; - - acpu_isatty[num] = !strcmp(cptr, "/dev/tty"); - if (!acpu_isatty[num]) { - /* Write UTF-8 tag: zero width no-break space. */ - fputs ("\xEF\xBB\xBF", u->fileref); - } - - READY &= ~(PRN1_NOT_READY >> num); - return SCPE_OK; -} - -t_stat printer_detach (UNIT *u) -{ - int num = u - printer_unit; - READY |= PRN1_NOT_READY >> num; - return detach_unit (u); -} - -/* - * Управление двигателями, прогон - */ -void printer_control (int num, uint32 cmd) -{ - UNIT *u = &printer_unit[num]; - struct acpu_t * dev = acpu + num; - - if (printer_dev.dctrl) - besm6_debug(">>> АЦПУ%d команда %o", num, cmd); - if (READY & (PRN1_NOT_READY >> num)) { - if (printer_dev.dctrl) - besm6_debug(">>> АЦПУ%d не готово", num, cmd); - return; - } - switch (cmd) { - case 1: /* linefeed */ - READY &= ~(PRN1_LINEFEED >> num); - offset_gost_write (num, u->fileref); - dev->feed = LINEFEED_SYNC; - break; - case 4: /* start */ - /* стартуем из состояния прогона для надежности */ - dev->feed = LINEFEED_SYNC; - READY &= ~(PRN1_LINEFEED >> num); - if (dev->rampup) - sim_activate (u, dev->rampup); - dev->rampup = 0; - break; - case 10: /* motor and ribbon off */ - case 8: /* motor off? (undocumented) */ - case 2: /* ribbon off */ - dev->rampup = cmd == 2 ? FAST_START : SLOW_START; - sim_cancel (u); - break; - } -} - -/* - * Управление молоточками - */ -void printer_hammer (int num, int pos, uint32 mask) -{ - struct acpu_t * dev = acpu + num; - while (mask) { - if (mask & 1) { - int strike = 0; - while (dev->line[pos][strike] && strike < MAX_STRIKES) - ++strike; - if (strike < MAX_STRIKES) { - dev->line[pos][strike] = dev->curchar; - if (pos + 1 > dev->length) - dev->length = pos + 1; - if (strike + 1 > dev->strikes) - dev->strikes = strike + 1; - } - } - mask >>= 1; - pos += 8; - } -} - -/* - * Событие: вращение барабана АЦПУ. - * Устанавливаем флаг прерывания. - */ -t_stat printer_event (UNIT *u) -{ - int num = u - printer_unit; - struct acpu_t * dev = acpu + num; - - switch (dev->curchar) { - case 0 ... 0137: - GRP |= GRP_PRN1_SYNC >> num; - ++dev->curchar; - /* For next char */ - sim_activate (u, 1400*USEC); - if (dev->feed && --dev->feed == 0) { - READY |= PRN1_LINEFEED >> num; - } - break; - case 0140: - /* For "zero" */ - dev->curchar = 0; - GRP |= GRP_PRN1_ZERO >> num; - if (printer_dev.dctrl) - besm6_debug(">>> АЦПУ%d 'ноль'", num); - /* For first sync after "zero" */ - sim_activate (u, 1000*USEC); - break; - } - return SCPE_OK; -} - -int gost_latin = 0; /* default cyrillics */ - -/* - * GOST-10859 encoding. - * Documentation: http://en.wikipedia.org/wiki/GOST_10859 - */ -static const unsigned short gost_to_unicode_cyr [256] = { -/* 000-007 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, -/* 010-017 */ 0x38, 0x39, 0x2b, 0x2d, 0x2f, 0x2c, 0x2e, 0x2423, -/* 020-027 */ 0x65, 0x2191, 0x28, 0x29, 0xd7, 0x3d, 0x3b, 0x5b, -/* 030-037 */ 0x5d, 0x2a, 0x2018, 0x2019, 0x2260, 0x3c, 0x3e, 0x3a, -/* 040-047 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, -/* 050-057 */ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, -/* 060-067 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, -/* 070-077 */ 0x0428, 0x0429, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x44, -/* 100-107 */ 0x46, 0x47, 0x49, 0x4a, 0x4c, 0x4e, 0x51, 0x52, -/* 110-117 */ 0x53, 0x55, 0x56, 0x57, 0x5a, 0x203e, 0x2264, 0x2265, -/* 120-127 */ 0x2228, 0x2227, 0x2283, 0xac, 0xf7, 0x2261, 0x25, 0x25c7, -/* 130-137 */ 0x7c, 0x2015, 0x5f, 0x21, 0x22, 0x042a, 0xb0, 0x2032, -}; - -static const unsigned short gost_to_unicode_lat [256] = { -/* 000-007 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, -/* 010-017 */ 0x38, 0x39, 0x2b, 0x2d, 0x2f, 0x2c, 0x2e, 0x2423, -/* 020-027 */ 0x65, 0x2191, 0x28, 0x29, 0xd7, 0x3d, 0x3b, 0x5b, -/* 030-037 */ 0x5d, 0x2a, 0x2018, 0x2019, 0x2260, 0x3c, 0x3e, 0x3a, -/* 040-047 */ 0x41, 0x0411, 0x42, 0x0413, 0x0414, 0x45, 0x0416, 0x0417, -/* 050-057 */ 0x0418, 0x0419, 0x4b, 0x041b, 0x4d, 0x48, 0x4f, 0x041f, -/* 060-067 */ 0x50, 0x43, 0x54, 0x59, 0x0424, 0x58, 0x0426, 0x0427, -/* 070-077 */ 0x0428, 0x0429, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x44, -/* 100-107 */ 0x46, 0x47, 0x49, 0x4a, 0x4c, 0x4e, 0x51, 0x52, -/* 110-117 */ 0x53, 0x55, 0x56, 0x57, 0x5a, 0x203e, 0x2264, 0x2265, -/* 120-127 */ 0x2228, 0x2227, 0x2283, 0xac, 0xf7, 0x2261, 0x25, 0x25c7, -/* 130-137 */ 0x7c, 0x2015, 0x5f, 0x21, 0x22, 0x042a, 0xb0, 0x2032, -}; -/* - * Write Unicode symbol to file. - * Convert to UTF-8 encoding: - * 00000000.0xxxxxxx -> 0xxxxxxx - * 00000xxx.xxyyyyyy -> 110xxxxx, 10yyyyyy - * xxxxyyyy.yyzzzzzz -> 1110xxxx, 10yyyyyy, 10zzzzzz - */ -static void -utf8_putc (unsigned short ch, FILE *fout) -{ - if (ch < 0x80) { - putc (ch, fout); - return; - } - if (ch < 0x800) { - putc (ch >> 6 | 0xc0, fout); - putc ((ch & 0x3f) | 0x80, fout); - return; - } - putc (ch >> 12 | 0xe0, fout); - putc (((ch >> 6) & 0x3f) | 0x80, fout); - putc ((ch & 0x3f) | 0x80, fout); -} - -unsigned short -gost_to_unicode (unsigned char ch) -{ - return gost_latin ? gost_to_unicode_lat [ch] : - gost_to_unicode_cyr [ch]; -} - -/* - * Write GOST-10859 symbol to file. - * Convert to local encoding (UTF-8, KOI8-R, CP-1251, CP-866). - */ -void -gost_putc (unsigned char ch, FILE *fout) -{ - unsigned short u; - - u = gost_to_unicode (ch); - if (! u) - u = ' '; - utf8_putc (u, fout); -} - -/* - * Write GOST-10859 string with overprint to file in UTF-8. - */ -void -offset_gost_write (int num, FILE *fout) -{ - struct acpu_t * dev = acpu + num; - int s, p; - for (s = 0; s < dev->strikes; ++s) { - if (s) - fputc ('\r', fout); - for (p = 0; p < dev->length; ++p) { - gost_putc (dev->line[p][s] - 1, fout); - } - } - - if (acpu_isatty[num]) - fputc('\r', fout); - - fputc ('\n', fout); - memset(dev->line, 0, sizeof (dev->line)); - dev->length = dev->strikes = 0; -} - -/* - * Выясняем, остановлены ли АЦПУ. Нужно для входа в "спящий" режим. - */ -int printer_is_idle () -{ - if ((printer_unit[0].flags & UNIT_ATT) && acpu[0].rampup == 0) - return 0; - if ((printer_unit[1].flags & UNIT_ATT) && acpu[1].rampup == 0) - return 0; - return 1; -} +/* + * besm6_printer.c: BESM-6 line printer device + * + * Copyright (c) 2009, Leonid Broukhis + * + * 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 "besm6_defs.h" + +t_stat printer_event (UNIT *u); +void offset_gost_write (int num, FILE *fout); + +/* + * Printer data structures + * + * printer_dev PRINTER device descriptor + * printer_unit PRINTER unit descriptor + * printer_reg PRINTER register list + */ +UNIT printer_unit [] = { + { UDATA (printer_event, UNIT_ATTABLE+UNIT_SEQ, 0) }, + { UDATA (printer_event, UNIT_ATTABLE+UNIT_SEQ, 0) }, +}; + + +#define MAX_STRIKES 10 +struct acpu_t { + int curchar, feed, rampup; + int strikes; + int length; + unsigned char line[128][MAX_STRIKES]; +} acpu[2]; + +int acpu_isatty[2]; + +#define PRN1_NOT_READY (1<<19) +#define PRN2_NOT_READY (1<<18) + +/* 1 = можно пользоваться молоточками, 0 - бумага в процессе протяжки */ +#define PRN1_LINEFEED (1<<23) +#define PRN2_LINEFEED (1<<22) + +#define SLOW_START 100*MSEC +#define FAST_START 1*MSEC +#define LINEFEED_SYNC 1 /* Чтобы быстрее печатало; в жизни 20-25 мс/1.4 мс ~= 17 */ + +REG printer_reg[] = { +{ "Готов", &READY, 2, 2, 18, 1 }, +{ "Прогон", &READY, 2, 2, 22, 1 }, +{ 0 } +}; + +MTAB printer_mod[] = { + { 0 } +}; + +t_stat printer_reset (DEVICE *dptr); +t_stat printer_attach (UNIT *uptr, char *cptr); +t_stat printer_detach (UNIT *uptr); + +DEVICE printer_dev = { + "PRN", printer_unit, printer_reg, printer_mod, + 2, 8, 19, 1, 8, 50, + NULL, NULL, &printer_reset, NULL, &printer_attach, &printer_detach, + NULL, DEV_DISABLE | DEV_DEBUG +}; + +/* + * Reset routine + */ +t_stat printer_reset (DEVICE *dptr) +{ + memset(acpu, 0, sizeof (acpu)); + acpu[0].rampup = acpu[1].rampup = SLOW_START; + sim_cancel (&printer_unit[0]); + sim_cancel (&printer_unit[1]); + READY |= PRN1_NOT_READY | PRN2_NOT_READY; + if (printer_unit[0].flags & UNIT_ATT) + READY &= ~PRN1_NOT_READY; + if (printer_unit[1].flags & UNIT_ATT) + READY &= ~PRN2_NOT_READY; + return SCPE_OK; +} + +t_stat printer_attach (UNIT *u, char *cptr) +{ + t_stat s; + int num = u - printer_unit; + + if (u->flags & UNIT_ATT) { + /* Switching files cleanly */ + detach_unit (u); + } + s = attach_unit (u, cptr); + if (s != SCPE_OK) + return s; + + acpu_isatty[num] = !strcmp(cptr, "/dev/tty"); + if (!acpu_isatty[num]) { + /* Write UTF-8 tag: zero width no-break space. */ + fputs ("\xEF\xBB\xBF", u->fileref); + } + + READY &= ~(PRN1_NOT_READY >> num); + return SCPE_OK; +} + +t_stat printer_detach (UNIT *u) +{ + int num = u - printer_unit; + READY |= PRN1_NOT_READY >> num; + return detach_unit (u); +} + +/* + * Управление двигателями, прогон + */ +void printer_control (int num, uint32 cmd) +{ + UNIT *u = &printer_unit[num]; + struct acpu_t * dev = acpu + num; + + if (printer_dev.dctrl) + besm6_debug(">>> АЦПУ%d команда %o", num, cmd); + if (READY & (PRN1_NOT_READY >> num)) { + if (printer_dev.dctrl) + besm6_debug(">>> АЦПУ%d не готово", num, cmd); + return; + } + switch (cmd) { + case 1: /* linefeed */ + READY &= ~(PRN1_LINEFEED >> num); + offset_gost_write (num, u->fileref); + dev->feed = LINEFEED_SYNC; + break; + case 4: /* start */ + /* стартуем из состояния прогона для надежности */ + dev->feed = LINEFEED_SYNC; + READY &= ~(PRN1_LINEFEED >> num); + if (dev->rampup) + sim_activate (u, dev->rampup); + dev->rampup = 0; + break; + case 10: /* motor and ribbon off */ + case 8: /* motor off? (undocumented) */ + case 2: /* ribbon off */ + dev->rampup = cmd == 2 ? FAST_START : SLOW_START; + sim_cancel (u); + break; + } +} + +/* + * Управление молоточками + */ +void printer_hammer (int num, int pos, uint32 mask) +{ + struct acpu_t * dev = acpu + num; + while (mask) { + if (mask & 1) { + int strike = 0; + while (dev->line[pos][strike] && strike < MAX_STRIKES) + ++strike; + if (strike < MAX_STRIKES) { + dev->line[pos][strike] = dev->curchar; + if (pos + 1 > dev->length) + dev->length = pos + 1; + if (strike + 1 > dev->strikes) + dev->strikes = strike + 1; + } + } + mask >>= 1; + pos += 8; + } +} + +/* + * Событие: вращение барабана АЦПУ. + * Устанавливаем флаг прерывания. + */ +t_stat printer_event (UNIT *u) +{ + int num = u - printer_unit; + struct acpu_t * dev = acpu + num; + + switch (dev->curchar) { + case 0 ... 0137: + GRP |= GRP_PRN1_SYNC >> num; + ++dev->curchar; + /* For next char */ + sim_activate (u, 1400*USEC); + if (dev->feed && --dev->feed == 0) { + READY |= PRN1_LINEFEED >> num; + } + break; + case 0140: + /* For "zero" */ + dev->curchar = 0; + GRP |= GRP_PRN1_ZERO >> num; + if (printer_dev.dctrl) + besm6_debug(">>> АЦПУ%d 'ноль'", num); + /* For first sync after "zero" */ + sim_activate (u, 1000*USEC); + break; + } + return SCPE_OK; +} + +int gost_latin = 0; /* default cyrillics */ + +/* + * GOST-10859 encoding. + * Documentation: http://en.wikipedia.org/wiki/GOST_10859 + */ +static const unsigned short gost_to_unicode_cyr [256] = { +/* 000-007 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +/* 010-017 */ 0x38, 0x39, 0x2b, 0x2d, 0x2f, 0x2c, 0x2e, 0x2423, +/* 020-027 */ 0x65, 0x2191, 0x28, 0x29, 0xd7, 0x3d, 0x3b, 0x5b, +/* 030-037 */ 0x5d, 0x2a, 0x2018, 0x2019, 0x2260, 0x3c, 0x3e, 0x3a, +/* 040-047 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, +/* 050-057 */ 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, +/* 060-067 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, +/* 070-077 */ 0x0428, 0x0429, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x44, +/* 100-107 */ 0x46, 0x47, 0x49, 0x4a, 0x4c, 0x4e, 0x51, 0x52, +/* 110-117 */ 0x53, 0x55, 0x56, 0x57, 0x5a, 0x203e, 0x2264, 0x2265, +/* 120-127 */ 0x2228, 0x2227, 0x2283, 0xac, 0xf7, 0x2261, 0x25, 0x25c7, +/* 130-137 */ 0x7c, 0x2015, 0x5f, 0x21, 0x22, 0x042a, 0xb0, 0x2032, +}; + +static const unsigned short gost_to_unicode_lat [256] = { +/* 000-007 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +/* 010-017 */ 0x38, 0x39, 0x2b, 0x2d, 0x2f, 0x2c, 0x2e, 0x2423, +/* 020-027 */ 0x65, 0x2191, 0x28, 0x29, 0xd7, 0x3d, 0x3b, 0x5b, +/* 030-037 */ 0x5d, 0x2a, 0x2018, 0x2019, 0x2260, 0x3c, 0x3e, 0x3a, +/* 040-047 */ 0x41, 0x0411, 0x42, 0x0413, 0x0414, 0x45, 0x0416, 0x0417, +/* 050-057 */ 0x0418, 0x0419, 0x4b, 0x041b, 0x4d, 0x48, 0x4f, 0x041f, +/* 060-067 */ 0x50, 0x43, 0x54, 0x59, 0x0424, 0x58, 0x0426, 0x0427, +/* 070-077 */ 0x0428, 0x0429, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x44, +/* 100-107 */ 0x46, 0x47, 0x49, 0x4a, 0x4c, 0x4e, 0x51, 0x52, +/* 110-117 */ 0x53, 0x55, 0x56, 0x57, 0x5a, 0x203e, 0x2264, 0x2265, +/* 120-127 */ 0x2228, 0x2227, 0x2283, 0xac, 0xf7, 0x2261, 0x25, 0x25c7, +/* 130-137 */ 0x7c, 0x2015, 0x5f, 0x21, 0x22, 0x042a, 0xb0, 0x2032, +}; +/* + * Write Unicode symbol to file. + * Convert to UTF-8 encoding: + * 00000000.0xxxxxxx -> 0xxxxxxx + * 00000xxx.xxyyyyyy -> 110xxxxx, 10yyyyyy + * xxxxyyyy.yyzzzzzz -> 1110xxxx, 10yyyyyy, 10zzzzzz + */ +static void +utf8_putc (unsigned short ch, FILE *fout) +{ + if (ch < 0x80) { + putc (ch, fout); + return; + } + if (ch < 0x800) { + putc (ch >> 6 | 0xc0, fout); + putc ((ch & 0x3f) | 0x80, fout); + return; + } + putc (ch >> 12 | 0xe0, fout); + putc (((ch >> 6) & 0x3f) | 0x80, fout); + putc ((ch & 0x3f) | 0x80, fout); +} + +unsigned short +gost_to_unicode (unsigned char ch) +{ + return gost_latin ? gost_to_unicode_lat [ch] : + gost_to_unicode_cyr [ch]; +} + +/* + * Write GOST-10859 symbol to file. + * Convert to local encoding (UTF-8, KOI8-R, CP-1251, CP-866). + */ +void +gost_putc (unsigned char ch, FILE *fout) +{ + unsigned short u; + + u = gost_to_unicode (ch); + if (! u) + u = ' '; + utf8_putc (u, fout); +} + +/* + * Write GOST-10859 string with overprint to file in UTF-8. + */ +void +offset_gost_write (int num, FILE *fout) +{ + struct acpu_t * dev = acpu + num; + int s, p; + for (s = 0; s < dev->strikes; ++s) { + if (s) + fputc ('\r', fout); + for (p = 0; p < dev->length; ++p) { + gost_putc (dev->line[p][s] - 1, fout); + } + } + + if (acpu_isatty[num]) + fputc('\r', fout); + + fputc ('\n', fout); + memset(dev->line, 0, sizeof (dev->line)); + dev->length = dev->strikes = 0; +} + +/* + * Выясняем, остановлены ли АЦПУ. Нужно для входа в "спящий" режим. + */ +int printer_is_idle () +{ + if ((printer_unit[0].flags & UNIT_ATT) && acpu[0].rampup == 0) + return 0; + if ((printer_unit[1].flags & UNIT_ATT) && acpu[1].rampup == 0) + return 0; + return 1; +} diff --git a/BESM6/besm6_punch.c b/BESM6/besm6_punch.c index d9d974f1..7f1a5235 100644 --- a/BESM6/besm6_punch.c +++ b/BESM6/besm6_punch.c @@ -1,472 +1,472 @@ -/* - * besm6_punch.c: BESM-6 punchcard/punchtape devices - * - * Copyright (c) 2009, Leonid Broukhis - * - * 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 "besm6_defs.h" -#include -#include - -t_stat fs_event (UNIT *u); -t_stat uvvk_event (UNIT *u); - -UNIT fs_unit [] = { - { UDATA (fs_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, - { UDATA (fs_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, -}; - -int curchar[2], feed[2], isfifo[2]; -char line[2][128]; - -#define FS1_READY (1<<15) -#define FS2_READY (1<<14) - -/* #define NEGATIVE_RDY */ - -#ifndef NEGATIVE_RDY -#define ENB_RDY SET_RDY -#define DIS_RDY CLR_RDY -#define IS_RDY ISSET_RDY -#else -#define ENB_RDY CLR_RDY -#define DIS_RDY SET_RDY -#define IS_RDY ISCLR_RDY -#endif - -#define SET_RDY(x) do READY |= x; while (0) -#define CLR_RDY(x) do READY &= ~(x); while (0) -#define ISSET_RDY(x) ((READY & (x)) != 0) -#define ISCLR_RDY(x) ((READY & (x)) == 0) - -#define FS_RATE 1000*MSEC/1500 - -unsigned char FS[2]; - -REG fs_reg[] = { -{ "Готов", &READY, 2, 2, 14, 1 }, -{ "ФС1500-1", &FS[0], 8, 8, 0, 1 }, -{ "ФС1500-2", &FS[2], 8, 8, 0, 1 }, -{ 0 } -}; - -MTAB fs_mod[] = { - { 0 } -}; - -t_stat fs_reset (DEVICE *dptr); -t_stat fs_attach (UNIT *uptr, char *cptr); -t_stat fs_detach (UNIT *uptr); - -DEVICE fs_dev = { - "FS", fs_unit, fs_reg, fs_mod, - 2, 8, 19, 1, 8, 50, - NULL, NULL, &fs_reset, NULL, &fs_attach, &fs_detach, - NULL, DEV_DISABLE | DEV_DEBUG -}; - -#define CARD_LEN 120 -enum { - FS_IDLE, - FS_STARTING, - FS_RUNNING, - FS_IMAGE, - FS_IMAGE_LAST = FS_IMAGE + CARD_LEN - 1, - FS_TOOLONG, - FS_FILLUP, - FS_FILLUP_LAST = FS_FILLUP + CARD_LEN - 1, - FS_ENDA3, - FS_ENDA3_LAST = FS_ENDA3 + CARD_LEN - 1, - FS_TAIL, -} fs_state[2]; - -/* - * Reset routine - */ -t_stat fs_reset (DEVICE *dptr) -{ - sim_cancel (&fs_unit[0]); - sim_cancel (&fs_unit[1]); - fs_state[0] = fs_state[1] = FS_IDLE; - DIS_RDY(FS1_READY | FS2_READY); - if (fs_unit[0].flags & UNIT_ATT) - ENB_RDY(FS1_READY); - if (fs_unit[1].flags & UNIT_ATT) - ENB_RDY(FS2_READY); - return SCPE_OK; -} - -t_stat fs_attach (UNIT *u, char *cptr) -{ - t_stat s; - int num = u - fs_unit; - s = attach_unit (u, cptr); - if (s != SCPE_OK) - return s; - struct stat stbuf; - fstat (fileno(u->fileref), &stbuf); - isfifo[num] = (stbuf.st_mode & S_IFIFO) != 0; - if (isfifo[num]) { - int flags = fcntl(fileno(u->fileref), F_GETFL, 0); - fcntl(fileno(u->fileref), F_SETFL, flags | O_NONBLOCK); - } - ENB_RDY(FS1_READY >> num); - return SCPE_OK; -} - -t_stat fs_detach (UNIT *u) -{ - int num = u - fs_unit; - DIS_RDY(FS1_READY >> num); - return detach_unit (u); -} - -/* - * Управление двигателем, лампой, протяжкой - */ -void fs_control (int num, uint32 cmd) -{ - UNIT *u = &fs_unit[num]; - - static int bytecnt = 0; - if (fs_dev.dctrl) - besm6_debug("<<< ФС1500-%d команда %o", num, cmd); - if (! IS_RDY(FS1_READY >> num)) { - if (fs_dev.dctrl) - besm6_debug("<<< ФС1500-%d не готово", num, cmd); - return; - } - switch (cmd) { - case 0: /* полное выключение */ - sim_cancel (u); - fs_state[num] = FS_IDLE; - if (fs_dev.dctrl) - besm6_debug("<<<ФС1500-%d ВЫКЛ..", num); - bytecnt = 0; - break; - case 4: /* двигатель без протяжки */ - fs_state[num] = FS_STARTING; - if (fs_dev.dctrl) - besm6_debug("<<<ФС1500-%d ВКЛ.", num); - sim_cancel (u); - break; - case 5: /* протяжка */ - if (fs_state[num] == FS_IDLE) - besm6_debug("<<< ФС1500-%d протяжка без мотора", num); - else if (fs_state[num] != FS_TAIL) { - sim_activate (u, FS_RATE); - bytecnt++; - } else { - if (! isfifo[num]) { - fs_detach(u); - fs_state[num] = FS_IDLE; - } - } - break; - default: - besm6_debug ("<<< ФС1500-%d неизвестная команда %o", num, cmd); - } - if (cmd && fs_dev.dctrl) { - besm6_debug("<<<ФС1500-%d: %d симв.", num, bytecnt); - } -} - -unsigned char unicode_to_gost (unsigned short val); -static int utf8_getc (FILE *fin); - -/* - * Событие: читаем очередной символ с перфоленты в регистр. - * Устанавливаем флаг прерывания. - */ -t_stat fs_event (UNIT *u) -{ - int num = u - fs_unit; -again: - switch (fs_state[num]) { - case FS_STARTING: - /* По первому прерыванию после запуска двигателя ничего не читаем */ - FS[num] = 0; - fs_state[num] = FS_RUNNING; - break; - case FS_RUNNING: { - int ch; - /* переводы строк игнорируются */ - while ((ch = utf8_getc (u->fileref)) == '\n'); - if (ch < 0) { - /* хвост ленты без пробивок */ - FS[num] = 0; - fs_state[num] = FS_TAIL; - } else if (ch == '\f') { - fs_state[num] = FS_IMAGE; - goto again; - } else { - ch = FS[num] = unicode_to_gost (ch); - ch = (ch & 0x55) + ((ch >> 1) & 0x55); - ch = (ch & 0x33) + ((ch >> 2) & 0x33); - ch = (ch & 0x0F) + ((ch >> 4) & 0x0F); - if (ch & 1); else FS[num] |= 0x80; - } - break; - } - case FS_IMAGE ... FS_IMAGE_LAST: { - int ch = utf8_getc (u->fileref); - if (ch < 0) { - /* обрыв ленты */ - FS[num] = 0; - fs_state[num] = FS_TAIL; - } else if (ch == '\n') { - /* идем дополнять образ карты нулевыми байтами */ - fs_state[num] = FS_FILLUP + (fs_state[num] - FS_IMAGE); - goto again; - } else if (ch == '\f') { - if (fs_state[num] != FS_IMAGE) - besm6_debug("<<< ENDA3 requested mid-card?"); - fs_state[num] = FS_ENDA3; - goto again; - } else { - ch = FS[num] = unicode_to_gost (ch); - ch = (ch & 0x55) + ((ch >> 1) & 0x55); - ch = (ch & 0x33) + ((ch >> 2) & 0x33); - ch = (ch & 0x0F) + ((ch >> 4) & 0x0F); - if (ch & 1); else FS[num] |= 0x80; - ++fs_state[num]; - } - break; - } - case FS_TOOLONG: { - /* дочитываем до конца строки */ - int ch; - besm6_debug("<<< too long???"); - while ((ch = utf8_getc (u->fileref)) != '\n' && ch >= 0); - if (ch < 0) { - /* хвост ленты без пробивок */ - FS[num] = 0; - fs_state[num] = FS_TAIL; - } else - goto again; - break; - } - case FS_FILLUP ... FS_FILLUP_LAST: - FS[num] = 0; - if (++fs_state[num] == FS_ENDA3) { - fs_state[num] = FS_IMAGE; - } - break; - case FS_ENDA3 ... FS_ENDA3_LAST: - if ((fs_state[num] - FS_ENDA3) % 5 == 0) - FS[num] = 0200; - else - FS[num] = 0; - if (++fs_state[num] == FS_TAIL) { - fs_state[num] = FS_RUNNING; - } - break; - case FS_IDLE: - case FS_TAIL: - FS[num] = 0; - break; - } - GRP |= GRP_FS1_SYNC >> num; - return SCPE_OK; -} - -int fs_read(int num) { - if (fs_dev.dctrl) - besm6_debug("<<< ФС1500-%d: байт %03o", num, FS[num]); - - return FS[num]; -} - -int fs_is_idle (void) -{ - return fs_state[0] == FS_IDLE && fs_state[1] == FS_IDLE; -} - -unsigned char -unicode_to_gost (unsigned short val) -{ - static const unsigned char tab0 [256] = { -/* 00 - 07 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* 08 - 0f */ 017, 017, 0214, 017, 017, 0174, 017, 017, -/* 10 - 17 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* 18 - 1f */ 017, 017, 017, 017, 017, 017, 017, 017, -/* !"#$%&' */ 0017, 0133, 0134, 0034, 0127, 0126, 0121, 0033, -/* ()*+,-./ */ 0022, 0023, 0031, 0012, 0015, 0013, 0016, 0014, -/* 01234567 */ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, -/* 89:;<=>? */ 0010, 0011, 0037, 0026, 0035, 0025, 0036, 0136, -/* @ABCDEFG */ 0021, 0040, 0042, 0061, 0077, 0045, 0100, 0101, -/* HIJKLMNO */ 0055, 0102, 0103, 0052, 0104, 0054, 0105, 0056, -/* PQRSTUVW */ 0060, 0106, 0107, 0110, 0062, 0111, 0112, 0113, -/* XYZ[\]^_ */ 0065, 0063, 0114, 0027, 017, 0030, 0115, 0132, -/* `abcdefg */ 0032, 0040, 0042, 0061, 0077, 0045, 0100, 0101, -/* hijklmno */ 0055, 0102, 0103, 0052, 0104, 0054, 0105, 0056, -/* pqrstuvw */ 0060, 0106, 0107, 0110, 0062, 0111, 0112, 0113, -/* xyz{|}~ */ 0065, 0063, 0114, 017, 0130, 017, 0123, 017, -/* 80 - 87 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* 88 - 8f */ 017, 017, 017, 017, 017, 017, 017, 017, -/* 90 - 97 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* 98 - 9f */ 017, 017, 017, 017, 017, 017, 017, 017, -/* a0 - a7 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* a8 - af */ 017, 017, 017, 017, 0123, 017, 017, 017, -/* b0 - b7 */ 0136, 017, 017, 017, 017, 017, 017, 017, -/* b8 - bf */ 017, 017, 017, 017, 017, 017, 017, 017, -/* c0 - c7 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* c8 - cf */ 017, 017, 017, 017, 017, 017, 017, 017, -/* d0 - d7 */ 017, 017, 017, 017, 017, 017, 017, 0024, -/* d8 - df */ 017, 017, 017, 017, 017, 017, 017, 017, -/* e0 - e7 */ 017, 017, 017, 017, 017, 017, 017, 017, -/* e8 - ef */ 017, 017, 017, 017, 017, 017, 017, 017, -/* f0 - f7 */ 017, 017, 017, 017, 017, 017, 017, 0124, -/* f8 - ff */ 017, 017, 017, 017, 017, 017, 017, 017, - }; - switch (val >> 8) { - case 0x00: - return tab0 [val]; - case 0x04: - switch ((unsigned char) val) { - case 0x10: return 0040; - case 0x11: return 0041; - case 0x12: return 0042; - case 0x13: return 0043; - case 0x14: return 0044; - case 0x15: return 0045; - case 0x16: return 0046; - case 0x17: return 0047; - case 0x18: return 0050; - case 0x19: return 0051; - case 0x1a: return 0052; - case 0x1b: return 0053; - case 0x1c: return 0054; - case 0x1d: return 0055; - case 0x1e: return 0056; - case 0x1f: return 0057; - case 0x20: return 0060; - case 0x21: return 0061; - case 0x22: return 0062; - case 0x23: return 0063; - case 0x24: return 0064; - case 0x25: return 0065; - case 0x26: return 0066; - case 0x27: return 0067; - case 0x28: return 0070; - case 0x29: return 0071; - case 0x2a: return 0135; - case 0x2b: return 0072; - case 0x2c: return 0073; - case 0x2d: return 0074; - case 0x2e: return 0075; - case 0x2f: return 0076; - case 0x30: return 0040; - case 0x31: return 0041; - case 0x32: return 0042; - case 0x33: return 0043; - case 0x34: return 0044; - case 0x35: return 0045; - case 0x36: return 0046; - case 0x37: return 0047; - case 0x38: return 0050; - case 0x39: return 0051; - case 0x3a: return 0052; - case 0x3b: return 0053; - case 0x3c: return 0054; - case 0x3d: return 0055; - case 0x3e: return 0056; - case 0x3f: return 0057; - case 0x40: return 0060; - case 0x41: return 0061; - case 0x42: return 0062; - case 0x43: return 0063; - case 0x44: return 0064; - case 0x45: return 0065; - case 0x46: return 0066; - case 0x47: return 0067; - case 0x48: return 0070; - case 0x49: return 0071; - case 0x4a: return 0135; - case 0x4b: return 0072; - case 0x4c: return 0073; - case 0x4d: return 0074; - case 0x4e: return 0075; - case 0x4f: return 0076; - } - break; - case 0x20: - switch ((unsigned char) val) { - case 0x15: return 0131; - case 0x18: return 0032; - case 0x19: return 0033; - case 0x32: return 0137; - case 0x3e: return 0115; - } - break; - case 0x21: - switch ((unsigned char) val) { - case 0x2f: return 0020; - case 0x91: return 0021; - } - break; - case 0x22: - switch ((unsigned char) val) { - case 0x27: return 0121; - case 0x28: return 0120; - case 0x60: return 0034; - case 0x61: return 0125; - case 0x64: return 0116; - case 0x65: return 0117; - case 0x83: return 0122; - } - break; - case 0x25: - switch ((unsigned char) val) { - case 0xc7: return 0127; - case 0xca: return 0127; - } - break; - } - return 017; -} - -/* - * Read Unicode symbol from file. - * Convert from UTF-8 encoding. - */ -static int -utf8_getc (FILE *fin) -{ - int c1, c2, c3; -again: - c1 = getc (fin); - if (c1 < 0 || ! (c1 & 0x80)) - return c1; - c2 = getc (fin); - if (! (c1 & 0x20)) - return (c1 & 0x1f) << 6 | (c2 & 0x3f); - c3 = getc (fin); - if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { - /* Skip zero width no-break space. */ - goto again; - } - return (c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f); -} +/* + * besm6_punch.c: BESM-6 punchcard/punchtape devices + * + * Copyright (c) 2009, Leonid Broukhis + * + * 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 "besm6_defs.h" +#include +#include + +t_stat fs_event (UNIT *u); +t_stat uvvk_event (UNIT *u); + +UNIT fs_unit [] = { + { UDATA (fs_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, + { UDATA (fs_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, +}; + +int curchar[2], feed[2], isfifo[2]; +char line[2][128]; + +#define FS1_READY (1<<15) +#define FS2_READY (1<<14) + +/* #define NEGATIVE_RDY */ + +#ifndef NEGATIVE_RDY +#define ENB_RDY SET_RDY +#define DIS_RDY CLR_RDY +#define IS_RDY ISSET_RDY +#else +#define ENB_RDY CLR_RDY +#define DIS_RDY SET_RDY +#define IS_RDY ISCLR_RDY +#endif + +#define SET_RDY(x) do READY |= x; while (0) +#define CLR_RDY(x) do READY &= ~(x); while (0) +#define ISSET_RDY(x) ((READY & (x)) != 0) +#define ISCLR_RDY(x) ((READY & (x)) == 0) + +#define FS_RATE 1000*MSEC/1500 + +unsigned char FS[2]; + +REG fs_reg[] = { +{ "Готов", &READY, 2, 2, 14, 1 }, +{ "ФС1500-1", &FS[0], 8, 8, 0, 1 }, +{ "ФС1500-2", &FS[2], 8, 8, 0, 1 }, +{ 0 } +}; + +MTAB fs_mod[] = { + { 0 } +}; + +t_stat fs_reset (DEVICE *dptr); +t_stat fs_attach (UNIT *uptr, char *cptr); +t_stat fs_detach (UNIT *uptr); + +DEVICE fs_dev = { + "FS", fs_unit, fs_reg, fs_mod, + 2, 8, 19, 1, 8, 50, + NULL, NULL, &fs_reset, NULL, &fs_attach, &fs_detach, + NULL, DEV_DISABLE | DEV_DEBUG +}; + +#define CARD_LEN 120 +enum { + FS_IDLE, + FS_STARTING, + FS_RUNNING, + FS_IMAGE, + FS_IMAGE_LAST = FS_IMAGE + CARD_LEN - 1, + FS_TOOLONG, + FS_FILLUP, + FS_FILLUP_LAST = FS_FILLUP + CARD_LEN - 1, + FS_ENDA3, + FS_ENDA3_LAST = FS_ENDA3 + CARD_LEN - 1, + FS_TAIL, +} fs_state[2]; + +/* + * Reset routine + */ +t_stat fs_reset (DEVICE *dptr) +{ + sim_cancel (&fs_unit[0]); + sim_cancel (&fs_unit[1]); + fs_state[0] = fs_state[1] = FS_IDLE; + DIS_RDY(FS1_READY | FS2_READY); + if (fs_unit[0].flags & UNIT_ATT) + ENB_RDY(FS1_READY); + if (fs_unit[1].flags & UNIT_ATT) + ENB_RDY(FS2_READY); + return SCPE_OK; +} + +t_stat fs_attach (UNIT *u, char *cptr) +{ + t_stat s; + int num = u - fs_unit; + s = attach_unit (u, cptr); + if (s != SCPE_OK) + return s; + struct stat stbuf; + fstat (fileno(u->fileref), &stbuf); + isfifo[num] = (stbuf.st_mode & S_IFIFO) != 0; + if (isfifo[num]) { + int flags = fcntl(fileno(u->fileref), F_GETFL, 0); + fcntl(fileno(u->fileref), F_SETFL, flags | O_NONBLOCK); + } + ENB_RDY(FS1_READY >> num); + return SCPE_OK; +} + +t_stat fs_detach (UNIT *u) +{ + int num = u - fs_unit; + DIS_RDY(FS1_READY >> num); + return detach_unit (u); +} + +/* + * Управление двигателем, лампой, протяжкой + */ +void fs_control (int num, uint32 cmd) +{ + UNIT *u = &fs_unit[num]; + + static int bytecnt = 0; + if (fs_dev.dctrl) + besm6_debug("<<< ФС1500-%d команда %o", num, cmd); + if (! IS_RDY(FS1_READY >> num)) { + if (fs_dev.dctrl) + besm6_debug("<<< ФС1500-%d не готово", num, cmd); + return; + } + switch (cmd) { + case 0: /* полное выключение */ + sim_cancel (u); + fs_state[num] = FS_IDLE; + if (fs_dev.dctrl) + besm6_debug("<<<ФС1500-%d ВЫКЛ..", num); + bytecnt = 0; + break; + case 4: /* двигатель без протяжки */ + fs_state[num] = FS_STARTING; + if (fs_dev.dctrl) + besm6_debug("<<<ФС1500-%d ВКЛ.", num); + sim_cancel (u); + break; + case 5: /* протяжка */ + if (fs_state[num] == FS_IDLE) + besm6_debug("<<< ФС1500-%d протяжка без мотора", num); + else if (fs_state[num] != FS_TAIL) { + sim_activate (u, FS_RATE); + bytecnt++; + } else { + if (! isfifo[num]) { + fs_detach(u); + fs_state[num] = FS_IDLE; + } + } + break; + default: + besm6_debug ("<<< ФС1500-%d неизвестная команда %o", num, cmd); + } + if (cmd && fs_dev.dctrl) { + besm6_debug("<<<ФС1500-%d: %d симв.", num, bytecnt); + } +} + +unsigned char unicode_to_gost (unsigned short val); +static int utf8_getc (FILE *fin); + +/* + * Событие: читаем очередной символ с перфоленты в регистр. + * Устанавливаем флаг прерывания. + */ +t_stat fs_event (UNIT *u) +{ + int num = u - fs_unit; +again: + switch (fs_state[num]) { + case FS_STARTING: + /* По первому прерыванию после запуска двигателя ничего не читаем */ + FS[num] = 0; + fs_state[num] = FS_RUNNING; + break; + case FS_RUNNING: { + int ch; + /* переводы строк игнорируются */ + while ((ch = utf8_getc (u->fileref)) == '\n'); + if (ch < 0) { + /* хвост ленты без пробивок */ + FS[num] = 0; + fs_state[num] = FS_TAIL; + } else if (ch == '\f') { + fs_state[num] = FS_IMAGE; + goto again; + } else { + ch = FS[num] = unicode_to_gost (ch); + ch = (ch & 0x55) + ((ch >> 1) & 0x55); + ch = (ch & 0x33) + ((ch >> 2) & 0x33); + ch = (ch & 0x0F) + ((ch >> 4) & 0x0F); + if (ch & 1); else FS[num] |= 0x80; + } + break; + } + case FS_IMAGE ... FS_IMAGE_LAST: { + int ch = utf8_getc (u->fileref); + if (ch < 0) { + /* обрыв ленты */ + FS[num] = 0; + fs_state[num] = FS_TAIL; + } else if (ch == '\n') { + /* идем дополнять образ карты нулевыми байтами */ + fs_state[num] = FS_FILLUP + (fs_state[num] - FS_IMAGE); + goto again; + } else if (ch == '\f') { + if (fs_state[num] != FS_IMAGE) + besm6_debug("<<< ENDA3 requested mid-card?"); + fs_state[num] = FS_ENDA3; + goto again; + } else { + ch = FS[num] = unicode_to_gost (ch); + ch = (ch & 0x55) + ((ch >> 1) & 0x55); + ch = (ch & 0x33) + ((ch >> 2) & 0x33); + ch = (ch & 0x0F) + ((ch >> 4) & 0x0F); + if (ch & 1); else FS[num] |= 0x80; + ++fs_state[num]; + } + break; + } + case FS_TOOLONG: { + /* дочитываем до конца строки */ + int ch; + besm6_debug("<<< too long???"); + while ((ch = utf8_getc (u->fileref)) != '\n' && ch >= 0); + if (ch < 0) { + /* хвост ленты без пробивок */ + FS[num] = 0; + fs_state[num] = FS_TAIL; + } else + goto again; + break; + } + case FS_FILLUP ... FS_FILLUP_LAST: + FS[num] = 0; + if (++fs_state[num] == FS_ENDA3) { + fs_state[num] = FS_IMAGE; + } + break; + case FS_ENDA3 ... FS_ENDA3_LAST: + if ((fs_state[num] - FS_ENDA3) % 5 == 0) + FS[num] = 0200; + else + FS[num] = 0; + if (++fs_state[num] == FS_TAIL) { + fs_state[num] = FS_RUNNING; + } + break; + case FS_IDLE: + case FS_TAIL: + FS[num] = 0; + break; + } + GRP |= GRP_FS1_SYNC >> num; + return SCPE_OK; +} + +int fs_read(int num) { + if (fs_dev.dctrl) + besm6_debug("<<< ФС1500-%d: байт %03o", num, FS[num]); + + return FS[num]; +} + +int fs_is_idle (void) +{ + return fs_state[0] == FS_IDLE && fs_state[1] == FS_IDLE; +} + +unsigned char +unicode_to_gost (unsigned short val) +{ + static const unsigned char tab0 [256] = { +/* 00 - 07 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* 08 - 0f */ 017, 017, 0214, 017, 017, 0174, 017, 017, +/* 10 - 17 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* 18 - 1f */ 017, 017, 017, 017, 017, 017, 017, 017, +/* !"#$%&' */ 0017, 0133, 0134, 0034, 0127, 0126, 0121, 0033, +/* ()*+,-./ */ 0022, 0023, 0031, 0012, 0015, 0013, 0016, 0014, +/* 01234567 */ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, +/* 89:;<=>? */ 0010, 0011, 0037, 0026, 0035, 0025, 0036, 0136, +/* @ABCDEFG */ 0021, 0040, 0042, 0061, 0077, 0045, 0100, 0101, +/* HIJKLMNO */ 0055, 0102, 0103, 0052, 0104, 0054, 0105, 0056, +/* PQRSTUVW */ 0060, 0106, 0107, 0110, 0062, 0111, 0112, 0113, +/* XYZ[\]^_ */ 0065, 0063, 0114, 0027, 017, 0030, 0115, 0132, +/* `abcdefg */ 0032, 0040, 0042, 0061, 0077, 0045, 0100, 0101, +/* hijklmno */ 0055, 0102, 0103, 0052, 0104, 0054, 0105, 0056, +/* pqrstuvw */ 0060, 0106, 0107, 0110, 0062, 0111, 0112, 0113, +/* xyz{|}~ */ 0065, 0063, 0114, 017, 0130, 017, 0123, 017, +/* 80 - 87 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* 88 - 8f */ 017, 017, 017, 017, 017, 017, 017, 017, +/* 90 - 97 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* 98 - 9f */ 017, 017, 017, 017, 017, 017, 017, 017, +/* a0 - a7 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* a8 - af */ 017, 017, 017, 017, 0123, 017, 017, 017, +/* b0 - b7 */ 0136, 017, 017, 017, 017, 017, 017, 017, +/* b8 - bf */ 017, 017, 017, 017, 017, 017, 017, 017, +/* c0 - c7 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* c8 - cf */ 017, 017, 017, 017, 017, 017, 017, 017, +/* d0 - d7 */ 017, 017, 017, 017, 017, 017, 017, 0024, +/* d8 - df */ 017, 017, 017, 017, 017, 017, 017, 017, +/* e0 - e7 */ 017, 017, 017, 017, 017, 017, 017, 017, +/* e8 - ef */ 017, 017, 017, 017, 017, 017, 017, 017, +/* f0 - f7 */ 017, 017, 017, 017, 017, 017, 017, 0124, +/* f8 - ff */ 017, 017, 017, 017, 017, 017, 017, 017, + }; + switch (val >> 8) { + case 0x00: + return tab0 [val]; + case 0x04: + switch ((unsigned char) val) { + case 0x10: return 0040; + case 0x11: return 0041; + case 0x12: return 0042; + case 0x13: return 0043; + case 0x14: return 0044; + case 0x15: return 0045; + case 0x16: return 0046; + case 0x17: return 0047; + case 0x18: return 0050; + case 0x19: return 0051; + case 0x1a: return 0052; + case 0x1b: return 0053; + case 0x1c: return 0054; + case 0x1d: return 0055; + case 0x1e: return 0056; + case 0x1f: return 0057; + case 0x20: return 0060; + case 0x21: return 0061; + case 0x22: return 0062; + case 0x23: return 0063; + case 0x24: return 0064; + case 0x25: return 0065; + case 0x26: return 0066; + case 0x27: return 0067; + case 0x28: return 0070; + case 0x29: return 0071; + case 0x2a: return 0135; + case 0x2b: return 0072; + case 0x2c: return 0073; + case 0x2d: return 0074; + case 0x2e: return 0075; + case 0x2f: return 0076; + case 0x30: return 0040; + case 0x31: return 0041; + case 0x32: return 0042; + case 0x33: return 0043; + case 0x34: return 0044; + case 0x35: return 0045; + case 0x36: return 0046; + case 0x37: return 0047; + case 0x38: return 0050; + case 0x39: return 0051; + case 0x3a: return 0052; + case 0x3b: return 0053; + case 0x3c: return 0054; + case 0x3d: return 0055; + case 0x3e: return 0056; + case 0x3f: return 0057; + case 0x40: return 0060; + case 0x41: return 0061; + case 0x42: return 0062; + case 0x43: return 0063; + case 0x44: return 0064; + case 0x45: return 0065; + case 0x46: return 0066; + case 0x47: return 0067; + case 0x48: return 0070; + case 0x49: return 0071; + case 0x4a: return 0135; + case 0x4b: return 0072; + case 0x4c: return 0073; + case 0x4d: return 0074; + case 0x4e: return 0075; + case 0x4f: return 0076; + } + break; + case 0x20: + switch ((unsigned char) val) { + case 0x15: return 0131; + case 0x18: return 0032; + case 0x19: return 0033; + case 0x32: return 0137; + case 0x3e: return 0115; + } + break; + case 0x21: + switch ((unsigned char) val) { + case 0x2f: return 0020; + case 0x91: return 0021; + } + break; + case 0x22: + switch ((unsigned char) val) { + case 0x27: return 0121; + case 0x28: return 0120; + case 0x60: return 0034; + case 0x61: return 0125; + case 0x64: return 0116; + case 0x65: return 0117; + case 0x83: return 0122; + } + break; + case 0x25: + switch ((unsigned char) val) { + case 0xc7: return 0127; + case 0xca: return 0127; + } + break; + } + return 017; +} + +/* + * Read Unicode symbol from file. + * Convert from UTF-8 encoding. + */ +static int +utf8_getc (FILE *fin) +{ + int c1, c2, c3; +again: + c1 = getc (fin); + if (c1 < 0 || ! (c1 & 0x80)) + return c1; + c2 = getc (fin); + if (! (c1 & 0x20)) + return (c1 & 0x1f) << 6 | (c2 & 0x3f); + c3 = getc (fin); + if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { + /* Skip zero width no-break space. */ + goto again; + } + return (c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f); +} diff --git a/BESM6/besm6_sys.c b/BESM6/besm6_sys.c index 5d6e6927..1c128938 100644 --- a/BESM6/besm6_sys.c +++ b/BESM6/besm6_sys.c @@ -1,719 +1,719 @@ -/* - * besm6_sys.c: BESM-6 simulator interface - * - * Copyright (c) 2009, Serge Vakulenko - * Copyright (c) 2009, Leonid Broukhis - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You can redistribute this program and/or modify it under the terms of - * the GNU General Public License as published by the Free Software Foundation; - * either version 2 of the License, or (at your discretion) any later version. - * See the accompanying file "COPYING" for more details. - * - * This file implements three essential functions: - * - * sim_load() - loading and dumping memory and CPU state - * in a way, specific for BESM-6 architecture - * fprint_sym() - print a machune instruction using - * opcode mnemonic or in a digital format - * parse_sym() - scan a string and build an instruction - * word from it - */ -#include "besm6_defs.h" -#include -#include - -const char *opname_short_bemsh [64] = { - "зп", "зпм", "рег", "счм", "сл", "вч", "вчоб", "вчаб", - "сч", "и", "нтж", "слц", "знак", "или", "дел", "умн", - "сбр", "рзб", "чед", "нед", "слп", "вчп", "сд", "рж", - "счрж", "счмр", "э32", "увв", "слпа", "вчпа", "сда", "ржа", - "уи", "уим", "счи", "счим", "уии", "сли", "э46", "э47", - "э50", "э51", "э52", "э53", "э54", "э55", "э56", "э57", - "э60", "э61", "э62", "э63", "э64", "э65", "э66", "э67", - "э70", "э71", "э72", "э73", "э74", "э75", "э76", "э77", -}; - -static const char *opname_long_bemsh [16] = { - "э20", "э21", "мода", "мод", "уиа", "слиа", "по", "пе", - "пб", "пв", "выпр", "стоп", "пио", "пино", "э36", "цикл", -}; - -const char *opname_short_madlen [64] = { - "atx", "stx", "mod", "xts", "a+x", "a-x", "x-a", "amx", - "xta", "aax", "aex", "arx", "avx", "aox", "a/x", "a*x", - "apx", "aux", "acx", "anx", "e+x", "e-x", "asx", "xtr", - "rte", "yta", "*32", "ext", "e+n", "e-n", "asn", "ntr", - "ati", "sti", "ita", "its", "mtj", "j+m", "*46", "*47", - "*50", "*51", "*52", "*53", "*54", "*55", "*56", "*57", - "*60", "*61", "*62", "*63", "*64", "*65", "*66", "*67", - "*70", "*71", "*72", "*73", "*74", "*75", "*76", "*77", -}; - -static const char *opname_long_madlen [16] = { - "*20", "*21", "utc", "wtc", "vtm", "utm", "uza", "u1a", - "uj", "vjm", "ij", "stop", "vzm", "v1m", "*36", "vlm", -}; - -/* - * Выдача мнемоники по коду инструкции. - * Код должен быть в диапазоне 000..077 или 0200..0370. - */ -const char *besm6_opname (int opcode) -{ - if (sim_switches & SWMASK ('L')) { - /* Latin mnemonics. */ - if (opcode & 0200) - return opname_long_madlen [(opcode >> 3) & 017]; - return opname_short_madlen [opcode]; - } - if (opcode & 0200) - return opname_long_bemsh [(opcode >> 3) & 017]; - return opname_short_bemsh [opcode]; -}; - -/* - * Выдача кода инструкции по мнемонике (UTF-8). - */ -int besm6_opcode (char *instr) -{ - int i; - - for (i=0; i<64; ++i) - if (strcmp (opname_short_bemsh[i], instr) == 0 || - strcmp (opname_short_madlen[i], instr) == 0) - return i; - for (i=0; i<16; ++i) - if (strcmp (opname_long_bemsh[i], instr) == 0 || - strcmp (opname_long_madlen[i], instr) == 0) - return (i << 3) | 0200; - return -1; -} - -/* - * Выдача на консоль и в файл протокола. - * Если первый символ формата - подчерк, на консоль не печатаем. - * Добавляет перевод строки. - */ -void besm6_log (const char *fmt, ...) -{ - va_list args; - - if (*fmt == '_') - ++fmt; - else { - va_start (args, fmt); - vprintf (fmt, args); - printf ("\r\n"); - va_end (args); - } - if (sim_log) { - va_start (args, fmt); - vfprintf (sim_log, fmt, args); - if (sim_log == stdout) - fprintf (sim_log, "\r"); - fprintf (sim_log, "\n"); - fflush (sim_log); - va_end (args); - } -} - -/* - * Не добавляет перевод строки. - */ -void besm6_log_cont (const char *fmt, ...) -{ - va_list args; - - if (*fmt == '_') - ++fmt; - else { - va_start (args, fmt); - vprintf (fmt, args); - va_end (args); - } - if (sim_log) { - va_start (args, fmt); - vfprintf (sim_log, fmt, args); - fflush (sim_log); - va_end (args); - } -} - -/* - * Выдача на консоль и в файл отладки: если включён режим "cpu debug". - * Добавляет перевод строки. - */ -void besm6_debug (const char *fmt, ...) -{ - va_list args; - - va_start (args, fmt); - vprintf (fmt, args); - printf ("\r\n"); - va_end (args); - if (sim_deb && sim_deb != stdout) { - va_start (args, fmt); - vfprintf (sim_deb, fmt, args); - fprintf (sim_deb, "\n"); - fflush (sim_deb); - va_end (args); - } -} - -/* - * Преобразование вещественного числа в формат БЭСМ-6. - * - * Представление чисел в IEEE 754 (double): - * 64 63———53 52————–1 - * знак порядок мантисса - * Старший (53-й) бит мантиссы не хранится и всегда равен 1. - * - * Представление чисел в БЭСМ-6: - * 48——–42 41 40————————————————–1 - * порядок знак мантисса в доп. коде - */ -t_value ieee_to_besm6 (double d) -{ - t_value word; - int exponent; - int sign; - - sign = d < 0; - if (sign) - d = -d; - d = frexp (d, &exponent); - /* 0.5 <= d < 1.0 */ - d = ldexp (d, 40); - word = d; - if (d - word >= 0.5) - word += 1; /* Округление. */ - if (exponent < -64) - return 0LL; /* Близкое к нулю число */ - if (exponent > 63) { - return sign ? - 0xFEFFFFFFFFFFLL : /* Максимальное число */ - 0xFF0000000000LL; /* Минимальное число */ - } - if (sign) - word = 0x20000000000LL-word; /* Знак. */ - word |= ((t_value) (exponent + 64)) << 41; - return word; -} - -double besm6_to_ieee (t_value word) -{ - double mantissa; - - /* Убираем свертку */ - word &= BITS48; - - /* Сдвигаем так, чтобы знак мантиссы пришелся на знак целого; - * таким образом, mantissa равно исходной мантиссе, умноженной на 2**63. - */ - mantissa = (t_int64) word << (64 - 48 + 7); - - int exponent = word >> 41; - - /* Порядок смещен вверх на 64, и мантиссу нужно скорректировать */ - return ldexp (mantissa, exponent - 64 - 63); -} - -/* - * Пропуск пробелов. - */ -char *skip_spaces (char *p) -{ - for (;;) { - if (*p == (char) 0xEF && p[1] == (char) 0xBB && p[2] == (char) 0xBF) { - /* Skip zero width no-break space. */ - p += 3; - continue; - } - if (*p == ' ' || *p == '\t') { - ++p; - continue; - } - return p; - } -} - -/* - * Fetch Unicode symbol from UTF-8 string. - * Advance string pointer. - */ -int utf8_to_unicode (char **p) -{ - int c1, c2, c3; - - c1 = (unsigned char) *(*p)++; - if (! (c1 & 0x80)) - return c1; - c2 = (unsigned char) *(*p)++; - if (! (c1 & 0x20)) - return (c1 & 0x1f) << 6 | (c2 & 0x3f); - c3 = (unsigned char) *(*p)++; - return (c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f); -} - -char *besm6_parse_octal (char *cptr, int *offset) -{ - char *eptr; - - *offset = strtol (cptr, &eptr, 8); - if (eptr == cptr) - return 0; - return eptr; -} - -static char *get_alnum (char *iptr, char *optr) -{ - while ((*iptr >= 'a' && *iptr<='z') || - (*iptr >= 'A' && *iptr<='Z') || - (*iptr >= '0' && *iptr<='9') || (*iptr & 0x80)) { - *optr++ = *iptr++; - } - *optr = 0; - return iptr; -} - -/* - * Parse single instruction (half word). - * Allow mnemonics or octal code. - */ -char *parse_instruction (char *cptr, uint32 *val) -{ - int opcode, reg, addr, negate; - char gbuf[CBUFSIZE]; - - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr >= '0' && *cptr <= '7') { - /* Восьмеричное представление. */ - cptr = besm6_parse_octal (cptr, ®); /* get register */ - if (! cptr || reg > 15) { - /*printf ("Bad register\n");*/ - return 0; - } - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr == '2' || *cptr == '3') { - /* Длинная команда. */ - cptr = besm6_parse_octal (cptr, &opcode); - if (! cptr || opcode < 020 || opcode > 037) { - /*printf ("Bad long opcode\n");*/ - return 0; - } - opcode <<= 3; - } else { - /* Короткая команда. */ - cptr = besm6_parse_octal (cptr, &opcode); - if (! cptr || opcode > 0177) { - /*printf ("Bad short opcode\n");*/ - return 0; - } - } - cptr = besm6_parse_octal (cptr, &addr); /* get address */ - if (! cptr || addr > BITS(15) || - (opcode <= 0177 && addr > BITS(12))) { - /*printf ("Bad address\n");*/ - return 0; - } - } else { - /* Мнемоническое представление команды. */ - cptr = get_alnum (cptr, gbuf); /* get opcode */ - opcode = besm6_opcode (gbuf); - if (opcode < 0) { - /*printf ("Bad opname: %s\n", gbuf);*/ - return 0; - } - negate = 0; - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr == '-') { /* negative offset */ - negate = 1; - cptr = skip_spaces (cptr + 1); /* absorb spaces */ - } - addr = 0; - if (*cptr >= '0' && *cptr <= '7') { - /* Восьмеричный адрес. */ - cptr = besm6_parse_octal (cptr, &addr); - if (! cptr || addr > BITS(15)) { - /*printf ("Bad address: %o\n", addr);*/ - return 0; - } - if (negate) - addr = (- addr) & BITS(15); - if (opcode <= 077 && addr > BITS(12)) { - if (addr < 070000) { - /*printf ("Bad short address: %o\n", addr);*/ - return 0; - } - opcode |= 0100; - addr &= BITS(12); - } - } - reg = 0; - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr == '(') { - /* Индекс-регистр в скобках. */ - cptr = besm6_parse_octal (cptr+1, ®); - if (! cptr || reg > 15) { - /*printf ("Bad register: %o\n", reg);*/ - return 0; - } - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr != ')') { - /*printf ("No closing brace\n");*/ - return 0; - } - ++cptr; - } - } - *val = reg << 20 | opcode << 12 | addr; - return cptr; -} - -/* - * Instruction parse: two commands per word. - */ -t_stat parse_instruction_word (char *cptr, t_value *val) -{ - uint32 left, right; - - *val = 0; - cptr = parse_instruction (cptr, &left); - if (! cptr) - return SCPE_ARG; - right = 0; - cptr = skip_spaces (cptr); - if (*cptr == ',') { - cptr = parse_instruction (cptr + 1, &right); - if (! cptr) - return SCPE_ARG; - } - cptr = skip_spaces (cptr); /* absorb spaces */ - if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') { - /*printf ("Extra symbols at eoln: %s\n", cptr);*/ - return SCPE_2MARG; - } - *val = (t_value) left << 24 | right; - return SCPE_OK; -} - -/* - * Печать машинной инструкции с мнемоникой. - */ -void besm6_fprint_cmd (FILE *of, uint32 cmd) -{ - int reg, opcode, addr; - - reg = (cmd >> 20) & 017; - if (cmd & BBIT(20)) { - opcode = (cmd >> 12) & 0370; - addr = cmd & BITS(15); - } else { - opcode = (cmd >> 12) & 077; - addr = cmd & 07777; - if (cmd & BBIT(19)) - addr |= 070000; - } - fprintf (of, "%s", besm6_opname (opcode)); - if (addr) { - fprintf (of, " "); - if (addr >= 077700) - fprintf (of, "-%o", (addr ^ 077777) + 1); - else - fprintf (of, "%o", addr); - } - if (reg) { - if (! addr) - fprintf (of, " "); - fprintf (of, "(%o)", reg); - } -} - -/* - * Печать машинной инструкции в восьмеричном виде. - */ -void besm6_fprint_insn (FILE *of, uint32 insn) -{ - if (insn & BBIT(20)) - fprintf (of, "%02o %02o %05o ", - insn >> 20, (insn >> 15) & 037, insn & BITS(15)); - else - fprintf (of, "%02o %03o %04o ", - insn >> 20, (insn >> 12) & 0177, insn & 07777); -} - -/* - * Symbolic decode - * - * Inputs: - * *of = output stream - * addr = current PC - * *val = pointer to data - * *uptr = pointer to unit - * sw = switches - * Outputs: - * return = status code - */ -t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, - UNIT *uptr, int32 sw) -{ - t_value cmd; - - if (uptr && (uptr != &cpu_unit)) /* must be CPU */ - return SCPE_ARG; - - cmd = val[0]; - - - if (sw & SWMASK ('M')) { /* symbolic decode? */ - if (sw & SIM_SW_STOP && addr == PC && !(RUU & RUU_RIGHT_INSTR)) - fprintf (of, "-> "); - besm6_fprint_cmd (of, cmd >> 24); - if (sw & SIM_SW_STOP) /* stop point */ - fprintf (of, ", "); - else - fprintf (of, ",\n\t"); - if (sw & SIM_SW_STOP && addr == PC && (RUU & RUU_RIGHT_INSTR)) - fprintf (of, "-> "); - besm6_fprint_cmd (of, cmd & BITS(24)); - - } else if (sw & SWMASK ('I')) { - besm6_fprint_insn (of, (cmd >> 24) & BITS(24)); - besm6_fprint_insn (of, cmd & BITS(24)); - } else if (sw & SWMASK ('F')) { - fprintf (of, "%#.2g", besm6_to_ieee(cmd)); - } else if (sw & SWMASK ('B')) { - fprintf (of, "%03o %03o %03o %03o %03o %03o", - (int) (cmd >> 40) & 0377, - (int) (cmd >> 32) & 0377, - (int) (cmd >> 24) & 0377, - (int) (cmd >> 16) & 0377, - (int) (cmd >> 8) & 0377, - (int) cmd & 0377); - } else if (sw & SWMASK ('X')) { - fprintf (of, "%013llx", cmd); - } else - fprintf (of, "%04o %04o %04o %04o", - (int) (cmd >> 36) & 07777, - (int) (cmd >> 24) & 07777, - (int) (cmd >> 12) & 07777, - (int) cmd & 07777); - return SCPE_OK; -} - -/* - * Symbolic input - * - * Inputs: - * *cptr = pointer to input string - * addr = current PC - * *uptr = pointer to unit - * *val = pointer to output values - * sw = switches - * Outputs: - * status = error status - */ -t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) -{ - int32 i; - - if (uptr && (uptr != &cpu_unit)) /* must be CPU */ - return SCPE_ARG; - if (! parse_instruction_word (cptr, val)) /* symbolic parse? */ - return SCPE_OK; - - val[0] = 0; - for (i=0; i<16; i++) { - if (*cptr < '0' || *cptr > '7') - break; - val[0] = (val[0] << 3) | (*cptr - '0'); - cptr = skip_spaces (cptr+1); /* next char */ - } - if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') { - /*printf ("Extra symbols at eoln: %s\n", cptr);*/ - return SCPE_2MARG; - } - return SCPE_OK; -} - -/* - * Чтение строки входного файла. - * Форматы строк: - * п 76543 - адрес пуска - * в 12345 - адрес ввода - * ч -123.45e+6 - вещественное число - * с 0123 4567 0123 4567 - восьмеричное слово - * к 00 22 00000 00 010 0000 - команды - */ -t_stat besm6_read_line (FILE *input, int *type, t_value *val) -{ - char buf [512], *p; - int i, c; -again: - if (! fgets (buf, sizeof (buf), input)) { - *type = 0; - return SCPE_OK; - } - p = skip_spaces (buf); - if (*p == '\n' || *p == ';') - goto again; - c = utf8_to_unicode (&p); - if (c == CYRILLIC_SMALL_LETTER_VE || - c == CYRILLIC_CAPITAL_LETTER_VE || - c == 'b' || c == 'B') { - /* Адрес размещения данных. */ - *type = ':'; - *val = strtol (p, 0, 8); - return SCPE_OK; - } - if (c == CYRILLIC_SMALL_LETTER_PE || - c == CYRILLIC_CAPITAL_LETTER_PE || - c == 'p' || c == 'P') { - /* Стартовый адрес. */ - *type = '@'; - *val = strtol (p, 0, 8); - return SCPE_OK; - } - if (c == CYRILLIC_SMALL_LETTER_CHE || - c == CYRILLIC_CAPITAL_LETTER_CHE || - c == 'f' || c == 'F') { - /* Вещественное число. */ - *type = '='; - *val = ieee_to_besm6 (strtod (p, 0)); - return SCPE_OK; - } - if (c == CYRILLIC_SMALL_LETTER_ES || - c == CYRILLIC_CAPITAL_LETTER_ES || - c == 'c' || c == 'C') { - /* Восьмеричное слово. */ - *type = '='; - *val = 0; - for (i=0; i<16; ++i) { - p = skip_spaces (p); - if (*p < '0' || *p > '7') { - if (i == 0) { - /* слишком короткое слово */ - goto bad; - } - break; - } - *val = *val << 3 | (*p++ - '0'); - } - return SCPE_OK; - } - if (c == CYRILLIC_SMALL_LETTER_KA || - c == CYRILLIC_CAPITAL_LETTER_KA || - c == 'k' || c == 'K') { - /* Команда. */ - *type = '*'; - if (parse_instruction_word (p, val) != SCPE_OK) - goto bad; - return SCPE_OK; - } - /* Неверная строка входного файла */ -bad: besm6_log ("Invalid input line: %s", buf); - return SCPE_FMT; -} - -/* - * Load memory from file. - */ -t_stat besm6_load (FILE *input) -{ - int addr, type; - t_value word; - t_stat err; - - addr = 1; - PC = 1; - for (;;) { - err = besm6_read_line (input, &type, &word); - if (err) - return err; - switch (type) { - case 0: /* EOF */ - return SCPE_OK; - case ':': /* address */ - addr = word; - break; - case '=': /* word */ - if (addr < 010) - pult [addr] = SET_CONVOL (word, CONVOL_NUMBER); - else - memory [addr] = SET_CONVOL (word, CONVOL_NUMBER); - ++addr; - break; - case '*': /* instruction */ - if (addr < 010) - pult [addr] = SET_CONVOL (word, CONVOL_INSN); - else - memory [addr] = SET_CONVOL (word, CONVOL_INSN); - ++addr; - break; - case '@': /* start address */ - PC = word; - break; - } - if (addr > MEMSIZE) - return SCPE_FMT; - } - return SCPE_OK; -} - -/* - * Dump memory to file. - */ -t_stat besm6_dump (FILE *of, char *fnam) -{ - int addr, last_addr = -1; - t_value word; - - fprintf (of, "; %s\n", fnam); - for (addr=1; addr> 24); - fprintf (of, ", "); - besm6_fprint_cmd (of, word & BITS(24)); - fprintf (of, "\t\t; %05o - ", addr); - fprintf (of, "%04o %04o %04o %04o\n", - (int) (word >> 36) & 07777, - (int) (word >> 24) & 07777, - (int) (word >> 12) & 07777, - (int) word & 07777); - } else { - fprintf (of, "с %04o %04o %04o %04o", - (int) (word >> 36) & 07777, - (int) (word >> 24) & 07777, - (int) (word >> 12) & 07777, - (int) word & 07777); - fprintf (of, "\t\t; %05o\n", addr); - } - } - return SCPE_OK; -} - -/* - * Loader/dumper - */ -t_stat sim_load (FILE *fi, char *cptr, char *fnam, int dump_flag) -{ - if (dump_flag) - return besm6_dump (fi, fnam); - - return besm6_load (fi); -} +/* + * besm6_sys.c: BESM-6 simulator interface + * + * Copyright (c) 2009, Serge Vakulenko + * Copyright (c) 2009, Leonid Broukhis + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; + * either version 2 of the License, or (at your discretion) any later version. + * See the accompanying file "COPYING" for more details. + * + * This file implements three essential functions: + * + * sim_load() - loading and dumping memory and CPU state + * in a way, specific for BESM-6 architecture + * fprint_sym() - print a machune instruction using + * opcode mnemonic or in a digital format + * parse_sym() - scan a string and build an instruction + * word from it + */ +#include "besm6_defs.h" +#include +#include + +const char *opname_short_bemsh [64] = { + "зп", "зпм", "рег", "счм", "сл", "вч", "вчоб", "вчаб", + "сч", "и", "нтж", "слц", "знак", "или", "дел", "умн", + "сбр", "рзб", "чед", "нед", "слп", "вчп", "сд", "рж", + "счрж", "счмр", "э32", "увв", "слпа", "вчпа", "сда", "ржа", + "уи", "уим", "счи", "счим", "уии", "сли", "э46", "э47", + "э50", "э51", "э52", "э53", "э54", "э55", "э56", "э57", + "э60", "э61", "э62", "э63", "э64", "э65", "э66", "э67", + "э70", "э71", "э72", "э73", "э74", "э75", "э76", "э77", +}; + +static const char *opname_long_bemsh [16] = { + "э20", "э21", "мода", "мод", "уиа", "слиа", "по", "пе", + "пб", "пв", "выпр", "стоп", "пио", "пино", "э36", "цикл", +}; + +const char *opname_short_madlen [64] = { + "atx", "stx", "mod", "xts", "a+x", "a-x", "x-a", "amx", + "xta", "aax", "aex", "arx", "avx", "aox", "a/x", "a*x", + "apx", "aux", "acx", "anx", "e+x", "e-x", "asx", "xtr", + "rte", "yta", "*32", "ext", "e+n", "e-n", "asn", "ntr", + "ati", "sti", "ita", "its", "mtj", "j+m", "*46", "*47", + "*50", "*51", "*52", "*53", "*54", "*55", "*56", "*57", + "*60", "*61", "*62", "*63", "*64", "*65", "*66", "*67", + "*70", "*71", "*72", "*73", "*74", "*75", "*76", "*77", +}; + +static const char *opname_long_madlen [16] = { + "*20", "*21", "utc", "wtc", "vtm", "utm", "uza", "u1a", + "uj", "vjm", "ij", "stop", "vzm", "v1m", "*36", "vlm", +}; + +/* + * Выдача мнемоники по коду инструкции. + * Код должен быть в диапазоне 000..077 или 0200..0370. + */ +const char *besm6_opname (int opcode) +{ + if (sim_switches & SWMASK ('L')) { + /* Latin mnemonics. */ + if (opcode & 0200) + return opname_long_madlen [(opcode >> 3) & 017]; + return opname_short_madlen [opcode]; + } + if (opcode & 0200) + return opname_long_bemsh [(opcode >> 3) & 017]; + return opname_short_bemsh [opcode]; +}; + +/* + * Выдача кода инструкции по мнемонике (UTF-8). + */ +int besm6_opcode (char *instr) +{ + int i; + + for (i=0; i<64; ++i) + if (strcmp (opname_short_bemsh[i], instr) == 0 || + strcmp (opname_short_madlen[i], instr) == 0) + return i; + for (i=0; i<16; ++i) + if (strcmp (opname_long_bemsh[i], instr) == 0 || + strcmp (opname_long_madlen[i], instr) == 0) + return (i << 3) | 0200; + return -1; +} + +/* + * Выдача на консоль и в файл протокола. + * Если первый символ формата - подчерк, на консоль не печатаем. + * Добавляет перевод строки. + */ +void besm6_log (const char *fmt, ...) +{ + va_list args; + + if (*fmt == '_') + ++fmt; + else { + va_start (args, fmt); + vprintf (fmt, args); + printf ("\r\n"); + va_end (args); + } + if (sim_log) { + va_start (args, fmt); + vfprintf (sim_log, fmt, args); + if (sim_log == stdout) + fprintf (sim_log, "\r"); + fprintf (sim_log, "\n"); + fflush (sim_log); + va_end (args); + } +} + +/* + * Не добавляет перевод строки. + */ +void besm6_log_cont (const char *fmt, ...) +{ + va_list args; + + if (*fmt == '_') + ++fmt; + else { + va_start (args, fmt); + vprintf (fmt, args); + va_end (args); + } + if (sim_log) { + va_start (args, fmt); + vfprintf (sim_log, fmt, args); + fflush (sim_log); + va_end (args); + } +} + +/* + * Выдача на консоль и в файл отладки: если включён режим "cpu debug". + * Добавляет перевод строки. + */ +void besm6_debug (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + vprintf (fmt, args); + printf ("\r\n"); + va_end (args); + if (sim_deb && sim_deb != stdout) { + va_start (args, fmt); + vfprintf (sim_deb, fmt, args); + fprintf (sim_deb, "\n"); + fflush (sim_deb); + va_end (args); + } +} + +/* + * Преобразование вещественного числа в формат БЭСМ-6. + * + * Представление чисел в IEEE 754 (double): + * 64 63———53 52————–1 + * знак порядок мантисса + * Старший (53-й) бит мантиссы не хранится и всегда равен 1. + * + * Представление чисел в БЭСМ-6: + * 48——–42 41 40————————————————–1 + * порядок знак мантисса в доп. коде + */ +t_value ieee_to_besm6 (double d) +{ + t_value word; + int exponent; + int sign; + + sign = d < 0; + if (sign) + d = -d; + d = frexp (d, &exponent); + /* 0.5 <= d < 1.0 */ + d = ldexp (d, 40); + word = d; + if (d - word >= 0.5) + word += 1; /* Округление. */ + if (exponent < -64) + return 0LL; /* Близкое к нулю число */ + if (exponent > 63) { + return sign ? + 0xFEFFFFFFFFFFLL : /* Максимальное число */ + 0xFF0000000000LL; /* Минимальное число */ + } + if (sign) + word = 0x20000000000LL-word; /* Знак. */ + word |= ((t_value) (exponent + 64)) << 41; + return word; +} + +double besm6_to_ieee (t_value word) +{ + double mantissa; + + /* Убираем свертку */ + word &= BITS48; + + /* Сдвигаем так, чтобы знак мантиссы пришелся на знак целого; + * таким образом, mantissa равно исходной мантиссе, умноженной на 2**63. + */ + mantissa = (t_int64) word << (64 - 48 + 7); + + int exponent = word >> 41; + + /* Порядок смещен вверх на 64, и мантиссу нужно скорректировать */ + return ldexp (mantissa, exponent - 64 - 63); +} + +/* + * Пропуск пробелов. + */ +char *skip_spaces (char *p) +{ + for (;;) { + if (*p == (char) 0xEF && p[1] == (char) 0xBB && p[2] == (char) 0xBF) { + /* Skip zero width no-break space. */ + p += 3; + continue; + } + if (*p == ' ' || *p == '\t' || *p == '\r') { + ++p; + continue; + } + return p; + } +} + +/* + * Fetch Unicode symbol from UTF-8 string. + * Advance string pointer. + */ +int utf8_to_unicode (char **p) +{ + int c1, c2, c3; + + c1 = (unsigned char) *(*p)++; + if (! (c1 & 0x80)) + return c1; + c2 = (unsigned char) *(*p)++; + if (! (c1 & 0x20)) + return (c1 & 0x1f) << 6 | (c2 & 0x3f); + c3 = (unsigned char) *(*p)++; + return (c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f); +} + +char *besm6_parse_octal (char *cptr, int *offset) +{ + char *eptr; + + *offset = strtol (cptr, &eptr, 8); + if (eptr == cptr) + return 0; + return eptr; +} + +static char *get_alnum (char *iptr, char *optr) +{ + while ((*iptr >= 'a' && *iptr<='z') || + (*iptr >= 'A' && *iptr<='Z') || + (*iptr >= '0' && *iptr<='9') || (*iptr & 0x80)) { + *optr++ = *iptr++; + } + *optr = 0; + return iptr; +} + +/* + * Parse single instruction (half word). + * Allow mnemonics or octal code. + */ +char *parse_instruction (char *cptr, uint32 *val) +{ + int opcode, reg, addr, negate; + char gbuf[CBUFSIZE]; + + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr >= '0' && *cptr <= '7') { + /* Восьмеричное представление. */ + cptr = besm6_parse_octal (cptr, ®); /* get register */ + if (! cptr || reg > 15) { + /*printf ("Bad register\n");*/ + return 0; + } + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr == '2' || *cptr == '3') { + /* Длинная команда. */ + cptr = besm6_parse_octal (cptr, &opcode); + if (! cptr || opcode < 020 || opcode > 037) { + /*printf ("Bad long opcode\n");*/ + return 0; + } + opcode <<= 3; + } else { + /* Короткая команда. */ + cptr = besm6_parse_octal (cptr, &opcode); + if (! cptr || opcode > 0177) { + /*printf ("Bad short opcode\n");*/ + return 0; + } + } + cptr = besm6_parse_octal (cptr, &addr); /* get address */ + if (! cptr || addr > BITS(15) || + (opcode <= 0177 && addr > BITS(12))) { + /*printf ("Bad address\n");*/ + return 0; + } + } else { + /* Мнемоническое представление команды. */ + cptr = get_alnum (cptr, gbuf); /* get opcode */ + opcode = besm6_opcode (gbuf); + if (opcode < 0) { + /*printf ("Bad opname: %s\n", gbuf);*/ + return 0; + } + negate = 0; + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr == '-') { /* negative offset */ + negate = 1; + cptr = skip_spaces (cptr + 1); /* absorb spaces */ + } + addr = 0; + if (*cptr >= '0' && *cptr <= '7') { + /* Восьмеричный адрес. */ + cptr = besm6_parse_octal (cptr, &addr); + if (! cptr || addr > BITS(15)) { + /*printf ("Bad address: %o\n", addr);*/ + return 0; + } + if (negate) + addr = (- addr) & BITS(15); + if (opcode <= 077 && addr > BITS(12)) { + if (addr < 070000) { + /*printf ("Bad short address: %o\n", addr);*/ + return 0; + } + opcode |= 0100; + addr &= BITS(12); + } + } + reg = 0; + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr == '(') { + /* Индекс-регистр в скобках. */ + cptr = besm6_parse_octal (cptr+1, ®); + if (! cptr || reg > 15) { + /*printf ("Bad register: %o\n", reg);*/ + return 0; + } + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr != ')') { + /*printf ("No closing brace\n");*/ + return 0; + } + ++cptr; + } + } + *val = reg << 20 | opcode << 12 | addr; + return cptr; +} + +/* + * Instruction parse: two commands per word. + */ +t_stat parse_instruction_word (char *cptr, t_value *val) +{ + uint32 left, right; + + *val = 0; + cptr = parse_instruction (cptr, &left); + if (! cptr) + return SCPE_ARG; + right = 0; + cptr = skip_spaces (cptr); + if (*cptr == ',') { + cptr = parse_instruction (cptr + 1, &right); + if (! cptr) + return SCPE_ARG; + } + cptr = skip_spaces (cptr); /* absorb spaces */ + if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') { + /*printf ("Extra symbols at eoln: %s\n", cptr);*/ + return SCPE_2MARG; + } + *val = (t_value) left << 24 | right; + return SCPE_OK; +} + +/* + * Печать машинной инструкции с мнемоникой. + */ +void besm6_fprint_cmd (FILE *of, uint32 cmd) +{ + int reg, opcode, addr; + + reg = (cmd >> 20) & 017; + if (cmd & BBIT(20)) { + opcode = (cmd >> 12) & 0370; + addr = cmd & BITS(15); + } else { + opcode = (cmd >> 12) & 077; + addr = cmd & 07777; + if (cmd & BBIT(19)) + addr |= 070000; + } + fprintf (of, "%s", besm6_opname (opcode)); + if (addr) { + fprintf (of, " "); + if (addr >= 077700) + fprintf (of, "-%o", (addr ^ 077777) + 1); + else + fprintf (of, "%o", addr); + } + if (reg) { + if (! addr) + fprintf (of, " "); + fprintf (of, "(%o)", reg); + } +} + +/* + * Печать машинной инструкции в восьмеричном виде. + */ +void besm6_fprint_insn (FILE *of, uint32 insn) +{ + if (insn & BBIT(20)) + fprintf (of, "%02o %02o %05o ", + insn >> 20, (insn >> 15) & 037, insn & BITS(15)); + else + fprintf (of, "%02o %03o %04o ", + insn >> 20, (insn >> 12) & 0177, insn & 07777); +} + +/* + * Symbolic decode + * + * Inputs: + * *of = output stream + * addr = current PC + * *val = pointer to data + * *uptr = pointer to unit + * sw = switches + * Outputs: + * return = status code + */ +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ + t_value cmd; + + if (uptr && (uptr != &cpu_unit)) /* must be CPU */ + return SCPE_ARG; + + cmd = val[0]; + + + if (sw & SWMASK ('M')) { /* symbolic decode? */ + if (sw & SIM_SW_STOP && addr == PC && !(RUU & RUU_RIGHT_INSTR)) + fprintf (of, "-> "); + besm6_fprint_cmd (of, cmd >> 24); + if (sw & SIM_SW_STOP) /* stop point */ + fprintf (of, ", "); + else + fprintf (of, ",\n\t"); + if (sw & SIM_SW_STOP && addr == PC && (RUU & RUU_RIGHT_INSTR)) + fprintf (of, "-> "); + besm6_fprint_cmd (of, cmd & BITS(24)); + + } else if (sw & SWMASK ('I')) { + besm6_fprint_insn (of, (cmd >> 24) & BITS(24)); + besm6_fprint_insn (of, cmd & BITS(24)); + } else if (sw & SWMASK ('F')) { + fprintf (of, "%#.2g", besm6_to_ieee(cmd)); + } else if (sw & SWMASK ('B')) { + fprintf (of, "%03o %03o %03o %03o %03o %03o", + (int) (cmd >> 40) & 0377, + (int) (cmd >> 32) & 0377, + (int) (cmd >> 24) & 0377, + (int) (cmd >> 16) & 0377, + (int) (cmd >> 8) & 0377, + (int) cmd & 0377); + } else if (sw & SWMASK ('X')) { + fprintf (of, "%013llx", cmd); + } else + fprintf (of, "%04o %04o %04o %04o", + (int) (cmd >> 36) & 07777, + (int) (cmd >> 24) & 07777, + (int) (cmd >> 12) & 07777, + (int) cmd & 07777); + return SCPE_OK; +} + +/* + * Symbolic input + * + * Inputs: + * *cptr = pointer to input string + * addr = current PC + * *uptr = pointer to unit + * *val = pointer to output values + * sw = switches + * Outputs: + * status = error status + */ +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + int32 i; + + if (uptr && (uptr != &cpu_unit)) /* must be CPU */ + return SCPE_ARG; + if (! parse_instruction_word (cptr, val)) /* symbolic parse? */ + return SCPE_OK; + + val[0] = 0; + for (i=0; i<16; i++) { + if (*cptr < '0' || *cptr > '7') + break; + val[0] = (val[0] << 3) | (*cptr - '0'); + cptr = skip_spaces (cptr+1); /* next char */ + } + if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') { + /*printf ("Extra symbols at eoln: %s\n", cptr);*/ + return SCPE_2MARG; + } + return SCPE_OK; +} + +/* + * Чтение строки входного файла. + * Форматы строк: + * п 76543 - адрес пуска + * в 12345 - адрес ввода + * ч -123.45e+6 - вещественное число + * с 0123 4567 0123 4567 - восьмеричное слово + * к 00 22 00000 00 010 0000 - команды + */ +t_stat besm6_read_line (FILE *input, int *type, t_value *val) +{ + char buf [512], *p; + int i, c; +again: + if (! fgets (buf, sizeof (buf), input)) { + *type = 0; + return SCPE_OK; + } + p = skip_spaces (buf); + if (*p == '\n' || *p == ';') + goto again; + c = utf8_to_unicode (&p); + if (c == CYRILLIC_SMALL_LETTER_VE || + c == CYRILLIC_CAPITAL_LETTER_VE || + c == 'b' || c == 'B') { + /* Адрес размещения данных. */ + *type = ':'; + *val = strtol (p, 0, 8); + return SCPE_OK; + } + if (c == CYRILLIC_SMALL_LETTER_PE || + c == CYRILLIC_CAPITAL_LETTER_PE || + c == 'p' || c == 'P') { + /* Стартовый адрес. */ + *type = '@'; + *val = strtol (p, 0, 8); + return SCPE_OK; + } + if (c == CYRILLIC_SMALL_LETTER_CHE || + c == CYRILLIC_CAPITAL_LETTER_CHE || + c == 'f' || c == 'F') { + /* Вещественное число. */ + *type = '='; + *val = ieee_to_besm6 (strtod (p, 0)); + return SCPE_OK; + } + if (c == CYRILLIC_SMALL_LETTER_ES || + c == CYRILLIC_CAPITAL_LETTER_ES || + c == 'c' || c == 'C') { + /* Восьмеричное слово. */ + *type = '='; + *val = 0; + for (i=0; i<16; ++i) { + p = skip_spaces (p); + if (*p < '0' || *p > '7') { + if (i == 0) { + /* слишком короткое слово */ + goto bad; + } + break; + } + *val = *val << 3 | (*p++ - '0'); + } + return SCPE_OK; + } + if (c == CYRILLIC_SMALL_LETTER_KA || + c == CYRILLIC_CAPITAL_LETTER_KA || + c == 'k' || c == 'K') { + /* Команда. */ + *type = '*'; + if (parse_instruction_word (p, val) != SCPE_OK) + goto bad; + return SCPE_OK; + } + /* Неверная строка входного файла */ +bad: besm6_log ("Invalid input line: %s", buf); + return SCPE_FMT; +} + +/* + * Load memory from file. + */ +t_stat besm6_load (FILE *input) +{ + int addr, type; + t_value word; + t_stat err; + + addr = 1; + PC = 1; + for (;;) { + err = besm6_read_line (input, &type, &word); + if (err) + return err; + switch (type) { + case 0: /* EOF */ + return SCPE_OK; + case ':': /* address */ + addr = word; + break; + case '=': /* word */ + if (addr < 010) + pult [addr] = SET_CONVOL (word, CONVOL_NUMBER); + else + memory [addr] = SET_CONVOL (word, CONVOL_NUMBER); + ++addr; + break; + case '*': /* instruction */ + if (addr < 010) + pult [addr] = SET_CONVOL (word, CONVOL_INSN); + else + memory [addr] = SET_CONVOL (word, CONVOL_INSN); + ++addr; + break; + case '@': /* start address */ + PC = word; + break; + } + if (addr > MEMSIZE) + return SCPE_FMT; + } + return SCPE_OK; +} + +/* + * Dump memory to file. + */ +t_stat besm6_dump (FILE *of, char *fnam) +{ + int addr, last_addr = -1; + t_value word; + + fprintf (of, "; %s\n", fnam); + for (addr=1; addr> 24); + fprintf (of, ", "); + besm6_fprint_cmd (of, word & BITS(24)); + fprintf (of, "\t\t; %05o - ", addr); + fprintf (of, "%04o %04o %04o %04o\n", + (int) (word >> 36) & 07777, + (int) (word >> 24) & 07777, + (int) (word >> 12) & 07777, + (int) word & 07777); + } else { + fprintf (of, "с %04o %04o %04o %04o", + (int) (word >> 36) & 07777, + (int) (word >> 24) & 07777, + (int) (word >> 12) & 07777, + (int) word & 07777); + fprintf (of, "\t\t; %05o\n", addr); + } + } + return SCPE_OK; +} + +/* + * Loader/dumper + */ +t_stat sim_load (FILE *fi, char *cptr, char *fnam, int dump_flag) +{ + if (dump_flag) + return besm6_dump (fi, fnam); + + return besm6_load (fi); +} diff --git a/BESM6/besm6_tty.c b/BESM6/besm6_tty.c index 489de9b0..9d37e23f 100644 --- a/BESM6/besm6_tty.c +++ b/BESM6/besm6_tty.c @@ -1,1243 +1,1243 @@ -/* - * besm6_tty.c: BESM-6 teletype device - * - * Copyright (c) 2009, Leo Broukhis - * Copyright (c) 2009, Serge Vakulenko - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. - * - * You can redistribute this program and/or modify it under the terms of - * the GNU General Public License as published by the Free Software Foundation; - * either version 2 of the License, or (at your discretion) any later version. - * See the accompanying file "COPYING" for more details. - */ - -#include "besm6_defs.h" -#include "sim_sock.h" -#include "sim_tmxr.h" -#include - -#define TTY_MAX 24 /* Количество последовательных терминалов */ -#define LINES_MAX TTY_MAX + 2 /* Включая параллельные интерфейсы "Консулов" */ -/* - * Согласно таблице в http://ru.wikipedia.org/wiki/МТК-2 - */ -char * rus[] = { 0, "Т", "\r", "О", " ", "Х", "Н", "М", "\n", "Л", "Р", "Г", "И", "П", "Ц", "Ж", - "Е", "З", "Д", "Б", "С", "Ы", "Ф", "Ь", "А", "В", "Й", 0, "У", "Я", "К", 0 }; - -char * lat[] = { 0, "T", "\r", "O", " ", "H", "N", "M", "\n", "L", "R", "G", "I", "P", "C", "V", - "E", "Z", "D", "B", "S", "Y", "F", "X", "A", "W", "J", 0, "U", "Q", "K", 0 }; - -/* $ = Кто там? */ -char * dig[] = { 0, "5", "\r", "9", " ", "Щ", ",", ".", "\n", ")", "4", "Ш", "8", "0", ":", "=", - "3", "+", "$", "?", "'", "6", "Э", "/", "-", "2", "Ю", 0, "7", "1", "(", 0 }; - -char ** reg = 0; - -char * process (int sym) -{ - /* Требуется инверсия */ - sym ^= 31; - switch (sym) { - case 0: - reg = rus; - break; - case 27: - reg = dig; - break; - case 31: - reg = lat; - break; - default: - return reg[sym]; - } - return ""; -} - -/* Только для последовательных линий */ -int tty_active [TTY_MAX+1], tty_sym [TTY_MAX+1]; -int tty_typed [TTY_MAX+1], tty_instate [TTY_MAX+1]; -time_t tty_last_time [TTY_MAX+1]; -int tty_idle_count [TTY_MAX+1]; - -uint32 vt_sending, vt_receiving; -uint32 tt_sending, tt_receiving; - -// Attachments survive the reset -uint32 tt_mask = 0, vt_mask = 0; - -uint32 TTY_OUT = 0, TTY_IN = 0, vt_idle = 0; -uint32 CONSUL_IN[2]; - -uint32 CONS_CAN_PRINT[2] = { 01000, 00400 }; -uint32 CONS_HAS_INPUT[2] = { 04000, 02000 }; - -/* Буфера командных строк для режима telnet. */ -char vt_cbuf [CBUFSIZE] [LINES_MAX+1]; -char *vt_cptr [LINES_MAX+1]; - -void tt_print(); -void consul_receive(); -t_stat vt_clk(UNIT *); -extern char *get_sim_sw (char *cptr); - -UNIT tty_unit [] = { - { UDATA (vt_clk, UNIT_DIS, 0) }, /* fake unit, clock */ - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - /* The next two units are parallel interface */ - { UDATA (NULL, UNIT_SEQ, 0) }, - { UDATA (NULL, UNIT_SEQ, 0) }, - { 0 } -}; - -REG tty_reg[] = { - { 0 } -}; - -/* - * Дескрипторы линий для мультиплексора TMXR. - * Поле .conn содержит номер сокета и означает занятую линию. - * Для локальных терминалов делаем .conn = 1. - * Чтобы нумерация линий совпадала с нумерацией терминалов - * (с единицы), нулевую линию держим занятой (.conn = 1). - * Поле .rcve устанавливается в 1 для сетевых соединений. - * Для локальных терминалов оно равно 0. - */ -TMLN tty_line [LINES_MAX+1]; -TMXR tty_desc = { LINES_MAX+1, 0, 0, tty_line }; /* mux descriptor */ - -#define TTY_UNICODE_CHARSET 0 -#define TTY_KOI7_JCUKEN_CHARSET (1<>= 1)) { - tt_print(); - /* Прием не реализован */ - clk_divider = 1<<29; - } - - /* Есть новые сетевые подключения? */ - int num = tmxr_poll_conn (&tty_desc); - if (num > 0 && num <= LINES_MAX) { - char buf [80]; - TMLN *t = &tty_line [num]; - besm6_debug ("*** tty%d: новое подключение от %s", - num, t->ipad); - t->rcve = 1; - tty_unit[num].flags &= ~TTY_STATE_MASK; - tty_unit[num].flags |= TTY_VT340_STATE; - if (num <= TTY_MAX) - vt_mask |= 1 << (TTY_MAX - num); - - switch (tty_unit[num].flags & TTY_CHARSET_MASK) { - case TTY_KOI7_JCUKEN_CHARSET: - tmxr_linemsg (t, "Encoding is KOI-7 (jcuken)\r\n"); - break; - case TTY_KOI7_QWERTY_CHARSET: - tmxr_linemsg (t, "Encoding is KOI-7 (qwerty)\r\n"); - break; - case TTY_UNICODE_CHARSET: - tmxr_linemsg (t, "Encoding is UTF-8\r\n"); - break; - } - tty_idle_count[num] = 0; - tty_last_time[num] = time (0); - sprintf (buf, "%.24s from %s\r\n", - ctime (&tty_last_time[num]), - t->ipad); - tmxr_linemsg (t, buf); - - /* Ввод ^C, чтобы получить приглашение. */ - t->rxb [t->rxbpi++] = '\3'; - } - - /* Опрашиваем сокеты на передачу. */ - tmxr_poll_tx (&tty_desc); - - return sim_activate (this, 1000*MSEC/300); -} - -t_stat tty_setmode (UNIT *u, int32 val, char *cptr, void *desc) -{ - int num = u - tty_unit; - TMLN *t = &tty_line [num]; - uint32 mask = 1 << (TTY_MAX - num); - - switch (val & TTY_STATE_MASK) { - case TTY_OFFLINE_STATE: - if (t->conn) { - if (t->rcve) { - tmxr_reset_ln (t); - t->rcve = 0; - } else - t->conn = 0; - if (num <= TTY_MAX) { - tty_sym[num] = - tty_active[num] = - tty_typed[num] = - tty_instate[num] = 0; - vt_mask &= ~mask; - tt_mask &= ~mask; - } - } - break; - case TTY_TELETYPE_STATE: - if (num > TTY_MAX) - return SCPE_NXPAR; - t->conn = 1; - t->rcve = 0; - tt_mask |= mask; - vt_mask &= ~mask; - break; - case TTY_VT340_STATE: - t->conn = 1; - t->rcve = 0; - if (num <= TTY_MAX) { - vt_mask |= mask; - tt_mask &= ~mask; - } - break; - case TTY_CONSUL_STATE: - if (num <= TTY_MAX) - return SCPE_NXPAR; - t->conn = 1; - t->rcve = 0; - break; - } - return SCPE_OK; -} - -/* - * Разрешение подключения к терминалам через telnet. - * Делается командой: - * attach tty <порт> - * Здесь <порт> - номер порта telnet, например 4199. - */ -t_stat tty_attach (UNIT *u, char *cptr) -{ - int num = u - tty_unit; - int r, m, n; - - if (*cptr >= '0' && *cptr <= '9') { - /* Сохраняем и восстанавливаем все .conn, - * так как tmxr_attach() их обнуляет. */ - for (m=0, n=1; n<=LINES_MAX; ++n) - if (tty_line[n].conn) - m |= 1 << (LINES_MAX-n); - /* Неважно, какой номер порта указывать в команде задания - * порта telnet. Можно tty, можно tty1 - без разницы. */ - r = tmxr_attach (&tty_desc, &tty_unit[0], cptr); - for (n=1; n<=LINES_MAX; ++n) - if (m >> (LINES_MAX-n) & 1) - tty_line[n].conn = 1; - return r; - } - if (strcmp (cptr, "/dev/tty") == 0) { - /* Консоль. */ - u->flags &= ~TTY_STATE_MASK; - u->flags |= TTY_VT340_STATE; - tty_line[num].conn = 1; - tty_line[num].rcve = 0; - if (num <= TTY_MAX) - vt_mask |= 1 << (TTY_MAX - num); - besm6_debug ("*** консоль на T%03o", num); - return 0; - } - if (strcmp (cptr, "/dev/null") == 0) { - /* Запрещаем терминал. */ - tty_line[num].conn = 1; - tty_line[num].rcve = 0; - if (num <= TTY_MAX) { - vt_mask &= ~(1 << (TTY_MAX - num)); - tt_mask &= ~(1 << (TTY_MAX - num)); - } - besm6_debug ("*** отключение терминала T%03o", num); - return 0; - } - return SCPE_ALATT; -} - -t_stat tty_detach (UNIT *u) -{ - return tmxr_detach (&tty_desc, &tty_unit[0]); -} - -/* - * Управление терминалами. - * set ttyN unicode - выбор кодировки UTF-8 - * set ttyN jcuken - выбор кодировки КОИ-7, раскладка йцукен - * set ttyN qwerty - выбор кодировки КОИ-7, раскладка яверты - * set ttyN off - отключение - * set ttyN tt - установка типа терминала "Телетайп" - * set ttyN vt - установка типа терминала "Видеотон-340" - * set ttyN consul - установка типа терминала "Consul-254" - * set ttyN destrbs - "стирающий" backspace - * set ttyN authbs - классический backspace - * set tty disconnect=N - принудительное завершение сеанса telnet - * show tty - просмотр режимов терминалов - * show tty connections - просмотр IP-адресов и времени соединений - * show tty statistics - просмотр счетчиков переданных и принятых байтов - */ -MTAB tty_mod[] = { - { TTY_CHARSET_MASK, TTY_UNICODE_CHARSET, "UTF-8 input", - "UNICODE" }, - { TTY_CHARSET_MASK, TTY_KOI7_JCUKEN_CHARSET, "KOI7 (jcuken) input", - "JCUKEN" }, - { TTY_CHARSET_MASK, TTY_KOI7_QWERTY_CHARSET, "KOI7 (qwerty) input", - "QWERTY" }, - { TTY_STATE_MASK, TTY_OFFLINE_STATE, "offline", - "OFF", &tty_setmode }, - { TTY_STATE_MASK, TTY_TELETYPE_STATE, "Teletype", - "TT", &tty_setmode }, - { TTY_STATE_MASK, TTY_VT340_STATE, "Videoton-340", - "VT", &tty_setmode }, - { TTY_STATE_MASK, TTY_CONSUL_STATE, "Consul-254", - "CONSUL", &tty_setmode }, - { TTY_BSPACE_MASK, TTY_DESTRUCTIVE_BSPACE, "destructive backspace", - "DESTRBS" }, - { TTY_BSPACE_MASK, TTY_AUTHENTIC_BSPACE, NULL, - "AUTHBS" }, - { MTAB_XTD | MTAB_VDV, 1, NULL, - "DISCONNECT", &tmxr_dscln, NULL, (void*) &tty_desc }, - { UNIT_ATT, UNIT_ATT, "connections", - NULL, NULL, &tmxr_show_summ, (void*) &tty_desc }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", - NULL, NULL, &tmxr_show_cstat, (void*) &tty_desc }, - { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", - NULL, NULL, &tmxr_show_cstat, (void*) &tty_desc }, - { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, - "LOG", &tmxr_set_log, &tmxr_show_log, (void*) &tty_desc }, - { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, - "NOLOG", &tmxr_set_nolog, NULL, (void*) &tty_desc }, - { 0 } -}; - -DEVICE tty_dev = { - "TTY", tty_unit, tty_reg, tty_mod, - 27, 2, 1, 1, 2, 1, - NULL, NULL, &tty_reset, NULL, &tty_attach, &tty_detach, - NULL, DEV_NET|DEV_DEBUG -}; - -void tty_send (uint32 mask) -{ - /* besm6_debug ("*** телетайпы: передача %08o", mask); */ - - TTY_OUT = mask; -} - -/* - * Выдача символа на терминал с указанным номером. - */ -void vt_putc (int num, int c) -{ - TMLN *t = &tty_line [num]; - - if (! t->conn) - return; - if (t->rcve) { - /* Передача через telnet. */ - tmxr_putc_ln (t, c); - } else { - /* Вывод на консоль. */ - if (t->txlog) { /* log if available */ - fputc (c, t->txlog); - if (c == '\n') - fflush (t->txlog); - } - fputc (c, stdout); - fflush (stdout); - } -} - -/* - * Выдача строки на терминал с указанным номером. - */ -void vt_puts (int num, const char *s) -{ - TMLN *t = &tty_line [num]; - - if (! t->conn) - return; - if (t->rcve) { - /* Передача через telnet. */ - tmxr_linemsg (t, (char*) s); - } else { - /* Вывод на консоль. */ - if (t->txlog) /* log if available */ - fputs (s, t->txlog); - fputs (s, stdout); - fflush (stdout); - } -} - -const char * koi7_rus_to_unicode [32] = { - "Ю", "А", "Б", "Ц", "Д", "Е", "Ф", "Г", - "Х", "И", "Й", "К", "Л", "М", "Н", "О", - "П", "Я", "Р", "С", "Т", "У", "Ж", "В", - "Ь", "Ы", "З", "Ш", "Э", "Щ", "Ч", "\0x7f", -}; - -void vt_send(int num, uint32 sym, int destructive_bs) -{ - if (sym < 0x60) { - switch (sym) { - case '\031': - /* Up */ - vt_puts (num, "\033["); - sym = 'A'; - break; - case '\032': - /* Down */ - vt_puts (num, "\033["); - sym = 'B'; - break; - case '\030': - /* Right */ - vt_puts (num, "\033["); - sym = 'C'; - break; - case '\b': - /* Left */ - vt_puts (num, "\033["); - if (destructive_bs) { - /* Стираем предыдущий символ. */ - vt_puts (num, "D \033["); - } - sym = 'D'; - break; - case '\v': - case '\033': - case '\0': - /* Выдаём управляющий символ. */ - break; - case '\037': - /* Очистка экрана */ - vt_puts (num, "\033[H\033["); - sym = 'J'; - break; - case '\n': - /* На VDT-340 также возвращал курсор в 1-ю позицию */ - vt_putc (num, '\r'); - sym = '\n'; - break; - case '\f': - /* Сообщение ERR при нажатии некоторых управляющих - * клавиш выдается с использованием reverse wraparound. - */ - vt_puts(num, "\033["); - sym = 'H'; - break; - case '\r': - case '\003': - /* Неотображаемые символы */ - sym = 0; - break; - default: - if (sym < ' ') { - /* Нефункциональные ctrl-символы были видны в половинной яркости */ - vt_puts (num, "\033[2m"); - vt_putc (num, sym | 0x40); - vt_puts (num, "\033["); - /* Завершаем ESC-последовательность */ - sym = 'm'; - } - } - if (sym) - vt_putc (num, sym); - } else - vt_puts (num, koi7_rus_to_unicode[sym - 0x60]); -} - -/* - * Обработка выдачи на все подключенные терминалы. - */ -void vt_print() -{ - uint32 workset = (TTY_OUT & vt_mask) | vt_sending; - int num; - - if (workset == 0) { - ++vt_idle; - return; - } - for (num = besm6_highest_bit (workset) - TTY_MAX; - workset; num = besm6_highest_bit (workset) - TTY_MAX) { - int mask = 1 << (TTY_MAX - num); - int c = (TTY_OUT & mask) != 0; - switch (tty_active[num]*2+c) { - case 0: /* idle */ - besm6_debug ("Warning: inactive ttys should have been screened"); - continue; - case 1: /* start bit */ - vt_sending |= mask; - tty_active[num] = 1; - break; - case 18: /* stop bit */ - tty_sym[num] = ~tty_sym[num] & 0x7f; - vt_send (num, tty_sym[num], - (tty_unit[num].flags & TTY_BSPACE_MASK) == TTY_DESTRUCTIVE_BSPACE); - tty_active[num] = 0; - tty_sym[num] = 0; - vt_sending &= ~mask; - break; - case 19: /* framing error */ - vt_putc (num, '#'); - break; - default: - /* little endian ordering */ - if (c) { - tty_sym[num] |= 1 << (tty_active[num]-1); - } - ++tty_active[num]; - break; - } - workset &= ~mask; - } - vt_idle = 0; -} - -/* Ввод с телетайпа не реализован; вывод работает только при использовании - * модельного времени. - */ -void tt_print() -{ - uint32 workset = (TTY_OUT & tt_mask) | tt_sending; - int num; - - if (workset == 0) { - return; - } - - for (num = besm6_highest_bit (workset) - TTY_MAX; - workset; num = besm6_highest_bit (workset) - TTY_MAX) { - int mask = 1 << (TTY_MAX - num); - int c = (TTY_OUT & mask) != 0; - switch (tty_active[num]*2+c) { - case 0: /* idle */ - break; - case 1: /* start bit */ - tt_sending |= mask; - tty_active[num] = 1; - break; - case 12: /* stop bit */ - vt_puts (num, process (tty_sym[num])); - tty_active[num] = 0; - tty_sym[num] = 0; - tt_sending &= ~mask; - break; - case 13: /* framing error */ - vt_putc (num, '#'); - break; - default: - /* big endian ordering */ - if (c) { - tty_sym[num] |= 1 << (5-tty_active[num]); - } - ++tty_active[num]; - break; - } - workset &= ~mask; - } - vt_idle = 0; -} - -/* - * Перекодировка из Unicode в КОИ-7. - * Если нет соответствия, возвращает -1. - */ -static int unicode_to_koi7 (unsigned val) -{ - switch (val) { - case '\0'... '_': return val; - case 'a' ... 'z': return val + 'Z' - 'z'; - case 0x007f: return 0x7f; - case 0x0410: case 0x0430: return 0x61; - case 0x0411: case 0x0431: return 0x62; - case 0x0412: case 0x0432: return 0x77; - case 0x0413: case 0x0433: return 0x67; - case 0x0414: case 0x0434: return 0x64; - case 0x0415: case 0x0435: return 0x65; - case 0x0416: case 0x0436: return 0x76; - case 0x0417: case 0x0437: return 0x7a; - case 0x0418: case 0x0438: return 0x69; - case 0x0419: case 0x0439: return 0x6a; - case 0x041a: case 0x043a: return 0x6b; - case 0x041b: case 0x043b: return 0x6c; - case 0x041c: case 0x043c: return 0x6d; - case 0x041d: case 0x043d: return 0x6e; - case 0x041e: case 0x043e: return 0x6f; - case 0x041f: case 0x043f: return 0x70; - case 0x0420: case 0x0440: return 0x72; - case 0x0421: case 0x0441: return 0x73; - case 0x0422: case 0x0442: return 0x74; - case 0x0423: case 0x0443: return 0x75; - case 0x0424: case 0x0444: return 0x66; - case 0x0425: case 0x0445: return 0x68; - case 0x0426: case 0x0446: return 0x63; - case 0x0427: case 0x0447: return 0x7e; - case 0x0428: case 0x0448: return 0x7b; - case 0x0429: case 0x0449: return 0x7d; - case 0x042b: case 0x044b: return 0x79; - case 0x042c: case 0x044c: return 0x78; - case 0x042d: case 0x044d: return 0x7c; - case 0x042e: case 0x044e: return 0x60; - case 0x042f: case 0x044f: return 0x71; - } - return -1; -} - -/* - * Set command - */ -static t_stat cmd_set (int32 num, char *cptr) -{ - char gbuf [CBUFSIZE]; - int len; - - cptr = get_sim_sw (cptr); - if (! cptr) - return SCPE_INVSW; - if (! *cptr) - return SCPE_NOPARAM; - cptr = get_glyph (cptr, gbuf, 0); - if (*cptr) - return SCPE_2MARG; - - len = strlen (gbuf); - if (strncmp ("UNICODE", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_CHARSET_MASK; - tty_unit[num].flags |= TTY_UNICODE_CHARSET; - } else if (strncmp ("JCUKEN", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_CHARSET_MASK; - tty_unit[num].flags |= TTY_KOI7_JCUKEN_CHARSET; - } else if (strncmp ("QWERTY", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_CHARSET_MASK; - tty_unit[num].flags |= TTY_KOI7_QWERTY_CHARSET; - } else if (strncmp ("TT", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_STATE_MASK; - tty_unit[num].flags |= TTY_TELETYPE_STATE; - } else if (strncmp ("VT", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_STATE_MASK; - tty_unit[num].flags |= TTY_VT340_STATE; - } else if (strncmp ("CONSUL", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_STATE_MASK; - tty_unit[num].flags |= TTY_CONSUL_STATE; - } else if (strncmp ("DESTRBS", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_BSPACE_MASK; - tty_unit[num].flags |= TTY_DESTRUCTIVE_BSPACE; - } else if (strncmp ("AUTHBS", gbuf, len) == 0) { - tty_unit[num].flags &= ~TTY_BSPACE_MASK; - tty_unit[num].flags |= TTY_AUTHENTIC_BSPACE; - } else { - return SCPE_NXPAR; - } - return SCPE_OK; -} - -/* - * Show command - */ -static t_stat cmd_show (int32 num, char *cptr) -{ - TMLN *t = &tty_line [num]; - char gbuf [CBUFSIZE]; - MTAB *m; - int len; - - cptr = get_sim_sw (cptr); - if (! cptr) - return SCPE_INVSW; - if (! *cptr) { - sprintf (gbuf, "TTY%d", num); - tmxr_linemsg (t, gbuf); - for (m=tty_mod; m->mask; m++) { - if (m->pstring && - (tty_unit[num].flags & m->mask) == m->match) { - tmxr_linemsg (t, ", "); - tmxr_linemsg (t, m->pstring); - } - } - if (t->txlog) - tmxr_linemsg (t, ", log"); - tmxr_linemsg (t, "\r\n"); - return SCPE_OK; - } - cptr = get_glyph (cptr, gbuf, 0); - if (*cptr) - return SCPE_2MARG; - - len = strlen (gbuf); - if (strncmp ("STATISTICS", gbuf, len) == 0) { - sprintf (gbuf, "line %d: input queued/total = %d/%d, " - "output queued/total = %d/%d\r\n", num, - t->rxbpi - t->rxbpr, t->rxcnt, - t->txbpi - t->txbpr, t->txcnt); - tmxr_linemsg (t, gbuf); - } else { - return SCPE_NXPAR; - } - return SCPE_OK; -} - -/* - * Exit command - */ -static t_stat cmd_exit (int32 num, char *cptr) -{ - return SCPE_EXIT; -} - -static t_stat cmd_help (int32 num, char *cptr); - -static CTAB cmd_table[] = { - { "SET", &cmd_set, 0, - "set unicode select UTF-8 encoding\r\n" - "set jcuken select KOI7 encoding, 'jcuken' keymap\r\n" - "set qwerty select KOI7 encoding, 'qwerty' keymap\r\n" - "set tt use Teletype mode\r\n" - "set vt use Videoton-340 mode\r\n" - "set consul use Consul-254 mode\r\n" - "set destrbs destructive backspace\r\n" - "set authbs authentic backspace\r\n" - }, - { "SHOW", &cmd_show, 0, - "sh{ow} show modes of the terminal\r\n" - "sh{ow} s{tatistics} show network statistics\r\n" - }, - { "EXIT", &cmd_exit, 0, - "exi{t} | q{uit} | by{e} exit from simulation\r\n" - }, - { "QUIT", &cmd_exit, 0, NULL - }, - { "BYE", &cmd_exit, 0, NULL - }, - { "HELP", &cmd_help, 0, - "h{elp} type this message\r\n" - "h{elp} type help for command\r\n" - }, - { 0 } -}; - -/* - * Find command routine - */ -static CTAB *lookup_cmd (char *command) -{ - CTAB *c; - int len; - - len = strlen (command); - for (c=cmd_table; c->name; c++) { - if (strncmp (command, c->name, len) == 0) - return c; - } - return 0; -} - -/* - * Help command - */ -static t_stat cmd_help (int32 num, char *cptr) -{ - TMLN *t = &tty_line [num]; - char gbuf [CBUFSIZE]; - CTAB *c; - - cptr = get_sim_sw (cptr); - if (! cptr) - return SCPE_INVSW; - if (! *cptr) { - /* Список всех команд. */ - tmxr_linemsg (t, "Commands may be abbreviated. Commands are:\r\n\r\n"); - for (c=cmd_table; c && c->name; c++) - if (c->help) - tmxr_linemsg (t, c->help); - return SCPE_OK; - } - cptr = get_glyph (cptr, gbuf, 0); - if (*cptr) - return SCPE_2MARG; - c = lookup_cmd (gbuf); - if (! c) - return SCPE_ARG; - /* Описание конкретной команды. */ - tmxr_linemsg (t, c->help); - return SCPE_OK; -} - -/* - * Выполнение командной строки. - */ -void vt_cmd_exec (int num) -{ - TMLN *t = &tty_line [num]; - char *cptr, gbuf [CBUFSIZE]; - CTAB *cmdp; - t_stat err; - extern char *scp_errors[]; - - cptr = get_glyph (vt_cbuf [num], gbuf, 0); /* get command glyph */ - cmdp = lookup_cmd (gbuf); /* lookup command */ - if (! cmdp) { - tmxr_linemsg (t, scp_errors[SCPE_UNK - SCPE_BASE]); - tmxr_linemsg (t, "\r\n"); - return; - } - err = cmdp->action (num, cptr); /* if found, exec */ - if (err >= SCPE_BASE) { /* error? */ - tmxr_linemsg (t, scp_errors [err - SCPE_BASE]); - tmxr_linemsg (t, "\r\n"); - } - if (err == SCPE_EXIT) { /* close telnet session */ - tmxr_reset_ln (t); - } -} - -/* - * Режим управляющей командной строки. - */ -void vt_cmd_loop (int num, int c) -{ - TMLN *t = &tty_line [num]; - char *cbuf, **cptr; - - cbuf = vt_cbuf [num]; - cptr = &vt_cptr [num]; - - switch (c) { - case '\r': - case '\n': - tmxr_linemsg (t, "\r\n"); - if (*cptr <= cbuf) { - /* Пустая строка - возврат в обычный режим. */ - tty_unit[num].flags &= ~TTY_CMDLINE_MASK; - break; - } - /* Выполнение. */ - **cptr = 0; - vt_cmd_exec (num); - tmxr_linemsg (t, "sim>"); - *cptr = vt_cbuf[num]; - break; - case '\b': - case 0177: - /* Стирание предыдущего символа. */ - if (*cptr <= cbuf) - break; - tmxr_linemsg (t, "\b \b"); - while (*cptr > cbuf) { - --*cptr; - if (! (**cptr & 0x80)) - break; - } - break; - case 'U' & 037: - /* Стирание всей строки. */ -erase_line: while (*cptr > cbuf) { - --*cptr; - if (! (**cptr & 0x80)) - tmxr_linemsg (t, "\b \b"); - } - break; - case 033: - /* Escape [ X. */ - if (tmxr_getc_ln (t) != '[' + TMXR_VALID) - break; - switch (tmxr_getc_ln (t) - TMXR_VALID) { - case 'A': /* стрелка вверх */ - if (*cptr <= cbuf) { - *cptr = cbuf + strlen (cbuf); - if (*cptr > cbuf) - tmxr_linemsg (t, cbuf); - } - break; - case 'B': /* стрелка вниз */ - goto erase_line; - } - break; - default: - if (c < ' ' || *cptr > cbuf+CBUFSIZE-5) - break; - *(*cptr)++ = c; - tmxr_putc_ln (t, c); - break; - } -} - -/* - * Ввод символа с терминала с указанным номером. - * Если нет приёма, возвращает -1. - * В случае прерывания возвращает 0400 (только для консоли). - */ -int vt_getc (int num) -{ - TMLN *t = &tty_line [num]; - extern int32 sim_int_char; - int c; - time_t now; - - if (! t->conn) { - /* Пользователь отключился. */ - if (t->ipad) { - besm6_debug ("*** tty%d: отключение %s", - num, - t->ipad); - t->ipad = NULL; - } - tty_setmode (tty_unit+num, TTY_OFFLINE_STATE, 0, 0); - tty_unit[num].flags &= ~TTY_STATE_MASK; - return -1; - } - if (t->rcve) { - /* Приём через telnet. */ - c = tmxr_getc_ln (t); - if (! (c & TMXR_VALID)) { - now = time (0); - if (now > tty_last_time[num] + 5*60) { - ++tty_idle_count[num]; - if (tty_idle_count[num] > 3) { - tmxr_linemsg (t, "\r\nКОНЕЦ СЕАНСА\r\n"); - tmxr_reset_ln (t); - return -1; - } - tmxr_linemsg (t, "\r\nНЕ СПАТЬ!\r\n"); - tty_last_time[num] = now; - } - return -1; - } - tty_idle_count[num] = 0; - tty_last_time[num] = time (0); - - if (tty_unit[num].flags & TTY_CMDLINE_MASK) { - /* Продолжение режима управляющей командной строки. */ - vt_cmd_loop (num, c & 0377); - return -1; - } - if ((c & 0377) == sim_int_char) { - /* Вход в режим управляющей командной строки. */ - tty_unit[num].flags |= TTY_CMDLINE_MASK; - tmxr_linemsg (t, "sim>"); - vt_cptr[num] = vt_cbuf[num]; - return -1; - } - } else { - /* Ввод с клавиатуры. */ - c = sim_poll_kbd(); - if (c == SCPE_STOP) - return 0400; /* прерывание */ - if (! (c & SCPE_KFLAG)) - return -1; - } - return c & 0377; -} - -/* - * Ввод символа с клавиатуры. - * Перекодировка из UTF-8 в КОИ-7. - * Полученный символ находится в диапазоне 0..0177. - * Если нет ввода, возвращает -1. - * В случае прерывания (^E) возвращает 0400. - */ -static int vt_kbd_input_unicode (int num) -{ - int c1, c2, c3, r; -again: - r = vt_getc (num); - if (r < 0 || r > 0377) - return r; - c1 = r & 0377; - if (! (c1 & 0x80)) - return unicode_to_koi7 (c1); - - r = vt_getc (num); - if (r < 0 || r > 0377) - return r; - c2 = r & 0377; - if (! (c1 & 0x20)) - return unicode_to_koi7 ((c1 & 0x1f) << 6 | (c2 & 0x3f)); - - r = vt_getc (num); - if (r < 0 || r > 0377) - return r; - c3 = r & 0377; - if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { - /* Skip zero width no-break space. */ - goto again; - } - return unicode_to_koi7 ((c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | - (c3 & 0x3f)); -} - -/* - * Альтернативный вариант ввода, не требующий переключения на русскую клавиатуру. - * Символы "точка" и "запятая" вводятся через shift, "больше-меньше" - "тильда-гравис". - * "точка с запятой" - "закр. фиг. скобка", "апостроф" - "верт. черта". - */ -static int vt_kbd_input_koi7 (int num) -{ - int r; - - r = vt_getc (num); - if (r < 0 || r > 0377) - return r; - r &= 0377; - switch (r) { - case '\r': return '\003'; - case 'q': return 'j'; - case 'w': return 'c'; - case 'e': return 'u'; - case 'r': return 'k'; - case 't': return 'e'; - case 'y': return 'n'; - case 'u': return 'g'; - case 'i': return '{'; - case 'o': return '}'; - case 'p': return 'z'; - case '[': return 'h'; - case '{': return '['; - case 'a': return 'f'; - case 's': return 'y'; - case 'd': return 'w'; - case 'f': return 'a'; - case 'g': return 'p'; - case 'h': return 'r'; - case 'j': return 'o'; - case 'k': return 'l'; - case 'l': return 'd'; - case ';': return 'v'; - case '}': return ';'; - case '\'': return '|'; - case '|': return '\''; - case 'z': return 'q'; - case 'x': return '~'; - case 'c': return 's'; - case 'v': return 'm'; - case 'b': return 'i'; - case 'n': return 't'; - case 'm': return 'x'; - case ',': return 'b'; - case '<': return ','; - case '.': return '`'; - case '>': return '.'; - case '~': return '>'; - case '`': return '<'; - default: return r; - } -} - -int odd_parity(unsigned char c) -{ - c = (c & 0x55) + ((c >> 1) & 0x55); - c = (c & 0x33) + ((c >> 2) & 0x33); - c = (c & 0x0F) + ((c >> 4) & 0x0F); - return c & 1; -} - -/* - * Обработка ввода со всех подключенных терминалов. - */ -void vt_receive() -{ - uint32 workset = vt_mask; - int num; - - TTY_IN = 0; - for (num = besm6_highest_bit (workset) - TTY_MAX; - workset; num = besm6_highest_bit (workset) - TTY_MAX) { - uint32 mask = 1 << (TTY_MAX - num); - switch (tty_instate[num]) { - case 0: - switch (tty_unit[num].flags & TTY_CHARSET_MASK) { - case TTY_KOI7_JCUKEN_CHARSET: - tty_typed[num] = vt_kbd_input_koi7 (num); - break; - case TTY_KOI7_QWERTY_CHARSET: - tty_typed[num] = vt_getc (num); - break; - case TTY_UNICODE_CHARSET: - tty_typed[num] = vt_kbd_input_unicode (num); - break; - default: - tty_typed[num] = '?'; - break; - } - if (tty_typed[num] < 0) { - /* TODO: обработать исключение от "неоператорского" терминала */ - sim_interval = 0; - break; - } - if (tty_typed[num] <= 0177) { - if (tty_typed[num] == '\r' || tty_typed[num] == '\n') - tty_typed[num] = 3; /* ^C - конец строки */ - if (tty_typed[num] == '\177') - tty_typed[num] = '\b'; /* ASCII DEL -> BS */ - tty_instate[num] = 1; - TTY_IN |= mask; /* start bit */ - GRP |= GRP_TTY_START; /* не используется ? */ - MGRP |= BBIT(19); /* для терминалов по методу МГУ */ - vt_receiving |= mask; - } - break; - case 1 ... 7: - /* need inverted byte */ - TTY_IN |= (tty_typed[num] & (1 << (tty_instate[num]-1))) ? 0 : mask; - tty_instate[num]++; - break; - case 8: - TTY_IN |= odd_parity(tty_typed[num]) ? 0 : mask; /* even parity of inverted */ - tty_instate[num]++; - break; - case 9 ... 11: - /* stop bits are 0 */ - tty_instate[num]++; - break; - case 12: - tty_instate[num] = 0; /* ready for the next char */ - vt_receiving &= ~mask; - break; - } - workset &= ~mask; - } - if (vt_receiving) - vt_idle = 0; -} - -/* - * Выясняем, остановлены ли терминалы. Нужно для входа в "спящий" режим. - */ -int vt_is_idle () -{ - return (tt_mask ? vt_idle > 300 : vt_idle > 10); -} - -int tty_query () -{ -/* besm6_debug ("*** телетайпы: приём");*/ - return TTY_IN; -} - -void consul_print (int dev_num, uint32 cmd) -{ - int line_num = dev_num + TTY_MAX + 1; - if (tty_dev.dctrl) - besm6_debug(">>> CONSUL%o: %03o", line_num, cmd & 0377); - cmd &= 0177; - switch (tty_unit[line_num].flags & TTY_STATE_MASK) { - case TTY_VT340_STATE: - vt_send (line_num, cmd, - (tty_unit[line_num].flags & TTY_BSPACE_MASK) == TTY_DESTRUCTIVE_BSPACE); - break; - case TTY_CONSUL_STATE: - besm6_debug(">>> CONSUL%o: Native charset not implemented", line_num); - break; - } - // PRP |= CONS_CAN_PRINT[dev_num]; - vt_idle = 0; -} - -void consul_receive () -{ - int c, line_num, dev_num; - - for (dev_num = 0; dev_num < 2; ++dev_num){ - line_num = dev_num + TTY_MAX + 1; - if (! tty_line[line_num].conn) - continue; - switch (tty_unit[line_num].flags & TTY_CHARSET_MASK) { - case TTY_KOI7_JCUKEN_CHARSET: - c = vt_kbd_input_koi7 (line_num); - break; - case TTY_KOI7_QWERTY_CHARSET: - c = vt_getc (line_num); - break; - case TTY_UNICODE_CHARSET: - c = vt_kbd_input_unicode (line_num); - break; - default: - c = '?'; - break; - } - if (c >= 0 && c <= 0177) { - CONSUL_IN[dev_num] = odd_parity(c) ? c | 0200 : c; - if (c == '\r' || c == '\n') - CONSUL_IN[dev_num] = 3; - PRP |= CONS_HAS_INPUT[dev_num]; - vt_idle = 0; - } - } -} - -uint32 consul_read (int num) -{ - if (tty_dev.dctrl) - besm6_debug("<<< CONSUL%o: %03o", num+TTY_MAX+1, CONSUL_IN[num]); - return CONSUL_IN[num]; -} +/* + * besm6_tty.c: BESM-6 teletype device + * + * Copyright (c) 2009, Leo Broukhis + * Copyright (c) 2009, Serge Vakulenko + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You can redistribute this program and/or modify it under the terms of + * the GNU General Public License as published by the Free Software Foundation; + * either version 2 of the License, or (at your discretion) any later version. + * See the accompanying file "COPYING" for more details. + */ + +#include "besm6_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define TTY_MAX 24 /* Количество последовательных терминалов */ +#define LINES_MAX TTY_MAX + 2 /* Включая параллельные интерфейсы "Консулов" */ +/* + * Согласно таблице в http://ru.wikipedia.org/wiki/МТК-2 + */ +char * rus[] = { 0, "Т", "\r", "О", " ", "Х", "Н", "М", "\n", "Л", "Р", "Г", "И", "П", "Ц", "Ж", + "Е", "З", "Д", "Б", "С", "Ы", "Ф", "Ь", "А", "В", "Й", 0, "У", "Я", "К", 0 }; + +char * lat[] = { 0, "T", "\r", "O", " ", "H", "N", "M", "\n", "L", "R", "G", "I", "P", "C", "V", + "E", "Z", "D", "B", "S", "Y", "F", "X", "A", "W", "J", 0, "U", "Q", "K", 0 }; + +/* $ = Кто там? */ +char * dig[] = { 0, "5", "\r", "9", " ", "Щ", ",", ".", "\n", ")", "4", "Ш", "8", "0", ":", "=", + "3", "+", "$", "?", "'", "6", "Э", "/", "-", "2", "Ю", 0, "7", "1", "(", 0 }; + +char ** reg = 0; + +char * process (int sym) +{ + /* Требуется инверсия */ + sym ^= 31; + switch (sym) { + case 0: + reg = rus; + break; + case 27: + reg = dig; + break; + case 31: + reg = lat; + break; + default: + return reg[sym]; + } + return ""; +} + +/* Только для последовательных линий */ +int tty_active [TTY_MAX+1], tty_sym [TTY_MAX+1]; +int tty_typed [TTY_MAX+1], tty_instate [TTY_MAX+1]; +time_t tty_last_time [TTY_MAX+1]; +int tty_idle_count [TTY_MAX+1]; + +uint32 vt_sending, vt_receiving; +uint32 tt_sending, tt_receiving; + +// Attachments survive the reset +uint32 tt_mask = 0, vt_mask = 0; + +uint32 TTY_OUT = 0, TTY_IN = 0, vt_idle = 0; +uint32 CONSUL_IN[2]; + +uint32 CONS_CAN_PRINT[2] = { 01000, 00400 }; +uint32 CONS_HAS_INPUT[2] = { 04000, 02000 }; + +/* Буфера командных строк для режима telnet. */ +char vt_cbuf [CBUFSIZE] [LINES_MAX+1]; +char *vt_cptr [LINES_MAX+1]; + +void tt_print(); +void consul_receive(); +t_stat vt_clk(UNIT *); +extern char *get_sim_sw (char *cptr); + +UNIT tty_unit [] = { + { UDATA (vt_clk, UNIT_DIS, 0) }, /* fake unit, clock */ + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + /* The next two units are parallel interface */ + { UDATA (NULL, UNIT_SEQ, 0) }, + { UDATA (NULL, UNIT_SEQ, 0) }, + { 0 } +}; + +REG tty_reg[] = { + { 0 } +}; + +/* + * Дескрипторы линий для мультиплексора TMXR. + * Поле .conn содержит номер сокета и означает занятую линию. + * Для локальных терминалов делаем .conn = 1. + * Чтобы нумерация линий совпадала с нумерацией терминалов + * (с единицы), нулевую линию держим занятой (.conn = 1). + * Поле .rcve устанавливается в 1 для сетевых соединений. + * Для локальных терминалов оно равно 0. + */ +TMLN tty_line [LINES_MAX+1]; +TMXR tty_desc = { LINES_MAX+1, 0, 0, tty_line }; /* mux descriptor */ + +#define TTY_UNICODE_CHARSET 0 +#define TTY_KOI7_JCUKEN_CHARSET (1<>= 1)) { + tt_print(); + /* Прием не реализован */ + clk_divider = 1<<29; + } + + /* Есть новые сетевые подключения? */ + int num = tmxr_poll_conn (&tty_desc); + if (num > 0 && num <= LINES_MAX) { + char buf [80]; + TMLN *t = &tty_line [num]; + besm6_debug ("*** tty%d: новое подключение от %s", + num, t->ipad); + t->rcve = 1; + tty_unit[num].flags &= ~TTY_STATE_MASK; + tty_unit[num].flags |= TTY_VT340_STATE; + if (num <= TTY_MAX) + vt_mask |= 1 << (TTY_MAX - num); + + switch (tty_unit[num].flags & TTY_CHARSET_MASK) { + case TTY_KOI7_JCUKEN_CHARSET: + tmxr_linemsg (t, "Encoding is KOI-7 (jcuken)\r\n"); + break; + case TTY_KOI7_QWERTY_CHARSET: + tmxr_linemsg (t, "Encoding is KOI-7 (qwerty)\r\n"); + break; + case TTY_UNICODE_CHARSET: + tmxr_linemsg (t, "Encoding is UTF-8\r\n"); + break; + } + tty_idle_count[num] = 0; + tty_last_time[num] = time (0); + sprintf (buf, "%.24s from %s\r\n", + ctime (&tty_last_time[num]), + t->ipad); + tmxr_linemsg (t, buf); + + /* Ввод ^C, чтобы получить приглашение. */ + t->rxb [t->rxbpi++] = '\3'; + } + + /* Опрашиваем сокеты на передачу. */ + tmxr_poll_tx (&tty_desc); + + return sim_activate (this, 1000*MSEC/300); +} + +t_stat tty_setmode (UNIT *u, int32 val, char *cptr, void *desc) +{ + int num = u - tty_unit; + TMLN *t = &tty_line [num]; + uint32 mask = 1 << (TTY_MAX - num); + + switch (val & TTY_STATE_MASK) { + case TTY_OFFLINE_STATE: + if (t->conn) { + if (t->rcve) { + tmxr_reset_ln (t); + t->rcve = 0; + } else + t->conn = 0; + if (num <= TTY_MAX) { + tty_sym[num] = + tty_active[num] = + tty_typed[num] = + tty_instate[num] = 0; + vt_mask &= ~mask; + tt_mask &= ~mask; + } + } + break; + case TTY_TELETYPE_STATE: + if (num > TTY_MAX) + return SCPE_NXPAR; + t->conn = 1; + t->rcve = 0; + tt_mask |= mask; + vt_mask &= ~mask; + break; + case TTY_VT340_STATE: + t->conn = 1; + t->rcve = 0; + if (num <= TTY_MAX) { + vt_mask |= mask; + tt_mask &= ~mask; + } + break; + case TTY_CONSUL_STATE: + if (num <= TTY_MAX) + return SCPE_NXPAR; + t->conn = 1; + t->rcve = 0; + break; + } + return SCPE_OK; +} + +/* + * Разрешение подключения к терминалам через telnet. + * Делается командой: + * attach tty <порт> + * Здесь <порт> - номер порта telnet, например 4199. + */ +t_stat tty_attach (UNIT *u, char *cptr) +{ + int num = u - tty_unit; + int r, m, n; + + if (*cptr >= '0' && *cptr <= '9') { + /* Сохраняем и восстанавливаем все .conn, + * так как tmxr_attach() их обнуляет. */ + for (m=0, n=1; n<=LINES_MAX; ++n) + if (tty_line[n].conn) + m |= 1 << (LINES_MAX-n); + /* Неважно, какой номер порта указывать в команде задания + * порта telnet. Можно tty, можно tty1 - без разницы. */ + r = tmxr_attach (&tty_desc, &tty_unit[0], cptr); + for (n=1; n<=LINES_MAX; ++n) + if (m >> (LINES_MAX-n) & 1) + tty_line[n].conn = 1; + return r; + } + if (strcmp (cptr, "/dev/tty") == 0) { + /* Консоль. */ + u->flags &= ~TTY_STATE_MASK; + u->flags |= TTY_VT340_STATE; + tty_line[num].conn = 1; + tty_line[num].rcve = 0; + if (num <= TTY_MAX) + vt_mask |= 1 << (TTY_MAX - num); + besm6_debug ("*** консоль на T%03o", num); + return 0; + } + if (strcmp (cptr, "/dev/null") == 0) { + /* Запрещаем терминал. */ + tty_line[num].conn = 1; + tty_line[num].rcve = 0; + if (num <= TTY_MAX) { + vt_mask &= ~(1 << (TTY_MAX - num)); + tt_mask &= ~(1 << (TTY_MAX - num)); + } + besm6_debug ("*** отключение терминала T%03o", num); + return 0; + } + return SCPE_ALATT; +} + +t_stat tty_detach (UNIT *u) +{ + return tmxr_detach (&tty_desc, &tty_unit[0]); +} + +/* + * Управление терминалами. + * set ttyN unicode - выбор кодировки UTF-8 + * set ttyN jcuken - выбор кодировки КОИ-7, раскладка йцукен + * set ttyN qwerty - выбор кодировки КОИ-7, раскладка яверты + * set ttyN off - отключение + * set ttyN tt - установка типа терминала "Телетайп" + * set ttyN vt - установка типа терминала "Видеотон-340" + * set ttyN consul - установка типа терминала "Consul-254" + * set ttyN destrbs - "стирающий" backspace + * set ttyN authbs - классический backspace + * set tty disconnect=N - принудительное завершение сеанса telnet + * show tty - просмотр режимов терминалов + * show tty connections - просмотр IP-адресов и времени соединений + * show tty statistics - просмотр счетчиков переданных и принятых байтов + */ +MTAB tty_mod[] = { + { TTY_CHARSET_MASK, TTY_UNICODE_CHARSET, "UTF-8 input", + "UNICODE" }, + { TTY_CHARSET_MASK, TTY_KOI7_JCUKEN_CHARSET, "KOI7 (jcuken) input", + "JCUKEN" }, + { TTY_CHARSET_MASK, TTY_KOI7_QWERTY_CHARSET, "KOI7 (qwerty) input", + "QWERTY" }, + { TTY_STATE_MASK, TTY_OFFLINE_STATE, "offline", + "OFF", &tty_setmode }, + { TTY_STATE_MASK, TTY_TELETYPE_STATE, "Teletype", + "TT", &tty_setmode }, + { TTY_STATE_MASK, TTY_VT340_STATE, "Videoton-340", + "VT", &tty_setmode }, + { TTY_STATE_MASK, TTY_CONSUL_STATE, "Consul-254", + "CONSUL", &tty_setmode }, + { TTY_BSPACE_MASK, TTY_DESTRUCTIVE_BSPACE, "destructive backspace", + "DESTRBS" }, + { TTY_BSPACE_MASK, TTY_AUTHENTIC_BSPACE, NULL, + "AUTHBS" }, + { MTAB_XTD | MTAB_VDV, 1, NULL, + "DISCONNECT", &tmxr_dscln, NULL, (void*) &tty_desc }, + { UNIT_ATT, UNIT_ATT, "connections", + NULL, NULL, &tmxr_show_summ, (void*) &tty_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", + NULL, NULL, &tmxr_show_cstat, (void*) &tty_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", + NULL, NULL, &tmxr_show_cstat, (void*) &tty_desc }, + { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, + "LOG", &tmxr_set_log, &tmxr_show_log, (void*) &tty_desc }, + { MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, + "NOLOG", &tmxr_set_nolog, NULL, (void*) &tty_desc }, + { 0 } +}; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 27, 2, 1, 1, 2, 1, + NULL, NULL, &tty_reset, NULL, &tty_attach, &tty_detach, + NULL, DEV_NET|DEV_DEBUG +}; + +void tty_send (uint32 mask) +{ + /* besm6_debug ("*** телетайпы: передача %08o", mask); */ + + TTY_OUT = mask; +} + +/* + * Выдача символа на терминал с указанным номером. + */ +void vt_putc (int num, int c) +{ + TMLN *t = &tty_line [num]; + + if (! t->conn) + return; + if (t->rcve) { + /* Передача через telnet. */ + tmxr_putc_ln (t, c); + } else { + /* Вывод на консоль. */ + if (t->txlog) { /* log if available */ + fputc (c, t->txlog); + if (c == '\n') + fflush (t->txlog); + } + fputc (c, stdout); + fflush (stdout); + } +} + +/* + * Выдача строки на терминал с указанным номером. + */ +void vt_puts (int num, const char *s) +{ + TMLN *t = &tty_line [num]; + + if (! t->conn) + return; + if (t->rcve) { + /* Передача через telnet. */ + tmxr_linemsg (t, (char*) s); + } else { + /* Вывод на консоль. */ + if (t->txlog) /* log if available */ + fputs (s, t->txlog); + fputs (s, stdout); + fflush (stdout); + } +} + +const char * koi7_rus_to_unicode [32] = { + "Ю", "А", "Б", "Ц", "Д", "Е", "Ф", "Г", + "Х", "И", "Й", "К", "Л", "М", "Н", "О", + "П", "Я", "Р", "С", "Т", "У", "Ж", "В", + "Ь", "Ы", "З", "Ш", "Э", "Щ", "Ч", "\0x7f", +}; + +void vt_send(int num, uint32 sym, int destructive_bs) +{ + if (sym < 0x60) { + switch (sym) { + case '\031': + /* Up */ + vt_puts (num, "\033["); + sym = 'A'; + break; + case '\032': + /* Down */ + vt_puts (num, "\033["); + sym = 'B'; + break; + case '\030': + /* Right */ + vt_puts (num, "\033["); + sym = 'C'; + break; + case '\b': + /* Left */ + vt_puts (num, "\033["); + if (destructive_bs) { + /* Стираем предыдущий символ. */ + vt_puts (num, "D \033["); + } + sym = 'D'; + break; + case '\v': + case '\033': + case '\0': + /* Выдаём управляющий символ. */ + break; + case '\037': + /* Очистка экрана */ + vt_puts (num, "\033[H\033["); + sym = 'J'; + break; + case '\n': + /* На VDT-340 также возвращал курсор в 1-ю позицию */ + vt_putc (num, '\r'); + sym = '\n'; + break; + case '\f': + /* Сообщение ERR при нажатии некоторых управляющих + * клавиш выдается с использованием reverse wraparound. + */ + vt_puts(num, "\033["); + sym = 'H'; + break; + case '\r': + case '\003': + /* Неотображаемые символы */ + sym = 0; + break; + default: + if (sym < ' ') { + /* Нефункциональные ctrl-символы были видны в половинной яркости */ + vt_puts (num, "\033[2m"); + vt_putc (num, sym | 0x40); + vt_puts (num, "\033["); + /* Завершаем ESC-последовательность */ + sym = 'm'; + } + } + if (sym) + vt_putc (num, sym); + } else + vt_puts (num, koi7_rus_to_unicode[sym - 0x60]); +} + +/* + * Обработка выдачи на все подключенные терминалы. + */ +void vt_print() +{ + uint32 workset = (TTY_OUT & vt_mask) | vt_sending; + int num; + + if (workset == 0) { + ++vt_idle; + return; + } + for (num = besm6_highest_bit (workset) - TTY_MAX; + workset; num = besm6_highest_bit (workset) - TTY_MAX) { + int mask = 1 << (TTY_MAX - num); + int c = (TTY_OUT & mask) != 0; + switch (tty_active[num]*2+c) { + case 0: /* idle */ + besm6_debug ("Warning: inactive ttys should have been screened"); + continue; + case 1: /* start bit */ + vt_sending |= mask; + tty_active[num] = 1; + break; + case 18: /* stop bit */ + tty_sym[num] = ~tty_sym[num] & 0x7f; + vt_send (num, tty_sym[num], + (tty_unit[num].flags & TTY_BSPACE_MASK) == TTY_DESTRUCTIVE_BSPACE); + tty_active[num] = 0; + tty_sym[num] = 0; + vt_sending &= ~mask; + break; + case 19: /* framing error */ + vt_putc (num, '#'); + break; + default: + /* little endian ordering */ + if (c) { + tty_sym[num] |= 1 << (tty_active[num]-1); + } + ++tty_active[num]; + break; + } + workset &= ~mask; + } + vt_idle = 0; +} + +/* Ввод с телетайпа не реализован; вывод работает только при использовании + * модельного времени. + */ +void tt_print() +{ + uint32 workset = (TTY_OUT & tt_mask) | tt_sending; + int num; + + if (workset == 0) { + return; + } + + for (num = besm6_highest_bit (workset) - TTY_MAX; + workset; num = besm6_highest_bit (workset) - TTY_MAX) { + int mask = 1 << (TTY_MAX - num); + int c = (TTY_OUT & mask) != 0; + switch (tty_active[num]*2+c) { + case 0: /* idle */ + break; + case 1: /* start bit */ + tt_sending |= mask; + tty_active[num] = 1; + break; + case 12: /* stop bit */ + vt_puts (num, process (tty_sym[num])); + tty_active[num] = 0; + tty_sym[num] = 0; + tt_sending &= ~mask; + break; + case 13: /* framing error */ + vt_putc (num, '#'); + break; + default: + /* big endian ordering */ + if (c) { + tty_sym[num] |= 1 << (5-tty_active[num]); + } + ++tty_active[num]; + break; + } + workset &= ~mask; + } + vt_idle = 0; +} + +/* + * Перекодировка из Unicode в КОИ-7. + * Если нет соответствия, возвращает -1. + */ +static int unicode_to_koi7 (unsigned val) +{ + switch (val) { + case '\0'... '_': return val; + case 'a' ... 'z': return val + 'Z' - 'z'; + case 0x007f: return 0x7f; + case 0x0410: case 0x0430: return 0x61; + case 0x0411: case 0x0431: return 0x62; + case 0x0412: case 0x0432: return 0x77; + case 0x0413: case 0x0433: return 0x67; + case 0x0414: case 0x0434: return 0x64; + case 0x0415: case 0x0435: return 0x65; + case 0x0416: case 0x0436: return 0x76; + case 0x0417: case 0x0437: return 0x7a; + case 0x0418: case 0x0438: return 0x69; + case 0x0419: case 0x0439: return 0x6a; + case 0x041a: case 0x043a: return 0x6b; + case 0x041b: case 0x043b: return 0x6c; + case 0x041c: case 0x043c: return 0x6d; + case 0x041d: case 0x043d: return 0x6e; + case 0x041e: case 0x043e: return 0x6f; + case 0x041f: case 0x043f: return 0x70; + case 0x0420: case 0x0440: return 0x72; + case 0x0421: case 0x0441: return 0x73; + case 0x0422: case 0x0442: return 0x74; + case 0x0423: case 0x0443: return 0x75; + case 0x0424: case 0x0444: return 0x66; + case 0x0425: case 0x0445: return 0x68; + case 0x0426: case 0x0446: return 0x63; + case 0x0427: case 0x0447: return 0x7e; + case 0x0428: case 0x0448: return 0x7b; + case 0x0429: case 0x0449: return 0x7d; + case 0x042b: case 0x044b: return 0x79; + case 0x042c: case 0x044c: return 0x78; + case 0x042d: case 0x044d: return 0x7c; + case 0x042e: case 0x044e: return 0x60; + case 0x042f: case 0x044f: return 0x71; + } + return -1; +} + +/* + * Set command + */ +static t_stat cmd_set (int32 num, char *cptr) +{ + char gbuf [CBUFSIZE]; + int len; + + cptr = get_sim_sw (cptr); + if (! cptr) + return SCPE_INVSW; + if (! *cptr) + return SCPE_NOPARAM; + cptr = get_glyph (cptr, gbuf, 0); + if (*cptr) + return SCPE_2MARG; + + len = strlen (gbuf); + if (strncmp ("UNICODE", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_CHARSET_MASK; + tty_unit[num].flags |= TTY_UNICODE_CHARSET; + } else if (strncmp ("JCUKEN", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_CHARSET_MASK; + tty_unit[num].flags |= TTY_KOI7_JCUKEN_CHARSET; + } else if (strncmp ("QWERTY", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_CHARSET_MASK; + tty_unit[num].flags |= TTY_KOI7_QWERTY_CHARSET; + } else if (strncmp ("TT", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_STATE_MASK; + tty_unit[num].flags |= TTY_TELETYPE_STATE; + } else if (strncmp ("VT", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_STATE_MASK; + tty_unit[num].flags |= TTY_VT340_STATE; + } else if (strncmp ("CONSUL", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_STATE_MASK; + tty_unit[num].flags |= TTY_CONSUL_STATE; + } else if (strncmp ("DESTRBS", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_BSPACE_MASK; + tty_unit[num].flags |= TTY_DESTRUCTIVE_BSPACE; + } else if (strncmp ("AUTHBS", gbuf, len) == 0) { + tty_unit[num].flags &= ~TTY_BSPACE_MASK; + tty_unit[num].flags |= TTY_AUTHENTIC_BSPACE; + } else { + return SCPE_NXPAR; + } + return SCPE_OK; +} + +/* + * Show command + */ +static t_stat cmd_show (int32 num, char *cptr) +{ + TMLN *t = &tty_line [num]; + char gbuf [CBUFSIZE]; + MTAB *m; + int len; + + cptr = get_sim_sw (cptr); + if (! cptr) + return SCPE_INVSW; + if (! *cptr) { + sprintf (gbuf, "TTY%d", num); + tmxr_linemsg (t, gbuf); + for (m=tty_mod; m->mask; m++) { + if (m->pstring && + (tty_unit[num].flags & m->mask) == m->match) { + tmxr_linemsg (t, ", "); + tmxr_linemsg (t, m->pstring); + } + } + if (t->txlog) + tmxr_linemsg (t, ", log"); + tmxr_linemsg (t, "\r\n"); + return SCPE_OK; + } + cptr = get_glyph (cptr, gbuf, 0); + if (*cptr) + return SCPE_2MARG; + + len = strlen (gbuf); + if (strncmp ("STATISTICS", gbuf, len) == 0) { + sprintf (gbuf, "line %d: input queued/total = %d/%d, " + "output queued/total = %d/%d\r\n", num, + t->rxbpi - t->rxbpr, t->rxcnt, + t->txbpi - t->txbpr, t->txcnt); + tmxr_linemsg (t, gbuf); + } else { + return SCPE_NXPAR; + } + return SCPE_OK; +} + +/* + * Exit command + */ +static t_stat cmd_exit (int32 num, char *cptr) +{ + return SCPE_EXIT; +} + +static t_stat cmd_help (int32 num, char *cptr); + +static CTAB cmd_table[] = { + { "SET", &cmd_set, 0, + "set unicode select UTF-8 encoding\r\n" + "set jcuken select KOI7 encoding, 'jcuken' keymap\r\n" + "set qwerty select KOI7 encoding, 'qwerty' keymap\r\n" + "set tt use Teletype mode\r\n" + "set vt use Videoton-340 mode\r\n" + "set consul use Consul-254 mode\r\n" + "set destrbs destructive backspace\r\n" + "set authbs authentic backspace\r\n" + }, + { "SHOW", &cmd_show, 0, + "sh{ow} show modes of the terminal\r\n" + "sh{ow} s{tatistics} show network statistics\r\n" + }, + { "EXIT", &cmd_exit, 0, + "exi{t} | q{uit} | by{e} exit from simulation\r\n" + }, + { "QUIT", &cmd_exit, 0, NULL + }, + { "BYE", &cmd_exit, 0, NULL + }, + { "HELP", &cmd_help, 0, + "h{elp} type this message\r\n" + "h{elp} type help for command\r\n" + }, + { 0 } +}; + +/* + * Find command routine + */ +static CTAB *lookup_cmd (char *command) +{ + CTAB *c; + int len; + + len = strlen (command); + for (c=cmd_table; c->name; c++) { + if (strncmp (command, c->name, len) == 0) + return c; + } + return 0; +} + +/* + * Help command + */ +static t_stat cmd_help (int32 num, char *cptr) +{ + TMLN *t = &tty_line [num]; + char gbuf [CBUFSIZE]; + CTAB *c; + + cptr = get_sim_sw (cptr); + if (! cptr) + return SCPE_INVSW; + if (! *cptr) { + /* Список всех команд. */ + tmxr_linemsg (t, "Commands may be abbreviated. Commands are:\r\n\r\n"); + for (c=cmd_table; c && c->name; c++) + if (c->help) + tmxr_linemsg (t, c->help); + return SCPE_OK; + } + cptr = get_glyph (cptr, gbuf, 0); + if (*cptr) + return SCPE_2MARG; + c = lookup_cmd (gbuf); + if (! c) + return SCPE_ARG; + /* Описание конкретной команды. */ + tmxr_linemsg (t, c->help); + return SCPE_OK; +} + +/* + * Выполнение командной строки. + */ +void vt_cmd_exec (int num) +{ + TMLN *t = &tty_line [num]; + char *cptr, gbuf [CBUFSIZE]; + CTAB *cmdp; + t_stat err; + extern char *scp_errors[]; + + cptr = get_glyph (vt_cbuf [num], gbuf, 0); /* get command glyph */ + cmdp = lookup_cmd (gbuf); /* lookup command */ + if (! cmdp) { + tmxr_linemsg (t, scp_errors[SCPE_UNK - SCPE_BASE]); + tmxr_linemsg (t, "\r\n"); + return; + } + err = cmdp->action (num, cptr); /* if found, exec */ + if (err >= SCPE_BASE) { /* error? */ + tmxr_linemsg (t, scp_errors [err - SCPE_BASE]); + tmxr_linemsg (t, "\r\n"); + } + if (err == SCPE_EXIT) { /* close telnet session */ + tmxr_reset_ln (t); + } +} + +/* + * Режим управляющей командной строки. + */ +void vt_cmd_loop (int num, int c) +{ + TMLN *t = &tty_line [num]; + char *cbuf, **cptr; + + cbuf = vt_cbuf [num]; + cptr = &vt_cptr [num]; + + switch (c) { + case '\r': + case '\n': + tmxr_linemsg (t, "\r\n"); + if (*cptr <= cbuf) { + /* Пустая строка - возврат в обычный режим. */ + tty_unit[num].flags &= ~TTY_CMDLINE_MASK; + break; + } + /* Выполнение. */ + **cptr = 0; + vt_cmd_exec (num); + tmxr_linemsg (t, "sim>"); + *cptr = vt_cbuf[num]; + break; + case '\b': + case 0177: + /* Стирание предыдущего символа. */ + if (*cptr <= cbuf) + break; + tmxr_linemsg (t, "\b \b"); + while (*cptr > cbuf) { + --*cptr; + if (! (**cptr & 0x80)) + break; + } + break; + case 'U' & 037: + /* Стирание всей строки. */ +erase_line: while (*cptr > cbuf) { + --*cptr; + if (! (**cptr & 0x80)) + tmxr_linemsg (t, "\b \b"); + } + break; + case 033: + /* Escape [ X. */ + if (tmxr_getc_ln (t) != '[' + TMXR_VALID) + break; + switch (tmxr_getc_ln (t) - TMXR_VALID) { + case 'A': /* стрелка вверх */ + if (*cptr <= cbuf) { + *cptr = cbuf + strlen (cbuf); + if (*cptr > cbuf) + tmxr_linemsg (t, cbuf); + } + break; + case 'B': /* стрелка вниз */ + goto erase_line; + } + break; + default: + if (c < ' ' || *cptr > cbuf+CBUFSIZE-5) + break; + *(*cptr)++ = c; + tmxr_putc_ln (t, c); + break; + } +} + +/* + * Ввод символа с терминала с указанным номером. + * Если нет приёма, возвращает -1. + * В случае прерывания возвращает 0400 (только для консоли). + */ +int vt_getc (int num) +{ + TMLN *t = &tty_line [num]; + extern int32 sim_int_char; + int c; + time_t now; + + if (! t->conn) { + /* Пользователь отключился. */ + if (t->ipad) { + besm6_debug ("*** tty%d: отключение %s", + num, + t->ipad); + t->ipad = NULL; + } + tty_setmode (tty_unit+num, TTY_OFFLINE_STATE, 0, 0); + tty_unit[num].flags &= ~TTY_STATE_MASK; + return -1; + } + if (t->rcve) { + /* Приём через telnet. */ + c = tmxr_getc_ln (t); + if (! (c & TMXR_VALID)) { + now = time (0); + if (now > tty_last_time[num] + 5*60) { + ++tty_idle_count[num]; + if (tty_idle_count[num] > 3) { + tmxr_linemsg (t, "\r\nКОНЕЦ СЕАНСА\r\n"); + tmxr_reset_ln (t); + return -1; + } + tmxr_linemsg (t, "\r\nНЕ СПАТЬ!\r\n"); + tty_last_time[num] = now; + } + return -1; + } + tty_idle_count[num] = 0; + tty_last_time[num] = time (0); + + if (tty_unit[num].flags & TTY_CMDLINE_MASK) { + /* Продолжение режима управляющей командной строки. */ + vt_cmd_loop (num, c & 0377); + return -1; + } + if ((c & 0377) == sim_int_char) { + /* Вход в режим управляющей командной строки. */ + tty_unit[num].flags |= TTY_CMDLINE_MASK; + tmxr_linemsg (t, "sim>"); + vt_cptr[num] = vt_cbuf[num]; + return -1; + } + } else { + /* Ввод с клавиатуры. */ + c = sim_poll_kbd(); + if (c == SCPE_STOP) + return 0400; /* прерывание */ + if (! (c & SCPE_KFLAG)) + return -1; + } + return c & 0377; +} + +/* + * Ввод символа с клавиатуры. + * Перекодировка из UTF-8 в КОИ-7. + * Полученный символ находится в диапазоне 0..0177. + * Если нет ввода, возвращает -1. + * В случае прерывания (^E) возвращает 0400. + */ +static int vt_kbd_input_unicode (int num) +{ + int c1, c2, c3, r; +again: + r = vt_getc (num); + if (r < 0 || r > 0377) + return r; + c1 = r & 0377; + if (! (c1 & 0x80)) + return unicode_to_koi7 (c1); + + r = vt_getc (num); + if (r < 0 || r > 0377) + return r; + c2 = r & 0377; + if (! (c1 & 0x20)) + return unicode_to_koi7 ((c1 & 0x1f) << 6 | (c2 & 0x3f)); + + r = vt_getc (num); + if (r < 0 || r > 0377) + return r; + c3 = r & 0377; + if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { + /* Skip zero width no-break space. */ + goto again; + } + return unicode_to_koi7 ((c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | + (c3 & 0x3f)); +} + +/* + * Альтернативный вариант ввода, не требующий переключения на русскую клавиатуру. + * Символы "точка" и "запятая" вводятся через shift, "больше-меньше" - "тильда-гравис". + * "точка с запятой" - "закр. фиг. скобка", "апостроф" - "верт. черта". + */ +static int vt_kbd_input_koi7 (int num) +{ + int r; + + r = vt_getc (num); + if (r < 0 || r > 0377) + return r; + r &= 0377; + switch (r) { + case '\r': return '\003'; + case 'q': return 'j'; + case 'w': return 'c'; + case 'e': return 'u'; + case 'r': return 'k'; + case 't': return 'e'; + case 'y': return 'n'; + case 'u': return 'g'; + case 'i': return '{'; + case 'o': return '}'; + case 'p': return 'z'; + case '[': return 'h'; + case '{': return '['; + case 'a': return 'f'; + case 's': return 'y'; + case 'd': return 'w'; + case 'f': return 'a'; + case 'g': return 'p'; + case 'h': return 'r'; + case 'j': return 'o'; + case 'k': return 'l'; + case 'l': return 'd'; + case ';': return 'v'; + case '}': return ';'; + case '\'': return '|'; + case '|': return '\''; + case 'z': return 'q'; + case 'x': return '~'; + case 'c': return 's'; + case 'v': return 'm'; + case 'b': return 'i'; + case 'n': return 't'; + case 'm': return 'x'; + case ',': return 'b'; + case '<': return ','; + case '.': return '`'; + case '>': return '.'; + case '~': return '>'; + case '`': return '<'; + default: return r; + } +} + +int odd_parity(unsigned char c) +{ + c = (c & 0x55) + ((c >> 1) & 0x55); + c = (c & 0x33) + ((c >> 2) & 0x33); + c = (c & 0x0F) + ((c >> 4) & 0x0F); + return c & 1; +} + +/* + * Обработка ввода со всех подключенных терминалов. + */ +void vt_receive() +{ + uint32 workset = vt_mask; + int num; + + TTY_IN = 0; + for (num = besm6_highest_bit (workset) - TTY_MAX; + workset; num = besm6_highest_bit (workset) - TTY_MAX) { + uint32 mask = 1 << (TTY_MAX - num); + switch (tty_instate[num]) { + case 0: + switch (tty_unit[num].flags & TTY_CHARSET_MASK) { + case TTY_KOI7_JCUKEN_CHARSET: + tty_typed[num] = vt_kbd_input_koi7 (num); + break; + case TTY_KOI7_QWERTY_CHARSET: + tty_typed[num] = vt_getc (num); + break; + case TTY_UNICODE_CHARSET: + tty_typed[num] = vt_kbd_input_unicode (num); + break; + default: + tty_typed[num] = '?'; + break; + } + if (tty_typed[num] < 0) { + /* TODO: обработать исключение от "неоператорского" терминала */ + sim_interval = 0; + break; + } + if (tty_typed[num] <= 0177) { + if (tty_typed[num] == '\r' || tty_typed[num] == '\n') + tty_typed[num] = 3; /* ^C - конец строки */ + if (tty_typed[num] == '\177') + tty_typed[num] = '\b'; /* ASCII DEL -> BS */ + tty_instate[num] = 1; + TTY_IN |= mask; /* start bit */ + GRP |= GRP_TTY_START; /* не используется ? */ + MGRP |= BBIT(19); /* для терминалов по методу МГУ */ + vt_receiving |= mask; + } + break; + case 1 ... 7: + /* need inverted byte */ + TTY_IN |= (tty_typed[num] & (1 << (tty_instate[num]-1))) ? 0 : mask; + tty_instate[num]++; + break; + case 8: + TTY_IN |= odd_parity(tty_typed[num]) ? 0 : mask; /* even parity of inverted */ + tty_instate[num]++; + break; + case 9 ... 11: + /* stop bits are 0 */ + tty_instate[num]++; + break; + case 12: + tty_instate[num] = 0; /* ready for the next char */ + vt_receiving &= ~mask; + break; + } + workset &= ~mask; + } + if (vt_receiving) + vt_idle = 0; +} + +/* + * Выясняем, остановлены ли терминалы. Нужно для входа в "спящий" режим. + */ +int vt_is_idle () +{ + return (tt_mask ? vt_idle > 300 : vt_idle > 10); +} + +int tty_query () +{ +/* besm6_debug ("*** телетайпы: приём");*/ + return TTY_IN; +} + +void consul_print (int dev_num, uint32 cmd) +{ + int line_num = dev_num + TTY_MAX + 1; + if (tty_dev.dctrl) + besm6_debug(">>> CONSUL%o: %03o", line_num, cmd & 0377); + cmd &= 0177; + switch (tty_unit[line_num].flags & TTY_STATE_MASK) { + case TTY_VT340_STATE: + vt_send (line_num, cmd, + (tty_unit[line_num].flags & TTY_BSPACE_MASK) == TTY_DESTRUCTIVE_BSPACE); + break; + case TTY_CONSUL_STATE: + besm6_debug(">>> CONSUL%o: Native charset not implemented", line_num); + break; + } + // PRP |= CONS_CAN_PRINT[dev_num]; + vt_idle = 0; +} + +void consul_receive () +{ + int c, line_num, dev_num; + + for (dev_num = 0; dev_num < 2; ++dev_num){ + line_num = dev_num + TTY_MAX + 1; + if (! tty_line[line_num].conn) + continue; + switch (tty_unit[line_num].flags & TTY_CHARSET_MASK) { + case TTY_KOI7_JCUKEN_CHARSET: + c = vt_kbd_input_koi7 (line_num); + break; + case TTY_KOI7_QWERTY_CHARSET: + c = vt_getc (line_num); + break; + case TTY_UNICODE_CHARSET: + c = vt_kbd_input_unicode (line_num); + break; + default: + c = '?'; + break; + } + if (c >= 0 && c <= 0177) { + CONSUL_IN[dev_num] = odd_parity(c) ? c | 0200 : c; + if (c == '\r' || c == '\n') + CONSUL_IN[dev_num] = 3; + PRP |= CONS_HAS_INPUT[dev_num]; + vt_idle = 0; + } + } +} + +uint32 consul_read (int num) +{ + if (tty_dev.dctrl) + besm6_debug("<<< CONSUL%o: %03o", num+TTY_MAX+1, CONSUL_IN[num]); + return CONSUL_IN[num]; +} diff --git a/BESM6/boot_dispak.b6 b/BESM6/boot_dispak.b6 new file mode 100644 index 00000000..7c3c74a6 --- /dev/null +++ b/BESM6/boot_dispak.b6 @@ -0,0 +1,484 @@ +; boot_dispak.b6 +п 2000 + +; Использование клавиатуры пульта БРУС при вызове ОС ДИСПАК. +; +; ТР2: 32, 31, 26 и 25-й разряды = '1' - при работе с ЭНМД-3, +; либо - 33 и 27-й разряды = '1' - при работе с ЭНМД-4; +; 000 000 000 000 000 011 000 011 000 000 000 000 000 000 000 000 ЭНМД-3 +; 3 0 3 +; 000 000 000 000 000 100 000 100 000 000 000 000 000 000 000 000 ЭНМД-4 +; 4 0 4 +в 2 +с 0000 0303 0000 0000 + +; ТРЗ: 24 ÷ 17 и 4-й разряды = '1' - для чистки каталогов ОС; +; 000 000 000 000 000 000 000 000 111 111 110 000 000 000 001 000 +; 7 7 6 0 0 0 1 0 +;с 7760 0010 +с 0 + +в 02000 +к рег 101, уиа 2260(2) ; 02000 - 0002 0101 1240 2260 +к уиа 2502(12), уиа -56(6) ; 02001 - 5240 2502 3247 7722 +к сч 2537, зп 2605 ; 02002 - 0010 2537 0000 2605 +к сда 101, зп 2664(6) ; 02003 - 0036 0101 3000 2664 +к цикл 2003(6), уиа -2(1) ; 02004 - 3370 2003 0647 7776 +к сч 2521(1), зп 502(1) ; 02005 - 0410 2521 0400 0502 +к цикл 2005(1), уиа -2(6) ; 02006 - 0770 2005 3247 7776 +к уиа (13), сч ; 02007 - 5640 0000 0010 0000 +к слц 2546(6), цикл 2010(6) ; 02010 - 3013 2546 3370 2010 +к нтж, по 2070 ; 02011 - 0012 0000 0260 2070 +к уиа 2444(5), пв 2346(10) ; 02012 - 2640 2444 4310 2346 +к нтж 2543, пе 2345 ; 02013 - 0012 2543 0270 2345 +к сч 2, пб 3000 ; 02014 - 0010 0002 0300 3000 +к сда 136, и 2427 ; 02015 - 0036 0136 0011 2427 +к по 2017, вч 2664 ; 02016 - 0260 2017 0005 2664 +к уи 16, уии 3(16) ; 02017 - 0040 0016 7044 0003 +к сли 16(16), сли 16(3) ; 02020 - 7045 0016 1445 0016 +к уиа -2(3), мода ; 02021 - 1647 7776 0220 0000 +к мода (16), сч 2546(3) ; 02022 - 7220 0000 1410 2546 +к зп 1777(3), цикл 2022(3) ; 02023 - 1400 1777 1770 2022 +к уиа -1(3), мода ; 02024 - 1647 7777 0220 0000 +к сч 1776, пио 2027(3) ; 02025 - 0010 1776 1740 2027 +к сда 110, мода ; 02026 - 0036 0110 0220 0000 +к сда 50, зп 1033(3) ; 02027 - 0036 0050 1400 1033 +к сч 1775, сда 115 ; 02030 - 0010 1775 0036 0115 +к пио 2032(3), сда 120 ; 02031 - 1740 2032 0036 0120 +к и 2540, сда 70 ; 02032 - 0011 2540 0036 0070 +к или 1033(3), зп 1033(3) ; 02033 - 1415 1033 1400 1033 +к сч 1777, пио 2036(3) ; 02034 - 0010 1777 1740 2036 +к сда 110, мода ; 02035 - 0036 0110 0220 0000 +к и 2541, или 1033(3) ; 02036 - 0011 2541 1415 1033 +к зп 1033(3), цикл 2025(3) ; 02037 - 1400 1033 1770 2025 +к уиа 3(16), уиа -1(3) ; 02040 - 7240 0003 1647 7777 +к сч, зп 1773 ; 02041 - 0010 0000 0000 1773 +к уиа 10(7), мода ; 02042 - 3640 0010 0220 0000 +к уиа -1(15), уиа -7(14) ; 02043 - 6647 7777 6247 7771 +к сч 1777(3), слиа 1(15) ; 02044 - 1410 1777 6650 0001 +к сда 140(7), и 2655(15) ; 02045 - 3436 0140 6411 2655 +к по 2060, счи 15 ; 02046 - 0260 2060 0042 0015 +к нтж 2415, уи 17 ; 02047 - 0012 2415 0040 0017 +к сда 71, или 2532 ; 02050 - 0036 0071 0015 2532 +к зп 1774, сч 1773 ; 02051 - 0000 1774 0010 1773 +к сда 33, или 1774 ; 02052 - 0036 0033 0015 1774 +к зп 1774, пв 2126(11) ; 02053 - 0000 1774 4710 2126 +к сч 2534, и 2615 ; 02054 - 0010 2534 0011 2615 +к по 2074, сч 2534 ; 02055 - 0260 2074 0010 2534 +к сда 114, нтж 1775 ; 02056 - 0036 0114 0012 1775 +к и 2423, по 2076 ; 02057 - 0011 2423 0260 2076 +к цикл 2044(14), слиа -10(7) ; 02060 - 6370 2044 3657 7770 +к сч 1773, слц 2664 ; 02061 - 0010 1773 0013 2664 +к зп 1773, сда 102 ; 02062 - 0000 1773 0036 0102 +к по 2043, пио 2066(3) ; 02063 - 0260 2043 1740 2066 +к уиа 4(16), уиа (3) ; 02064 - 7240 0004 1640 0000 +к пб 2041, мода ; 02065 - 0300 2041 0220 0000 +к уиа 2502(5), пв 2367(7) ; 02066 - 2640 2502 3710 2367 +к стоп, пб 2040 ; 02067 - 0330 0000 0300 2040 +к уиа -35(16), уиа 1(13) ; 02070 - 7247 7743 5640 0001 +к сч 72035(16), зп 2601(16) ; 02071 - 7110 2035 7000 2601 +к цикл 2071(16), пв 2346(10) ; 02072 - 7370 2071 4310 2346 +к зп 2543, пб 2014 ; 02073 - 0000 2543 0300 2014 +к сч 2534, нтж 1775 ; 02074 - 0010 2534 0012 1775 +к и 2423, пе 2060 ; 02075 - 0011 2423 0270 2060 +к счи 17, сда 71 ; 02076 - 0042 0017 0036 0071 +к или 2420, зп 1774 ; 02077 - 0015 2420 0000 1774 +к счи 16, сда 76 ; 02100 - 0042 0016 0036 0076 +к слц 1773, сда 33 ; 02101 - 0013 1773 0036 0033 +к или 1774, пб 2105 ; 02102 - 0015 1774 0300 2105 +к уиа 2257(11), мода ; 02103 - 4640 2257 0220 0000 +к уиа 2106(2), сч 1027 ; 02104 - 1240 2106 0010 1027 +к зп 1774, пб (2) ; 02105 - 0000 1774 1300 0000 +к сч 1774, сда 147 ; 02106 - 0010 1774 0036 0147 +к уи 16, сч 1774 ; 02107 - 0040 0016 0010 1774 +к сда 107, и 2415 ; 02110 - 0036 0107 0011 2415 +к уи 17, нтж 2415 ; 02111 - 0040 0017 0012 2415 +к уи 15, сч 1774 ; 02112 - 0040 0015 0010 1774 +к сда 145, и 2416 ; 02113 - 0036 0145 0011 2416 +к сда 75, уи 6 ; 02114 - 0036 0075 0040 0006 +к сли 6(17), сч 1027(16) ; 02115 - 7445 0006 7010 1027 +к и 2625(6), уиа (1) ; 02116 - 3011 2625 0640 0000 +к по 2120, уиа 2(1) ; 02117 - 0260 2120 0640 0002 +к сч 1775, мода ; 02120 - 0010 1775 0220 0000 +к сда 130, слц 2531 ; 02121 - 0036 0130 0013 2531 +к и 2526, мода ; 02122 - 0011 2526 0220 0000 +к сда 77(1), зп 2527 ; 02123 - 0436 0077 0000 2527 +к счмр, сда 146 ; 02124 - 0031 0000 0036 0146 +к и 2653, зп 2604 ; 02125 - 0011 2653 0000 2604 +к сч, рег 37 ; 02126 - 0010 0000 0002 0037 +к сч 2417, увв 20(16) ; 02127 - 0010 2417 7033 0020 +к уи, сч 1774 ; 02130 - 0040 0000 0010 1774 +к сда 145, и 2416 ; 02131 - 0036 0145 0011 2416 +к или 2654, или 2653 ; 02132 - 0015 2654 0015 2653 +к увв 20(16), уи ; 02133 - 7033 0020 0040 0000 +к рег 237, и 2625(16) ; 02134 - 0002 0237 7011 2625 +к по 2134, сч 2661 ; 02135 - 0260 2134 0010 2661 +к увв 20(16), уи ; 02136 - 7033 0020 0040 0000 +к уи, рег 37 ; 02137 - 0040 0000 0002 0037 +к уиа 2147(6), уиа 11(4) ; 02140 - 3240 2147 2240 0011 +к сч 2655(15), пио 2143(1) ; 02141 - 6410 2655 0740 2143 +к или 2604, мода ; 02142 - 0015 2604 0220 0000 +к или 2421, увв 20(16) ; 02143 - 0015 2421 7033 0020 +к уи, счи 4 ; 02144 - 0040 0000 0042 0004 +к увв 20(16), уи ; 02145 - 7033 0020 0040 0000 +к увв 4000(16), пб (6) ; 02146 - 7033 4000 3300 0000 +к и 2654, уиа 2430(5) ; 02147 - 0011 2654 2640 2430 +к по 2412, уиа -7(6) ; 02150 - 0260 2412 3247 7771 +к сч, мода ; 02151 - 0010 0000 0220 0000 +к мод 2521(16), зп 7(6) ; 02152 - 7230 2521 3000 0007 +к цикл 2152(6), уиа -7(6) ; 02153 - 3370 2152 3247 7771 +к зп 1, цикл 2154(6) ; 02154 - 0000 0001 3370 2154 +к сч 1774, увв (16) ; 02155 - 0010 1774 7033 0000 +к сч 2416, или 2422 ; 02156 - 0010 2416 0015 2422 +к увв 20(16), уи ; 02157 - 7033 0020 0040 0000 +к сч 2527, или 2651 ; 02160 - 0010 2527 0015 2651 +к уи, увв 20(16) ; 02161 - 0040 0000 7033 0020 +к уи, увв ; 02162 - 0040 0000 0033 0000 +к уиа -50(6), мода ; 02163 - 3247 7730 0220 0000 +к зп 1, цикл 2164(6) ; 02164 - 0000 0001 3370 2164 +к сч, уи ; 02165 - 0010 0000 0040 0000 +к рег 37, уиа 76030(6) ; 02166 - 0002 0037 3247 6030 +к уиа 77540(10), мода ; 02167 - 4247 7540 0220 0000 +к сбр, рег 237 ; 02170 - 0020 0000 0002 0237 +к и 2625(16), пе 2175 ; 02171 - 7011 2625 0270 2175 +к цикл 2170(10), цикл 2167(6) ; 02172 - 4370 2170 3370 2167 +к стоп, уиа 2126(7) ; 02173 - 0330 0000 3640 2126 +к уиа 2474(5), пб 2366 ; 02174 - 2640 2474 0300 2366 +к уиа 11(4), пв 2144(6) ; 02175 - 2240 0011 3310 2144 +к зп 2533, и 2651 ; 02176 - 0000 2533 0011 2651 +к пе 2351, увв 4035 ; 02177 - 0270 2351 0033 4035 +к и 2655(16), уиа 2436(5) ; 02200 - 7011 2655 2640 2436 +к пе 2412, уиа 1(10) ; 02201 - 0270 2412 4240 0001 +к уиа 2206(6), уиа 2206(5) ; 02202 - 3240 2206 2640 2206 +к сч 2424, уи 21 ; 02203 - 0010 2424 0040 0021 +к мод 2521(16), сч (10) ; 02204 - 7230 2521 4010 0000 +к уи, пб (6) ; 02205 - 0040 0000 3300 0000 +к зп 2534, и 2535 ; 02206 - 0000 2534 0011 2535 +к нтж 2536, уиа 2460(5) ; 02207 - 0012 2536 2640 2460 +к пе 2412, сч 1774 ; 02210 - 0270 2412 0010 1774 +к и 2660, пе 2302 ; 02211 - 0011 2660 0270 2302 +к сч 2533, и 2652 ; 02212 - 0010 2533 0011 2652 +к уиа 2444(5), пе 2366 ; 02213 - 2640 2444 0270 2366 +к уиа -3(4), уиа (12) ; 02214 - 2247 7775 5240 0000 +к счи 11, нтж 2530 ; 02215 - 0042 0011 0012 2530 +к пе 2217, слиа 2000(12) ; 02216 - 0270 2217 5250 2000 +к сч 2534, и 2614 ; 02217 - 0010 2534 0011 2614 +к пе 2240, уиа -1(10) ; 02220 - 0270 2240 4247 7777 +к уиа 2411(5), сч 2424 ; 02221 - 2640 2411 0010 2424 +к уи 21, уиа 77401(6) ; 02222 - 0040 0021 3247 7401 +к слиа 2(10), сч ; 02223 - 4250 0002 0010 0000 +к мода 70000(12), слц 377(6) ; 02224 - 5227 0000 3013 0377 +к цикл 2224(6), зп 2534 ; 02225 - 3370 2224 0000 2534 +к уиа 2227(5), пв 2203(6) ; 02226 - 2640 2227 3310 2203 +к зп 2533, нтж 2534 ; 02227 - 0000 2533 0012 2534 +к и 2423, пе 2250 ; 02230 - 0011 2423 0270 2250 +к сч 2533, и 2615 ; 02231 - 0010 2533 0011 2615 +к уиа 2444(5), по 2366 ; 02232 - 2640 2444 0260 2366 +к слиа 400(12), мода ; 02233 - 5250 0400 0220 0000 +к цикл 2221(4), уиа 2255(6) ; 02234 - 2370 2221 3240 2255 +к сч 2425, уи 21 ; 02235 - 0010 2425 0040 0021 +к сч 2417, увв 20(16) ; 02236 - 0010 2417 7033 0020 +к уи, пб (6) ; 02237 - 0040 0000 3300 0000 +к уиа 2411(5), сч 2424 ; 02240 - 2640 2411 0010 2424 +к уи 21, уиа 76001(6) ; 02241 - 0040 0021 3247 6001 +к сч, уиа 3(10) ; 02242 - 0010 0000 4240 0003 +к мода 70000(12), слц 1777(6) ; 02243 - 5227 0000 3013 1777 +к цикл 2243(6), зп 2534 ; 02244 - 3370 2243 0000 2534 +к уиа 2246(5), пв 2203(6) ; 02245 - 2640 2246 3310 2203 +к нтж 2534, пе 2213 ; 02246 - 0012 2534 0270 2213 +к уиа 2255(6), пб 2235 ; 02247 - 3240 2255 0300 2235 +к слиа -1(10), уиа 2252(5) ; 02250 - 4257 7777 2640 2252 +к мода, пв 2203(6) ; 02251 - 0220 0000 3310 2203 +к нтж 2534, и 2423 ; 02252 - 0012 2534 0011 2423 +к пе 2213, слиа 1(10) ; 02253 - 0270 2213 4250 0001 +к слиа 400(12), пб 2234 ; 02254 - 5250 0400 0300 2234 +к сч, уи ; 02255 - 0010 0000 0040 0000 +к рег 37, пб (11) ; 02256 - 0002 0037 4700 0000 +к уиа 1(14), пб 2335 ; 02257 - 6240 0001 0300 2335 +к зп 1027, сч 2516 ; 02260 - 0000 1027 0010 2516 +к или 1775, зп 1775 ; 02261 - 0015 1775 0000 1775 +к уиа 2265(11), пб 2104 ; 02262 - 4640 2265 0300 2104 +к слиа 40(4), цикл 2272(14) ; 02263 - 2250 0040 6370 2272 +к уиа 2466(5), пв 2367(7) ; 02264 - 2640 2466 3710 2367 +к сч 2, сда 130 ; 02265 - 0010 0002 0036 0130 +к и 2427, уиа -17(14) ; 02266 - 0011 2427 6247 7761 +к уиа (2), уиа (4) ; 02267 - 1240 0000 2240 0000 +к по 2271, вч 2664 ; 02270 - 0260 2271 0005 2664 +к зп 2542, мода ; 02271 - 0000 2542 0220 0000 +к уиа -37(7), сч 1775 ; 02272 - 3647 7741 0010 1775 +к нтж 70000(4), и 2423 ; 02273 - 2112 0000 0011 2423 +к пе 2263, сч 1775 ; 02274 - 0270 2263 0010 1775 +к сда 155, нтж 70016(4) ; 02275 - 0036 0155 2112 0016 +к и 2415, пе 2263 ; 02276 - 0011 2415 0270 2263 +к счи 2, нтж 2542 ; 02277 - 0042 0002 0012 2542 +к по 2303, слиа 1(2) ; 02300 - 0260 2303 1250 0001 +к пб 2263, мода ; 02301 - 0300 2263 0220 0000 +к уии 6(11), пб 2235 ; 02302 - 4444 0006 0300 2235 +к мода (4), сч 70037(7) ; 02303 - 2220 0000 3510 0037 +к зп 3537(7), цикл 2303(7) ; 02304 - 3400 3537 3770 2303 +к сч 3502, зп 1775 ; 02305 - 0010 3502 0000 1775 +к мода, пв 2040(2) ; 02306 - 0220 0000 1310 2040 +к зп 1030, сч 3500 ; 02307 - 0000 1030 0010 3500 +к зп 1775, сч 3 ; 02310 - 0000 1775 0010 0003 +к и 2660, пб 2103 ; 02311 - 0011 2660 0300 2103 +к уиа -2(14), сч 3515 ; 02312 - 6247 7776 0010 3515 +к сда 117, зп 1775 ; 02313 - 0036 0117 0000 1775 +к и 2423, по 2103 ; 02314 - 0011 2423 0260 2103 +к сч 2663, пв 2123(11) ; 02315 - 0010 2663 4710 2123 +к сч 3516, и 2415 ; 02316 - 0010 3516 0011 2415 +к сда 75, уи 6 ; 02317 - 0036 0075 0040 0006 +к сч 70000(6), нтж 3516 ; 02320 - 3110 0000 0012 3516 +к и 2415, пе 2103 ; 02321 - 0011 2415 0270 2103 +к мода (6), сч 70002(14) ; 02322 - 3220 0000 6110 0002 +к зп 2604(14), цикл 2322(14) ; 02323 - 6000 2604 6370 2322 +к уиа 2510(12), пв 2040(2) ; 02324 - 5240 2510 1310 2040 +к уиа -2(14), пв 2126(11) ; 02325 - 6247 7776 4710 2126 +к сч 70002(14), нтж 2604(14) ; 02326 - 6110 0002 6012 2604 +к уиа 2257(11), пе 2103 ; 02327 - 4640 2257 0270 2103 +к цикл 2326(14), уиа -22(14) ; 02330 - 6370 2326 6247 7756 +к сч 70026(14), зп 1026(14) ; 02331 - 6110 0026 6000 1026 +к цикл 2331(14), уиа 2335(11) ; 02332 - 6370 2331 4640 2335 +к сч 1774, зп 1031 ; 02333 - 0010 1774 0000 1031 +к пб 2104, мода ; 02334 - 0300 2104 0220 0000 +к сч 1774, слц 2650 ; 02335 - 0010 1774 0013 2650 +к зп 1774, сч 1775 ; 02336 - 0000 1774 0010 1775 +к слц 2634, пв 2121(11) ; 02337 - 0013 2634 4710 2121 +к сч 2522, зп 502 ; 02340 - 0010 2522 0000 0502 +к уиа -37(6), мода ; 02341 - 3247 7741 0220 0000 +к сч 3537(6), зп 71777(6) ; 02342 - 3010 3537 3100 1777 +к цикл 2342(6), сч ; 02343 - 3370 2342 0010 0000 +к зп 72000, пб 70000 ; 02344 - 0100 2000 0307 0000 +к уиа 2345(7), пб 2367 ; 02345 - 3640 2345 0300 2367 +к уиа -2(3), сч ; 02346 - 1647 7776 0010 0000 +к слц 2546(3), цикл 2347(3) ; 02347 - 1413 2546 1770 2347 +к пб (10), мода ; 02350 - 4300 0000 0220 0000 +к сч 1774, и 2660 ; 02351 - 0010 1774 0011 2660 +к пе 2060, уиа 31(4) ; 02352 - 0270 2060 2240 0031 +к мода, пв 2144(6) ; 02353 - 0220 0000 3310 2144 +к зп 2534, сч 2533 ; 02354 - 0000 2534 0010 2533 +к и 2654, по 2402 ; 02355 - 0011 2654 0260 2402 +к сч 2534, и 2662 ; 02356 - 0010 2534 0011 2662 +к уиа 2452(5), пе 2404 ; 02357 - 2640 2452 0270 2404 +к сч 2534, и 2660 ; 02360 - 0010 2534 0011 2660 +к уиа 2466(5), пе 2366 ; 02361 - 2640 2466 0270 2366 +к сч 2534, и 2661 ; 02362 - 0010 2534 0011 2661 +к пе 2410, сч 2534 ; 02363 - 0270 2410 0010 2534 +к и 2652, уиа 2452(5) ; 02364 - 0011 2652 2640 2452 +к пе 2366, уиа 2452(5) ; 02365 - 0270 2366 2640 2452 +к уиа 2126(7), пв 2235(6) ; 02366 - 3640 2126 3310 2235 +к уиа -10(10), мода ; 02367 - 4247 7770 0220 0000 +к зп 1, цикл 2370(10) ; 02370 - 0000 0001 4370 2370 +к сч, рег ; 02371 - 0010 0000 0002 0000 +к уиа -5(10), мода ; 02372 - 4247 7773 0220 0000 +к мода (5), сч 5(10) ; 02373 - 2620 0000 4010 0005 +к рег 6(10), цикл 2373(10) ; 02374 - 4002 0006 4370 2373 +к сч, рег 7 ; 02375 - 0010 0000 0002 0007 +к уиа 76030(10), мода ; 02376 - 4247 6030 0220 0000 +к уиа 77526(3), мода ; 02377 - 1647 7526 0220 0000 +к сбр, цикл 2400(3) ; 02400 - 0020 0000 1770 2400 +к цикл 2377(10), пб (7) ; 02401 - 4370 2377 3700 0000 +к сч 2533, уиа 2430(5) ; 02402 - 0010 2533 2640 2430 +к и 2653, по 2366 ; 02403 - 0011 2653 0260 2366 +к уиа 1(4), пв 2144(6) ; 02404 - 2240 0001 3310 2144 +к мода, пв 2235(6) ; 02405 - 0220 0000 3310 2235 +к уиа 76030(10), пв 2377(7) ; 02406 - 4247 6030 3710 2377 +к уиа 2126(7), пб 2367 ; 02407 - 3640 2126 0300 2367 +к уиа 2452(5), пб 2366 ; 02410 - 2640 2452 0300 2366 +к уиа 2126(6), пб 2235 ; 02411 - 3240 2126 0300 2235 +к сч 1774, и 2660 ; 02412 - 0010 1774 0011 2660 +к по 2366, пв 2235(6) ; 02413 - 0260 2366 3310 2235 +к пб 2060, мода ; 02414 - 0300 2060 0220 0000 + +в 02415 +с 0000 0000 0000 0007 ; 02415 +с 0000 0000 0000 0003 ; 02416 +с 0000 0000 0000 1050 ; 02417 +с 0000 0000 0174 0000 ; 02420 +с 0000 0000 0000 2400 ; 02421 +с 0000 0000 0000 0000 ; 02422 +с 0000 0000 0000 7777 ; 02423 +с 0000 0000 0000 2003 ; 02424 +с 0000 0000 0000 2013 ; 02425 +с 0000 0000 0000 0000 ; 02426 +с 0000 0000 0000 0017 ; 02427 +с 2236 0400 4176 2000 ; 02430 +с 2220 0400 4044 0000 ; 02431 +с 2220 0400 4044 0000 ; 02432 +с 3636 0400 4044 0000 ; 02433 +с 2220 0400 4044 0000 ; 02434 +с 2236 3700 7434 0000 ; 02435 +с 0003 4376 2022 0000 ; 02436 +с 0004 2222 2220 0000 ; 02437 +с 0004 2222 2220 0000 ; 02440 +с 0004 2222 2220 0000 ; 02441 +с 0004 2222 2520 0000 ; 02442 +с 0003 4222 3060 0000 ; 02443 +с 3476 2046 3440 4000 ; 02444 +с 4252 0044 4244 4000 ; 02445 +с 4252 0044 4044 4000 ; 02446 +с 4252 0070 4044 4000 ; 02447 +с 4252 0044 4252 4000 ; 02450 +с 3452 0046 3461 4000 ; 02451 +с 0000 0021 0740 0000 ; 02452 +с 0000 0021 0420 0000 ; 02453 +с 0000 0037 0420 0000 ; 02454 +с 0000 0021 0740 0000 ; 02455 +с 0000 0021 0420 0000 ; 02456 +с 0000 0017 0740 0000 ; 02457 +с 2236 0046 6247 0040 ; 02460 +с 2220 0044 2250 4040 ; 02461 +с 2220 0044 2250 4040 ; 02462 +с 3636 0070 2270 4740 ; 02463 +с 2220 0044 2250 4440 ; 02464 +с 2236 0046 1647 0440 ; 02465 +с 0022 3604 0070 4440 ; 02466 +с 0022 2004 0044 4440 ; 02467 +с 0022 2004 0044 4470 ; 02470 +с 0036 3604 0072 7444 ; 02471 +с 0022 2004 0044 4444 ; 02472 +с 0022 3637 0070 3470 ; 02473 +с 2236 0400 3470 4044 ; 02474 +с 2220 0400 4244 4440 ; 02475 +с 2220 0400 4244 4440 ; 02476 +с 3636 0400 4270 4440 ; 02477 +с 2220 0400 4240 5240 ; 02500 +с 2236 3700 3474 6140 ; 02501 +с 4561 0213 7462 2422 ; 02502 +с 4501 0211 1112 2420 ; 02503 +с 4501 0251 1013 6434 ; 02504 +с 7561 0251 1022 2422 ; 02505 +с 4501 0251 1112 2422 ; 02506 +с 4567 6330 7061 6734 ; 02507 +с 0456 1021 3716 2240 ; 02510 +с 0450 1021 1101 2240 ; 02511 +с 0450 1025 1107 2270 ; 02512 +с 0756 1025 1111 2244 ; 02513 +с 0450 1025 1111 2244 ; 02514 +с 0456 7633 0711 3670 ; 02515 +с 0000 0757 0000 0000 ; 02516 +с 0000 2523 0000 0000 ; 02517 +с 0010 2425 0040 0021 ; 02520 +с 0010 2523 2700 0000 ; 02521 +с 0010 2523 1700 0000 ; 02522 +с 0000 0000 0000 0000 ; 02523 +с 0000 0000 0000 0030 ; 02524 +с 0000 0000 0000 0040 ; 02525 +с 0000 0000 0003 7777 ; 02526 +с 0000 0000 0000 1334 ; 02527 +с 0000 0000 0000 2340 ; 02530 +с 0000 0000 0007 7775 ; 02531 +с 0000 0000 0440 0020 ; 02532 +с 0017 0707 4005 0513 ; 02533 +с 1646 5273 4157 0513 ; 02534 +с 0007 7777 0000 0000 ; 02535 +с 0007 0707 0000 0000 ; 02536 +с 4000 0000 0000 0000 ; 02537 +с 0000 0000 0017 7777 ; 02540 +с 0000 0000 0000 0377 ; 02541 +с 0000 0000 0000 0000 ; 02542 +с 2740 0000 0000 4007 ; 02543 +с 3000 0000 0000 4005 ; 02544 +с 7760 0000 0000 0000 ; 02545 +с 7760 0000 0000 0000 ; 02546 +с 3000 0000 0000 4005 ; 02547 +с 7777 7400 0000 0000 ; 02550 +с 7760 0000 0000 0000 ; 02551 +с 3000 0000 0000 4005 ; 02552 +с 7777 7400 0000 0000 ; 02553 +с 0000 0000 0000 0000 ; 02554 + +в 02600 +с 0000 0000 0000 0000 ; 02600 +с 0000 0000 0000 0037 ; 02601 +с 0000 0000 0742 1025 ; 02602 +с 0000 0000 0021 0000 ; 02603 +с 0000 0000 0000 0000 ; 02604 +с 4000 0000 0000 0000 ; 02605 +с 2000 0000 0000 0000 ; 02606 +с 1000 0000 0000 0000 ; 02607 +с 0400 0000 0000 0000 ; 02610 +с 0200 0000 0000 0000 ; 02611 +с 0100 0000 0000 0000 ; 02612 +с 0040 0000 0000 0000 ; 02613 +с 0020 0000 0000 0000 ; 02614 +с 0010 0000 0000 0000 ; 02615 +с 0004 0000 0000 0000 ; 02616 +с 0002 0000 0000 0000 ; 02617 +с 0001 0000 0000 0000 ; 02620 +с 0000 4000 0000 0000 ; 02621 +с 0000 2000 0000 0000 ; 02622 +с 0000 1000 0000 0000 ; 02623 +с 0000 0400 0000 0000 ; 02624 +с 0000 0200 0000 0000 ; 02625 +с 0000 0100 0000 0000 ; 02626 +с 0000 0040 0000 0000 ; 02627 +с 0000 0020 0000 0000 ; 02630 +с 0000 0010 0000 0000 ; 02631 +с 0000 0004 0000 0000 ; 02632 +с 0000 0002 0000 0000 ; 02633 +с 0000 0001 0000 0000 ; 02634 +с 0000 0000 4000 0000 ; 02635 +с 0000 0000 2000 0000 ; 02636 +с 0000 0000 1000 0000 ; 02637 +с 0000 0000 0400 0000 ; 02640 +с 0000 0000 0200 0000 ; 02641 +с 0000 0000 0100 0000 ; 02642 +с 0000 0000 0040 0000 ; 02643 +с 0000 0000 0020 0000 ; 02644 +с 0000 0000 0010 0000 ; 02645 +с 0000 0000 0004 0000 ; 02646 +с 0000 0000 0002 0000 ; 02647 +с 0000 0000 0001 0000 ; 02650 +с 0000 0000 0000 4000 ; 02651 +с 0000 0000 0000 2000 ; 02652 +с 0000 0000 0000 1000 ; 02653 +с 0000 0000 0000 0400 ; 02654 +с 0000 0000 0000 0200 ; 02655 +с 0000 0000 0000 0100 ; 02656 +с 0000 0000 0000 0040 ; 02657 +с 0000 0000 0000 0020 ; 02660 +с 0000 0000 0000 0010 ; 02661 +с 0000 0000 0000 0004 ; 02662 +с 0000 0000 0000 0002 ; 02663 +с 0000 0000 0000 0001 ; 02664 + +в 03000 +к по 3002, мода ; 03000 - 0260 3002 0220 0000 +к ржа 3, пб 2015 ; 03001 - 0037 0003 0300 2015 +к уиа 3004(7), уиа 2466(5) ; 03002 - 3640 3004 2640 2466 +к пб 2367, мода ; 03003 - 0300 2367 0220 0000 +к слиа 1(1), пб 3004 ; 03004 - 0650 0001 0300 3004 + +в 03500 +с 0000 0560 0000 4005 ; 03500 +с 4000 0000 0000 4005 ; 03501 +с 0000 0000 0000 4005 ; 03502 +с 0000 0000 0000 4005 ; 03503 +с 0000 0000 0000 0006 ; 03504 +с 4000 0000 0000 0000 ; 03505 +с 0000 0000 0000 3320 ; 03506 +с 0000 0000 0000 0022 ; 03507 +с 0000 0000 0000 0021 ; 03510 +с 7760 0000 3760 0000 ; 03511 +с 0000 0000 0000 0000 ; 03512 +с 0000 0000 0000 0000 ; 03513 +с 0000 0000 0000 0000 ; 03514 +с 4000 0070 0004 0310 ; 03515 +с 1770 0000 0000 0003 ; 03516 +с 0000 0000 1600 4227 ; 03517 +с 3722 1066 0361 6435 ; 03520 +с 0000 0000 0000 0000 ; 03521 +с 7760 0000 0000 0000 ; 03522 +с 7760 0000 0000 3220 ; 03523 +с 0000 0000 1022 4005 ; 03524 +с 1240 0000 0000 0000 ; 03525 +с 1313 1413 1413 1413 ; 03526 +с 7777 4000 6000 0000 ; 03527 +с 7403 0000 0000 0000 ; 03530 +с 7777 7777 7777 7777 ; 03531 +с 0000 0017 7760 0000 ; 03532 +с 0000 0000 0000 0000 ; 03533 +с 0000 0000 0000 0000 ; 03534 +с 0000 0000 0000 0000 ; 03535 +с 0000 0000 7760 0000 ; 03536 +с 0000 0000 0000 0000 ; 03537 diff --git a/BESM6/dispak.ini b/BESM6/dispak.ini new file mode 100644 index 00000000..d182d028 --- /dev/null +++ b/BESM6/dispak.ini @@ -0,0 +1,92 @@ +! rm -f log.txt +set console log=log.txt +set console debug=log +;set cpu debug +;set mmu debug +;set drum debug +;set disk debug + +; +; Приводим барабаны в исходное состояние. +; +! rm -f drum1x.bin drum2x.bin +attach drum0 drum1x.bin +attach drum1 drum2x.bin + +; +; Создаем рабочий диск. +; +! ./formatdisk 2052 > 2052.bin + +; +; Подключаем диски. +; +attach -e disk7 sbor2053.bin +attach -e disk6 2052.bin +attach -e disk5 krab2063.bin +attach -e disk0 sbor2048.bin +attach -e disk1 svs2048.bin +attach -e disk2 alt2048.bin + +; +; Подключаем АЦПУ. +; +attach prn0 /dev/tty + +; +; Активируем операторский терминал, +; на который идут сообщения. +; +attach tty1 /dev/tty + +; +; Режимы по вкусу +; +;set tty1 jcuken,authbs +set tty1 authbs +set tty1 log=tty1.txt + +; +; Разрешаем подключение пользователей по telnet, порт 4199. +; +attach tty 4199 +set tty2 authbs +set tty3 authbs +set tty4 authbs +set tty5 authbs +set tty6 authbs +set tty7 authbs +set tty8 authbs +set tty9 authbs +set tty10 authbs +set tty11 authbs +set tty12 authbs +set tty13 authbs +set tty14 authbs +set tty15 authbs +set tty16 authbs +set tty17 authbs +set tty18 authbs +set tty19 authbs +set tty20 authbs +set tty21 authbs +set tty22 authbs +set tty23 authbs +set tty24 authbs + +; +; Включение БРС/БРЗ для совместимости. +; Замедляет работу на 20%. +; +;set mmu cache + +; +; Запуск ОС ДИСПАК. +; +load boot_dispak.b6 + +; Происходит контроль числа по адресу 1031 +d 1031 0 + +run 2000 +;quit diff --git a/BESM6/formatdisk.c b/BESM6/formatdisk.c new file mode 100755 index 00000000..b72c7b86 --- /dev/null +++ b/BESM6/formatdisk.c @@ -0,0 +1,63 @@ +/* + * formatdisk.c - A utility to produce blank BESM-6 disk images. + * + * Copyright (c) 2014 Leonid Broukhis + * + * 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 +#include +#include +#include "besm6_defs.h" + +#define TOTBLK 01767 + +int main(int argc, char ** argv) { + t_value control[4]; /* block (zone) number, key, userid, checksum */ + int diskno, blkno, word; + + if (argc != 2 || (diskno = atoi(argv[1])) < 2048 || diskno > 4095) { + fprintf(stderr, "Usage: formatdisk NNNN > diskNNNN.bin, where 2048 <= NNNN <= 4095\n"); + exit(1); + } + + control[1] = SET_CONVOL(0, CONVOL_NUMBER); + control[2] = SET_CONVOL(0, CONVOL_NUMBER); + control[3] = SET_CONVOL(0, CONVOL_NUMBER); + + control[1] |= 01370707LL << 24; /* Magic */ + control[1] |= diskno << 12; + + for (blkno = 0; blkno < TOTBLK; ++blkno) { + control[0] = SET_CONVOL((t_value)(2*blkno) << 36, CONVOL_NUMBER); + fwrite(control, sizeof(t_value), 4, stdout); + control[0] = SET_CONVOL((t_value)(2*blkno+1) << 36, CONVOL_NUMBER); + fwrite(control, sizeof(t_value), 4, stdout); + for (word = 0; word < 02000; ++word) { + fwrite(control+2, sizeof(t_value), 1, stdout); + } + } + exit(0); +} diff --git a/BESM6/test_alu.b6 b/BESM6/test_alu.b6 new file mode 100644 index 00000000..6ee7c1fa --- /dev/null +++ b/BESM6/test_alu.b6 @@ -0,0 +1,2425 @@ +; +; Тест арифметического устройства БЭСМ-6. +; Если все хорошо, бесконечно циклится, начиная с адреса 32013. +; +п 32000 + +в 32000 +к уиа 30000(1), слц 2011(1) +к мода, мода +к мода, мода +к мода, мода +к мода, мода +к мода, мода +к мода, мода +к пб 2012(1), мода +с 0 +с 0000010000000001 +к сч, зп -12 ; 2012 +к ржа, мода ; 2013 +к сч, по 2016(1) +к стоп 2014(1), мода +к сч 4412(1), сч ; 2016 +к по 2020(1), стоп 2016(1) +к сч, сч 4412(1) ; 2020 +к нтж 4412(1), по 2023(1) +к стоп 2020(1), мода +к сч 4412(1), нтж ; 2023 +к пе 2025(1), стоп 2023(1) +к сч, по 2026(1) ; 2025 +к сч 4412(1), пе 2030(1) ; 2026 +к стоп 2025(1), мода +к сч 4412(1), нтж 4412(1) ; 2030 +к сч, по 2033(1) +к стоп 2030(1), мода +к сч 4412(1), зп 7777 ; 2033 +к сч 7777, нтж 4412(1) +к по 2036(1), стоп 2033(1) +к зп 7777, сч 7777 ; 2036 +к нтж, по 2041(1) +к стоп 2036(1), мода +к сч, нтж ; 2041 +к по 2043(1), стоп 2041(1) +к сч 4412(1), нтж 4412(1) ; 2043 +к по 2045(1), стоп 2043(1) +к сч 4413(1), нтж 4413(1) ; 2045 +к по 2047(1), стоп 2045(1) +к сч 4414(1), нтж 4414(1) ; 2047 +к по 2051(1), стоп 2047(1) +к сч 4412(1), нтж ; 2051 +к нтж 4412(1), по 2054(1) +к стоп 2051(1), мода +к сч, нтж 4412(1) ; 2054 +к счмр, по 2057(1) +к стоп 2054(1), мода +к сч, нтж 4412(1) ; 2057 +к нтж 4412(1), по 2062(1) +к стоп 2057(1), мода +к сч 4412(1), нтж 4412(1) ; 2062 +к счмр, нтж 4412(1) +к по 2065(1), стоп 2062(1) +к сч 4415(1), нтж ; 2065 +к счмр, нтж 4415(1) +к по 2070(1), стоп 2065(1) +к сч 4416(1), нтж ; 2070 +к счмр, нтж 4416(1) +к по 2073(1), стоп 2070(1) +к сч 4417(1), нтж ; 2073 +к счмр, нтж 4417(1) +к по 2076(1), стоп 2073(1) +к сч 4420(1), нтж ; 2076 +к счмр, нтж 4420(1) +к по 2101(1), стоп 2076(1) +к сч 4421(1), нтж ; 2101 +к зп 7777, счмр +к нтж 4421(1), по 2105(1) +к стоп 2101(1), мода +к сч 4422(1), нтж 4422(1) ; 2105 +к счмр 1, нтж 4422(1) +к по 2110(1), стоп 2105(1) +к сч, и ; 2110 +к по 2112(1), стоп 2110(1) +к сч, и 4412(1) ; 2112 +к по 2114(1), стоп 2112(1) +к сч 4412(1), и 4412(1) ; 2114 +к нтж 4412(1), по 2117(1) +к стоп 2114(1), мода +к сч 4413(1), и 4412(1) ; 2117 +к нтж 4413(1), по 2122(1) +к стоп 2117(1), мода +к сч 4414(1), и 4412(1) ; 2122 +к нтж 4414(1), по 2125(1) +к стоп 2122(1), мода +к сч 4423(1), и 4423(1) ; 2125 +к нтж 4423(1), по 2130(1) +к стоп 2125(1), мода +к сч, или ; 2130 +к по 2132(1), стоп 2130(1) +к сч, или 4412(1) ; 2132 +к нтж 4412(1), по 2135(1) +к стоп 2132(1), мода +к сч 4412(1), или ; 2135 +к нтж 4412(1), по 2140(1) +к стоп 2135(1), мода +к сч 4412(1), или 4412(1) ; 2140 +к нтж 4412(1), по 2143(1) +к стоп 2140(1), мода +к сч 4413(1), или 4413(1) ; 2143 +к нтж 4413(1), по 2146(1) +к стоп 2143(1), мода +к сч 4414(1), или 4414(1) ; 2146 +к нтж 4414(1), по 2151(1) +к стоп 2146(1), мода +к сч 4412(1), слц ; 2151 +к по 2153(1), стоп 2151(1) +к сч 4412(1), слц 4412(1) ; 2153 +к нтж 4412(1), по 2156(1) +к стоп 2153(1), мода +к сч, слц 4412(1) ; 2156 +к нтж 4412(1), по 2161(1) +к стоп 2156(1), мода +к сч 4412(1), слц ; 2161 +к нтж 4412(1), по 2164(1) +к стоп 2161(1), мода +к сч 4424(1), слц 4412(1) ; 2164 +к нтж 4424(1), по 2167(1) +к стоп 2164(1), мода +к сч 4425(1), слц 4412(1) ; 2167 +к нтж 4425(1), по 2172(1) +к стоп 2167(1), мода +к сч 4413(1), слц 4413(1) ; 2172 +к нтж 4414(1), по 2175(1) +к стоп 2172(1), мода +к сч 4414(1), слц 4414(1) ; 2175 +к нтж 4413(1), по 2200(1) +к стоп 2175(1), мода +к сч, слц ; 2200 +к пе 2202(1), стоп 2200(1) +к сч 4426(1), сда 20 ; 2202 +к по 2204(1), стоп 2202(1) +к сч 4427(1), сда 102 ; 2204 +к нтж 4430(1), по 2207(1) +к стоп 2204(1), мода +к сч 4431(1), сда 77 ; 2207 +к нтж 4432(1), по 2212(1) +к стоп 2207(1), мода +к сч 4433(1), сда 160 ; 2212 +к счмр, нтж 4433(1) +к по 2215(1), стоп 2212(1) +к сч 4426(1), сда 130 ; 2215 +к счмр, нтж 4434(1) +к по 2220(1), стоп 2215(1) +к сч, сда 157 ; 2220 +к счмр, по 2223(1) +к стоп 2220(1), мода +к сч 4435(1), сда 20 ; 2223 +к счмр, нтж 4435(1) +к по 2226(1), стоп 2223(1) +к сч 4412(1), сда 20 ; 2226 +к счмр, нтж 4412(1) +к по 2231(1), стоп 2226(1) +к сч 4426(1), сда 20 ; 2231 +к сч 4412(1), счмр +к нтж 4426(1), по 2235(1) +к стоп 2231(1), мода +к сч 4414(1), сда 77 ; 2235 +к нтж 4436(1), по 2240(1) +к стоп 2235(1), мода +к сч 4413(1), сда 77 ; 2240 +к нтж 4414(1), по 2243(1) +к стоп 2240(1), мода +к сч 4412(1), сд 4437(1) ; 2243 +к по 2245(1), стоп 2243(1) +к сч 4412(1), сд 4440(1) ; 2245 +к по 2247(1), стоп 2245(1) +к сч 4414(1), сд 4441(1) ; 2247 +к нтж 4442(1), по 2252(1) +к стоп 2247(1), мода +к сч 4413(1), сд 4443(1) ; 2252 +к нтж 4436(1), по 2255(1) +к стоп 2252(1), мода +к сч 4414(1), сд 4444(1) ; 2255 +к нтж 4436(1), по 2260(1) +к стоп 2255(1), мода +к сч 4413(1), сд 4445(1) ; 2260 +к нтж 4414(1), по 2263(1) +к стоп 2260(1), мода +к сч 4412(1), сд 4440(1) ; 2263 +к счмр, нтж 4412(1) +к по 2266(1), стоп 2263(1) +к сч 4412(1), сд 4440(1) ; 2266 +к сч 4412(1), нтж 4412(1) +к по 2271(1), стоп 2266(1) +к сч 4412(1), сд 4440(1) ; 2271 +к сч 4412(1), счмр +к нтж 4412(1), по 2275(1) +к стоп 2271(1), мода +к сч 4412(1), мод 36431 ; 2275 +к нтж, по 2300(1) +к стоп 2275(1), мода +к сч 4446(1), рж 4447(1) ; 2300 +к нтж 4446(1), по 2303(1) +к стоп 2300(1), мода +к сч 4446(1), рж 4450(1) ; 2303 +к нтж 4446(1), по 2306(1) +к стоп 2303(1), мода +к сч 4451(1), рж ; 2306 +к нтж 4451(1), по 2311(1) +к стоп 2306(1), мода +к сч 4422(1), рж 4452(1) ; 2311 +к зп 7777, нтж 4422(1) +к по 2314(1), стоп 2311(1) +к сч 4453(1), ржа ; 2314 +к нтж 4453(1), по 2317(1) +к стоп 2314(1), мода +к сч 4412(1), нтж 4412(1) ; 2317 +к ржа 4, счмр +к нтж 4412(1), по 2323(1) +к стоп 2317(1), мода +к сч 4412(1), нтж 4412(1) ; 2323 +к счрж, счмр +к нтж 4412(1), по 2327(1) +к стоп 2323(1), мода +к ржа 77, сч 4454(1) ; 2327 +к сл 4454(1), ржа +к нтж 4455(1), по 2333(1) +к стоп 2327(1), мода +к сч, ржа 77 ; 2333 +к зп 7774, счрж 77 +к нтж 4456(1), по 2337(1) +к стоп 2333(1), мода +к сч, ржа ; 2337 +к по 2341(1), пб 2342(1) +к стоп 2337(1), мода ; 2341 +к сч 4437(1), ржа 30 ; 2342 +к пе 2344(1), стоп 2342(1) +к сч 4454(1), ржа 10 ; 2344 +к пе 2346(1), стоп 2344(1) +к сч 4412(1), пе 2350(1) ; 2346 +к стоп 2346(1), мода +к сч, счрж 77 ; 2350 +к нтж 4457(1), по 2353(1) +к стоп 2350(1), мода +к сч, сч ; 2353 +к или, счрж 77 +к нтж 4457(1), по 2357(1) +к стоп 2353(1), мода +к сч, слц 4412(1) ; 2357 +к счрж 77, нтж 4460(1) +к по 2362(1), стоп 2357(1) +к сч 4461(1), сч 4461(1) ; 2362 +к сда, сл +к счрж 77, нтж 4462(1) +к по 2366(1), стоп 2362(1) +к сч 4422(1), сд 4422(1) ; 2366 +к счрж 177, нтж 4457(1) +к по 2371(1), стоп 2366(1) +к сч, сч ; 2371 +к рж 4463(1), счрж 37 +к нтж 4463(1), по 2375(1) +к стоп 2371(1), мода +к сч, сч ; 2375 +к рж 4463(1), счрж +к нтж, по 2401(1) +к стоп 2375(1), мода +к сч, сч ; 2401 +к рж, счрж 4463(1) +к нтж, по 2405(1) +к стоп 2401(1), мода +к сч, ржа 77 ; 2405 +к счрж 77, нтж 4456(1) +к по 2410(1), стоп 2405(1) +к сч, ржа ; 2410 +к счрж 77, по 2413(1) +к стоп 2410(1), мода +к сч 4412(1), нтж ; 2413 +к ржа 77, счрж 77 +к нтж 4456(1), по 2417(1) +к стоп 2413(1), мода +к ржа, мода ; 2417 +к сч 4461(1), ржа 40 +к сч 4422(1), сл 4422(1) +к нтж 4454(1), ржа 4 +к по 2424(1), стоп 2420(1) +к сч, сч ; 2424 +к сбр, счрж 77 +к нтж 4457(1), по 2430(1) +к стоп 2424(1), мода +к сч 4422(1), нед 4422(1) ; 2430 +к счрж 177, нтж 4457(1) +к по 2433(1), стоп 2430(1) +к сч 4461(1), сч 4461(1) ; 2433 +к слп, сл +к счрж 77, нтж 4462(1) +к по 2437(1), стоп 2433(1) +к сч 4433(1), слпа 101 ; 2437 +к счрж 177, нтж 4460(1) +к по 2442(1), стоп 2437(1) +к ржа, сч 4433(1) ; 2442 +к слпа 100, счрж 177 +к нтж 4460(1), по 2446(1) +к стоп 2442(1), мода +к сч 4422(1), вчп 4422(1) ; 2446 +к счрж 177, нтж 4460(1) +к по 2451(1), стоп 2446(1) +к сч 4464(1), вчп 4465(1) ; 2451 +к счрж 77, нтж 4460(1) +к по 2454(1), стоп 2451(1) +к сч 4422(1), вчаб 4422(1) ; 2454 +к счрж 177, нтж 4462(1) +к по 2457(1), стоп 2454(1) +к сч 4461(1), сл ; 2457 +к сда, счрж 77 +к нтж 4457(1), по 2463(1) +к стоп 2457(1), мода +к сч 4461(1), знак ; 2463 +к счрж 77, нтж 4462(1) +к по 2466(1), стоп 2463(1) +к сч 4464(1), сл 4465(1) ; 2466 +к счрж 77, нтж 4462(1) +к по 2471(1), стоп 2466(1) +к сч 4466(1), сл ; 2471 +к сда, счмр +к счрж 77, нтж 4457(1) +к по 2475(1), стоп 2471(1) +к сч 4433(1), умн 4433(1) ; 2475 +к счрж 177, нтж 4460(1) +к по 2500(1), стоп 2475(1) +к сч 4433(1), умн 4433(1) ; 2500 +к счрж 177, нтж 4460(1) +к по 2503(1), стоп 2500(1) +к сда 100, по 2505(1) ; 2503 +к стоп 2503(1), мода +к сч 4467(1), сбр 4470(1) ; 2505 +к нтж 4471(1), по 2510(1) +к стоп 2505(1), мода +к сч 4472(1), рзб 4473(1) ; 2510 +к нтж 4474(1), по 2513(1) +к стоп 2510(1), мода +к сч 4412(1), рзб 4437(1) ; 2513 +к нтж 4437(1), по 2516(1) +к стоп 2513(1), мода +к сч 4475(1), нед 4476(1) ; 2516 +к нтж 4477(1), по 2521(1) +к стоп 2516(1), мода +к сч 4475(1), нед 4476(1) ; 2521 +к счмр, нтж 4500(1) +к по 2524(1), стоп 2521(1) +к сч 4426(1), чед 4501(1) ; 2524 +к нтж 4502(1), по 2527(1) +к стоп 2524(1), мода +к сч 4503(1), слп 4504(1) ; 2527 +к нтж 4505(1), по 2532(1) +к стоп 2527(1), мода +к сч 4506(1), слп 4507(1) ; 2532 +к нтж 4433(1), по 2535(1) +к стоп 2532(1), мода +к сч 4461(1), слп ; 2535 +к нтж 4461(1), по 2540(1) +к стоп 2535(1), мода +к сч 4452(1), слп 4510(1) ; 2540 +к нтж 4511(1), по 2543(1) +к стоп 2540(1), мода +к сч 4512(1), слп 4476(1) ; 2543 +к нтж 4513(1), по 2546(1) +к стоп 2543(1), мода +к сч 4423(1), слп 4423(1) ; 2546 +к нтж 4461(1), по 2551(1) +к стоп 2546(1), мода +к сч 4500(1), слп 4433(1) ; 2551 +к нтж 4514(1), по 2554(1) +к стоп 2551(1), мода +к сч 4515(1), слп 4515(1) ; 2554 +к нтж 4506(1), по 2557(1) +к стоп 2554(1), мода +к сч 4476(1), слп 4512(1) ; 2557 +к нтж 4516(1), по 2562(1) +к стоп 2557(1), мода +к сч 4433(1), слпа 177 ; 2562 +к нтж 4422(1), по 2565(1) +к стоп 2562(1), мода +к сч 4453(1), слпа 140 ; 2565 +к нтж 4517(1), по 2570(1) +к стоп 2565(1), мода +к сч 4520(1), вчп 4421(1) ; 2570 +к нтж 4521(1), по 2573(1) +к стоп 2570(1), мода +к сч 4422(1), вчп 4466(1) ; 2573 +к нтж 4433(1), по 2576(1) +к стоп 2573(1), мода +к сч 4522(1), вчп 4523(1) ; 2576 +к нтж 4461(1), по 2601(1) +к стоп 2576(1), мода +к сч 4422(1), вчп 4412(1) ; 2601 +к нтж 4433(1), по 2604(1) +к стоп 2601(1), мода +к сч 4422(1), вчп 4466(1) ; 2604 +к нтж 4433(1), по 2607(1) +к стоп 2604(1), мода +к сч 4506(1), вчп ; 2607 +к нтж 4422(1), по 2612(1) +к стоп 2607(1), мода +к сч 4454(1), вчп 4507(1) ; 2612 +к нтж, по 2615(1) +к стоп 2612(1), мода +к сч 4524(1), вчп ; 2615 +к нтж 4422(1), по 2620(1) +к стоп 2615(1), мода +к сч 4412(1), нтж 4525(1) ; 2620 +к вчпа 101, нтж 4526(1) +к по 2623(1), стоп 2620(1) +к сч 4465(1), сл 4527(1) ; 2623 +к нтж 4530(1), по 2626(1) +к стоп 2623(1), мода +к сч 4531(1), сл 4532(1) ; 2626 +к нтж 4533(1), по 2631(1) +к стоп 2626(1), мода +к сч 4515(1), сл 4534(1) ; 2631 +к нтж 4535(1), по 2634(1) +к стоп 2631(1), мода +к сч 4536(1), сл 4537(1) ; 2634 +к нтж 4461(1), по 2637(1) +к стоп 2634(1), мода +к сч 4471(1), сл 4466(1) ; 2637 +к нтж 4471(1), по 2642(1) +к стоп 2637(1), мода +к сч 4540(1), сл 4541(1) ; 2642 +к нтж 4542(1), по 2645(1) +к стоп 2642(1), мода +к сч 4543(1), сл 4433(1) ; 2645 +к нтж 4544(1), по 2650(1) +к стоп 2645(1), мода +к сч 4545(1), сл 4512(1) ; 2650 +к нтж 4546(1), по 2653(1) +к стоп 2650(1), мода +к сч 4512(1), сл 4512(1) ; 2653 +к нтж 4547(1), по 2656(1) +к стоп 2653(1), мода +к сч 4512(1), сл 4550(1) ; 2656 +к нтж 4551(1), по 2661(1) +к стоп 2656(1), мода +к сч 4527(1), сл 4532(1) ; 2661 +к нтж 4552(1), по 2664(1) +к стоп 2661(1), мода +к сч 4553(1), сл 4554(1) ; 2664 +к нтж 4555(1), по 2667(1) +к стоп 2664(1), мода +к сч 4435(1), сл 4556(1) ; 2667 +к нтж 4557(1), по 2672(1) +к стоп 2667(1), мода +к сч 4560(1), сл 4554(1) ; 2672 +к нтж 4561(1), по 2675(1) +к стоп 2672(1), мода +к сч 4562(1), сл 4512(1) ; 2675 +к нтж 4433(1), по 2700(1) +к стоп 2675(1), мода +к сч 4554(1), сл 4553(1) ; 2700 +к нтж 4555(1), по 2703(1) +к стоп 2700(1), мода +к сч 4554(1), сл 4433(1) ; 2703 +к нтж 4563(1), по 2706(1) +к стоп 2703(1), мода +к сч 4564(1), сл 4565(1) ; 2706 +к нтж 4566(1), по 2711(1) +к стоп 2706(1), мода +к сч 4567(1), сл 4454(1) ; 2711 +к нтж 4570(1), по 2714(1) +к стоп 2711(1), мода +к сч 4571(1), сл 4572(1) ; 2714 +к нтж 4573(1), по 2717(1) +к стоп 2714(1), мода +к сч 4574(1), сл 4575(1) ; 2717 +к нтж 4576(1), по 2722(1) +к стоп 2717(1), мода +к сч 4422(1), сл 4465(1) ; 2722 +к нтж 4577(1), по 2725(1) +к стоп 2722(1), мода +к сч 4422(1), сл 4465(1) ; 2725 +к нтж 4577(1), по 2730(1) +к стоп 2725(1), мода +к сч 4461(1), сл 4600(1) ; 2730 +к нтж 4600(1), по 2733(1) +к стоп 2730(1), мода +к сч 4601(1), сл 4600(1) ; 2733 +к нтж 4600(1), по 2736(1) +к стоп 2733(1), мода +к сч 4602(1), сл 4603(1) ; 2736 +к нтж 4603(1), по 2741(1) +к стоп 2736(1), мода +к сч 4523(1), сл 4532(1) ; 2741 +к счмр 100, нтж 4604(1) +к по 2744(1), стоп 2741(1) +к сч 4564(1), сл 4565(1) ; 2744 +к счмр 100, нтж 4455(1) +к по 2747(1), стоп 2744(1) +к сч 4574(1), сл 4575(1) ; 2747 +к счмр 100, нтж 4455(1) +к по 2752(1), стоп 2747(1) +к сч 4605(1), сл 4606(1) ; 2752 +к счмр 1, нтж 4544(1) +к по 2755(1), стоп 2752(1) +к сч 4541(1), сл 4530(1) ; 2755 +к счмр, по 2760(1) +к стоп 2755(1), мода +к сч 4527(1), сл 4532(1) ; 2760 +к счмр 100, нтж 4604(1) +к по 2763(1), стоп 2760(1) +к сч 4465(1), ржа 2 ; 2763 +к сл 4422(1), нтж 4607(1) +к по 2766(1), стоп 2763(1) +к сч 4461(1), рж 4610(1) ; 2766 +к сч 4512(1), сл 4611(1) +к нтж 4611(1), по 2772(1) +к стоп 2766(1), мода +к ржа, мода ; 2772 +к сч 4612(1), сл 4613(1) +к нтж 4614(1), по 2776(1) +к стоп 2773(1), мода +к сч 4612(1), сл 4615(1) ; 2776 +к нтж 4616(1), по 3001(1) +к стоп 2776(1), мода +к сч 4421(1), сл 4613(1) ; 3001 +к нтж 4617(1), по 3004(1) +к стоп 3001(1), мода +к сч 4421(1), сл 4615(1) ; 3004 +к нтж 4620(1), по 3007(1) +к стоп 3004(1), мода +к сч 4613(1), сл 4612(1) ; 3007 +к нтж 4614(1), по 3012(1) +к стоп 3007(1), мода +к сч 4615(1), сл 4612(1) ; 3012 +к нтж 4616(1), по 3015(1) +к стоп 3012(1), мода +к сч 4613(1), сл 4421(1) ; 3015 +к нтж 4617(1), по 3020(1) +к стоп 3015(1), мода +к сч 4615(1), сл 4421(1) ; 3020 +к нтж 4620(1), по 3023(1) +к стоп 3020(1), мода +к сч 4621(1), сл 4622(1) ; 3023 +к нтж 4623(1), по 3026(1) +к стоп 3023(1), мода +к сч 4624(1), сл 4622(1) ; 3026 +к нтж 4625(1), по 3031(1) +к стоп 3026(1), мода +к сч 4626(1), сл 4622(1) ; 3031 +к нтж 4627(1), по 3034(1) +к стоп 3031(1), мода +к сч 4630(1), сл 4631(1) ; 3034 +к нтж 4630(1), по 3037(1) +к стоп 3034(1), мода +к сч 4630(1), сл 4632(1) ; 3037 +к нтж 4630(1), по 3042(1) +к стоп 3037(1), мода +к сч 4605(1), вч 4433(1) ; 3042 +к нтж 4605(1), по 3045(1) +к стоп 3042(1), мода +к сч 4611(1), вч 4512(1) ; 3045 +к нтж 4544(1), по 3050(1) +к стоп 3045(1), мода +к сч 4612(1), вч 4613(1) ; 3050 +к нтж 4633(1), по 3053(1) +к стоп 3050(1), мода +к сч 4612(1), вч 4615(1) ; 3053 +к нтж 4634(1), по 3056(1) +к стоп 3053(1), мода +к сч 4421(1), вч 4613(1) ; 3056 +к нтж 4635(1), по 3061(1) +к стоп 3056(1), мода +к сч 4421(1), вч 4615(1) ; 3061 +к нтж 4636(1), по 3064(1) +к стоп 3061(1), мода +к сч 4630(1), вч 4631(1) ; 3064 +к нтж 4630(1), по 3067(1) +к стоп 3064(1), мода +к сч 4630(1), вч 4632(1) ; 3067 +к нтж 4630(1), по 3072(1) +к стоп 3067(1), мода +к сч 4526(1), вч 4637(1) ; 3072 +к счмр 100, нтж 4640(1) +к по 3075(1), стоп 3072(1) +к сч 4641(1), вч 4642(1) ; 3075 +к нтж 4643(1), по 3100(1) +к стоп 3075(1), мода +к сч 4641(1), вч 4642(1) ; 3100 +к счмр 100, нтж 4644(1) +к по 3103(1), стоп 3100(1) +к сч 4527(1), вч 4532(1) ; 3103 +к счмр 100, нтж 4645(1) +к по 3106(1), стоп 3103(1) +к сч 4541(1), вч 4530(1) ; 3106 +к счмр, по 3111(1) +к стоп 3106(1), мода +к сч 4412(1), нтж 4412(1) ; 3111 +к сч 4641(1), вч 4642(1) +к счмр 160, нтж 4646(1) +к по 3115(1), стоп 3111(1) +к сч 4454(1), вчоб 4433(1) ; 3115 +к нтж 4647(1), по 3120(1) +к стоп 3115(1), мода +к сч 4454(1), вчоб 4433(1) ; 3120 +к счмр 100, нтж 4650(1) +к по 3123(1), стоп 3120(1) +к сч 4651(1), вчаб 4555(1) ; 3123 +к нтж 4652(1), по 3126(1) +к стоп 3123(1), мода +к сч 4433(1), вчаб 4454(1) ; 3126 +к нтж 4647(1), по 3131(1) +к стоп 3126(1), мода +к сч 4433(1), вчаб 4653(1) ; 3131 +к нтж 4654(1), по 3134(1) +к стоп 3131(1), мода +к сч 4655(1), вчаб 4656(1) ; 3134 +к нтж 4657(1), по 3137(1) +к стоп 3134(1), мода +к сч 4433(1), вчаб 4454(1) ; 3137 +к нтж 4647(1), по 3142(1) +к стоп 3137(1), мода +к сч 4655(1), вчаб 4656(1) ; 3142 +к счмр 100, нтж 4455(1) +к по 3145(1), стоп 3142(1) +к сч 4433(1), вчаб 4454(1) ; 3145 +к счмр 100, нтж 4650(1) +к по 3150(1), стоп 3145(1) +к сч 4452(1), знак ; 3150 +к нтж 4452(1), по 3153(1) +к стоп 3150(1), мода +к сч 4433(1), знак 4437(1) ; 3153 +к нтж 4654(1), по 3156(1) +к стоп 3153(1), мода +к сч 4562(1), умн 4433(1) ; 3156 +к нтж 4660(1), по 3161(1) +к стоп 3156(1), мода +к сч 4661(1), умн 4452(1) ; 3161 +к нтж 4661(1), по 3164(1) +к стоп 3161(1), мода +к сч 4555(1), умн 4653(1) ; 3164 +к нтж 4560(1), по 3167(1) +к стоп 3164(1), мода +к сч 4554(1), умн 4560(1) ; 3167 +к нтж 4554(1), по 3172(1) +к стоп 3167(1), мода +к сч 4555(1), умн 4662(1) ; 3172 +к нтж 4663(1), по 3175(1) +к стоп 3172(1), мода +к сч 4664(1), умн 4560(1) ; 3175 +к нтж 4665(1), по 3200(1) +к стоп 3175(1), мода +к сч 4413(1), умн 4662(1) ; 3200 +к нтж 4666(1), по 3203(1) +к стоп 3200(1), мода +к сч 4414(1), умн 4662(1) ; 3203 +к нтж 4667(1), по 3206(1) +к стоп 3203(1), мода +к сч 4466(1), умн 4512(1) ; 3206 +к нтж 4461(1), по 3211(1) +к стоп 3206(1), мода +к сч 4670(1), умн 4543(1) ; 3211 +к нтж 4671(1), по 3214(1) +к стоп 3211(1), мода +к сч 4672(1), умн 4543(1) ; 3214 +к нтж 4673(1), по 3217(1) +к стоп 3214(1), мода +к сч 4674(1), умн 4675(1) ; 3217 +к нтж 4676(1), по 3222(1) +к стоп 3217(1), мода +к сч 4677(1), умн 4700(1) ; 3222 +к нтж 4701(1), по 3225(1) +к стоп 3222(1), мода +к сч 4702(1), умн 4543(1) ; 3225 +к нтж 4703(1), по 3230(1) +к стоп 3225(1), мода +к сч 4704(1), умн 4433(1) ; 3230 +к нтж 4705(1), по 3233(1) +к стоп 3230(1), мода +к сч 4615(1), умн 4706(1) ; 3233 +к нтж 4707(1), по 3236(1) +к стоп 3233(1), мода +к сч 4710(1), умн 4615(1) ; 3236 +к нтж 4711(1), по 3241(1) +к стоп 3236(1), мода +к сч 4712(1), умн 4615(1) ; 3241 +к нтж 4647(1), по 3244(1) +к стоп 3241(1), мода +к сч 4712(1), умн 4636(1) ; 3244 +к нтж 4713(1), по 3247(1) +к стоп 3244(1), мода +к сч 4714(1), умн 4560(1) ; 3247 +к нтж 4714(1), по 3252(1) +к стоп 3247(1), мода +к сч 4715(1), умн 4716(1) ; 3252 +к нтж 4717(1), по 3255(1) +к стоп 3252(1), мода +к сч 4720(1), умн 4721(1) ; 3255 +к нтж 4722(1), по 3260(1) +к стоп 3255(1), мода +к сч 4454(1), умн 4452(1) ; 3260 +к нтж 4454(1), по 3263(1) +к стоп 3260(1), мода +к сч 4723(1), умн 4554(1) ; 3263 +к нтж 4724(1), по 3266(1) +к стоп 3263(1), мода +к сч 4555(1), умн 4653(1) ; 3266 +к счмр 100, нтж 4461(1) +к по 3271(1), стоп 3266(1) +к сч 4554(1), умн 4560(1) ; 3271 +к счмр 100, нтж 4544(1) +к по 3274(1), стоп 3271(1) +к сч 4555(1), умн 4662(1) ; 3274 +к счмр 100, нтж 4662(1) +к по 3277(1), стоп 3274(1) +к сч 4413(1), умн 4662(1) ; 3277 +к счмр 100, нтж 4725(1) +к по 3302(1), стоп 3277(1) +к сч 4672(1), умн 4662(1) ; 3302 +к счмр, нтж 4726(1) +к по 3305(1), стоп 3302(1) +к сч 4414(1), умн 4662(1) ; 3305 +к счмр, нтж 4727(1) +к по 3310(1), стоп 3305(1) +к сч 4614(1), умн 4662(1) ; 3310 +к счмр 100, нтж 4730(1) +к по 3313(1), стоп 3310(1) +к сч 4731(1), умн 4662(1) ; 3313 +к счмр, нтж 4732(1) +к по 3316(1), стоп 3313(1) +к ржа 1, сч 4702(1) ; 3316 +к умн 4543(1), счмр 100 +к нтж 4733(1), по 3322(1) +к стоп 3316(1), мода +к ржа, сч 4714(1) ; 3322 +к умн 4542(1), счмр 100 +к нтж 4734(1), по 3326(1) +к стоп 3322(1), мода +к сч 4735(1), умн 4716(1) ; 3326 +к счмр 100, нтж 4736(1) +к по 3331(1), стоп 3326(1) +к сч 4720(1), умн 4721(1) ; 3331 +к счмр, нтж 4737(1) +к по 3334(1), стоп 3331(1) +к сч 4704(1), умн 4433(1) ; 3334 +к счмр 100, нтж +к по 3337(1), стоп 3334(1) +к сч 4615(1), умн 4706(1) ; 3337 +к счмр 100, нтж 4740(1) +к по 3342(1), стоп 3337(1) +к сч 4714(1), умн 4560(1) ; 3342 +к счмр 100, нтж 4741(1) +к по 3345(1), стоп 3342(1) +к сч 4715(1), умн 4716(1) ; 3345 +к счмр 100, нтж 4742(1) +к по 3350(1), стоп 3345(1) +к сч 4720(1), умн 4721(1) ; 3350 +к счмр 100, нтж 4743(1) +к по 3353(1), стоп 3350(1) +к сч 4561(1), умн 4561(1) ; 3353 +к счмр, нтж 4461(1) +к по 3356(1), стоп 3353(1) +к сч 4561(1), умн 4563(1) ; 3356 +к счмр 77, нтж 4433(1) +к по 3361(1), стоп 3356(1) +к ржа 1, сч 4512(1) ; 3361 +к умн 4512(1), нтж 4512(1) +к по 3364(1), стоп 3361(1) +к сч 4744(1), умн 4512(1) ; 3364 +к нтж 4512(1), по 3367(1) +к стоп 3364(1), мода +к сч 4745(1), умн 4512(1) ; 3367 +к нтж 4512(1), по 3372(1) +к стоп 3367(1), мода +к сч 4746(1), умн 4512(1) ; 3372 +к нтж 4512(1), по 3375(1) +к стоп 3372(1), мода +к сч 4507(1), умн 4507(1) ; 3375 +к нтж 4747(1), по 3400(1) +к стоп 3375(1), мода +к ржа 2, сч 4723(1) ; 3400 +к умн 4554(1), нтж 4724(1) +к по 3403(1), стоп 3400(1) +к ржа, мода ; 3403 +к сч 4750(1), дел 4452(1) +к нтж 4750(1), по 3407(1) +к стоп 3404(1), мода +к сч 4751(1), дел 4751(1) ; 3407 +к нтж 4452(1), по 3412(1) +к стоп 3407(1), мода +к сч 4751(1), дел 4752(1) ; 3412 +к нтж 4653(1), по 3415(1) +к стоп 3412(1), мода +к сч 4452(1), дел 4653(1) ; 3415 +к нтж 4653(1), по 3420(1) +к стоп 3415(1), мода +к сч 4561(1), дел 4553(1) ; 3420 +к нтж 4653(1), по 3423(1) +к стоп 3420(1), мода +к сч 4753(1), дел 4751(1) ; 3423 +к нтж 4754(1), по 3426(1) +к стоп 3423(1), мода +к сч 4753(1), дел 4653(1) ; 3426 +к нтж 4755(1), по 3431(1) +к стоп 3426(1), мода +к сч 4756(1), дел 4757(1) ; 3431 +к нтж 4760(1), по 3434(1) +к стоп 3431(1), мода +к сч 4761(1), дел 4762(1) ; 3434 +к нтж 4763(1), по 3437(1) +к стоп 3434(1), мода +к сч 4764(1), дел 4765(1) ; 3437 +к нтж 4766(1), по 3442(1) +к стоп 3437(1), мода +к сч 4767(1), дел 4770(1) ; 3442 +к нтж 4653(1), по 3445(1) +к стоп 3442(1), мода +к сч 4446(1), дел 4771(1) ; 3445 +к нтж 4653(1), по 3450(1) +к стоп 3445(1), мода +к сч 4771(1), дел 4452(1) ; 3450 +к нтж 4771(1), по 3453(1) +к стоп 3450(1), мода +к сч 4753(1), дел 4772(1) ; 3453 +к нтж 4773(1), по 3456(1) +к стоп 3453(1), мода +к сч 4774(1), сда 103 ; 3456 +к пе 3460(1), пб 3461(1) +к стоп 3456(1), мода ; 3460 +к сч 4774(1), по 103 ; 3461 +к по 3463(1), пб 3464(1) +к стоп 3461(1), мода ; 3463 +к сч 4542(1), сл 4560(1) ; 3464 +к пе 3467(1), нтж 4775(1) +к по 3470(1), стоп 3464(1) +к стоп 33464(17), мода ; 3467 +к сч 4776(1), сл 4554(1) ; 3470 +к по 3473(1), нтж 4777(1) +к по 3474(1), мода +к стоп 3470(1), мода ; 3473 +к сч 4542(1), зп 7777 ; 3474 +к сч 4560(1), зп 7776 +к сч 7777, сл 7776 +к сда 31, сл 4726(1) +к нтж 5000(1), по 3502(1) +к стоп 3474(1), мода +к сч 7777, сл 7776 ; 3502 +к сда 157, нед 4412(1) +к нтж 4637(1), по 3506(1) +к стоп 3502(1), мода +к сч 7777, сл 7776 ; 3506 +к сда 32, вчаб 5001(1) +к нтж 5002(1), по 3512(1) +к стоп 3506(1), мода +к сч 7777, сл 7776 ; 3512 +к сда 31, вчаб 4452(1) +к нтж 4555(1), по 3516(1) +к стоп 3512(1), мода +к сч 7777, сл 7776 ; 3516 +к сда 31, вч 4452(1) +к нтж 4555(1), по 3522(1) +к стоп 3516(1), мода +к сч 5003(1), сда 77 ; 3522 +к сл 4651(1), нтж 4525(1) +к по 3525(1), стоп 3522(1) +к сч 4466(1), сл 4461(1) ; 3525 +к сч 4554(1), сл 4553(1) +к сл 4512(1), нтж 5004(1) +к по 3531(1), стоп 3525(1) +к сч 4542(1), сл 4433(1) ; 3531 +к сч 4412(1), слц 4424(1) +к нтж 4424(1), по 3535(1) +к стоп 3531(1), мода +к сч 5005(1), сл 4433(1) ; 3535 +к сда 30, нтж 4437(1) +к по 3540(1), стоп 3535(1) +к сч 5005(1), сл 4433(1) ; 3540 +к слц 4423(1), и 4412(1) +к нтж 4541(1), по 3544(1) +к стоп 3540(1), мода +к сч 5005(1), сл 4433(1) ; 3544 +к слц 4423(1), нтж 4541(1) +к по 3547(1), стоп 3544(1) +к сч 5005(1), сл 4433(1) ; 3547 +к сч 5006(1), сд 5007(1) +к знак 4437(1), нтж 4654(1) +к по 3553(1), стоп 3547(1) +к сч 5005(1), сл 4433(1) ; 3553 +к сч 4452(1), умн 5010(1) +к умн 4452(1), нтж 5010(1) +к по 3557(1), стоп 3553(1) +к сч 5005(1), сл 4433(1) ; 3557 +к сч 4424(1), сда 31 +к дел 4454(1), нтж 4452(1) +к по 3563(1), стоп 3557(1) +к сч 5011(1), зп 7777 ; 3563 +к сч 4560(1), зп 7776 +к сч 4750(1), зп 7775 +к сч 7777, сл 7776 +к сда, сч 7775 +к сл 4461(1), нтж 7775 +к по 3572(1), стоп 3563(1) +к сч 7777, сл 7776 ; 3572 +к сда, сл 7775 +к вч 4461(1), нтж 7775 +к по 3576(1), стоп 3572(1) +к сч 5012(1), зп 7775 ; 3576 +к сч 7777, сл 7776 +к вч 7775, вчаб 7775 +к нтж 5013(1), по 3603(1) +к стоп 3576(1), мода +к сч 7777, сл 7776 ; 3603 +к вч 7775, вчоб 5014(1) +к нтж 5015(1), по 3607(1) +к стоп 3603(1), мода +к сч 4412(1), зп 7775 ; 3607 +к сч 7777, сл 7776 +к и 4412(1), счмр +к по 3613(1), стоп 3607(1) +к сч 7777, сл 7776 ; 3613 +к рзб 7775, зп 7774 +к нтж 5014(1), по 3617(1) +к стоп 3613(1), мода +к сч 7777, сл 7776 ; 3617 +к рзб 7775, уи 5 +к нтж 7774, по 3623(1) +к стоп 3617(1), мода +к сч 7775, зп 7774 ; 3623 +к сч 7777, сл 7776 +к зп 7774, сч 7774 +к нтж 5014(1), по 3630(1) +к стоп 3623(1), мода +к сч 7777, сл 7776 ; 3630 +к и 7775, нтж 7774 +к по 3633(1), стоп 3630(1) +к сч 7777, сл 7776 ; 3633 +к и 7775, слц 7775 +к нтж 7774, по 3637(1) +к стоп 3633(1), мода +к сч 7777, сл 7776 ; 3637 +к или 7775, нтж 7775 +к по 3642(1), стоп 3637(1) +к сч 7777, сл 7776 ; 3642 +к сч 7775, и 7775 +к нтж 7775, по 3646(1) +к стоп 3642(1), мода +к сч 4437(1), зп 7774 ; 3646 +к сч 7777, сл 7776 +к слц 7775, знак 7774 +к нтж 5005(1), по 3653(1) +к стоп 3646(1), мода +к сч 7777, сл 7776 ; 3653 +к и, или 7775 +к нтж 7775, по 3657(1) +к стоп 3653(1), мода +к сч 4433(1), зп 7773 ; 3657 +к сч 7777, сл 7776 +к знак 7774, дел 7773 +к нтж 4542(1), по 3664(1) +к стоп 3657(1), мода +к сч 4452(1), зп 7773 ; 3664 +к сч 7777, сл 7776 +к слц 7775, умн 7773 +к нтж 5014(1), по 3671(1) +к стоп 3664(1), мода +к сч 7777, сл 7776 ; 3671 +к вчпа 146, слпа 146 +к нтж 5014(1), по 3675(1) +к стоп 3671(1), мода +к сч 4560(1), зп 7774 ; 3675 +к сч 7777, сл 7776 +к сл 7774, счмр 117 +к нтж 5016(1), по 3702(1) +к стоп 3675(1), мода +к сч 5017(1), зп 7774 ; 3702 +к сч 7777, сл 7776 +к сл 7774, рж +к нтж 4670(1), по 3707(1) +к стоп 3702(1), мода +к сч 5020(1), зп 7774 ; 3707 +к сч 7777, сл 7776 +к слп 7773, вчп 7774 +к нтж 4555(1), по 3714(1) +к стоп 3707(1), мода +к сч 4464(1), зп 7774 ; 3714 +к сч 7777, сл 7776 +к слц 7775, сд 7774 +к нтж 5021(1), по 3721(1) +к стоп 3714(1), мода +к сч 4433(1), зп 7774 ; 3721 +к сч 7777, сл 7776 +к слп 7774, счрж 177 +к нтж 4460(1), по 3726(1) +к стоп 3721(1), мода +к сч 5022(1), зп 7773 ; 3726 +к сч 7777, сл 7776 +к чед 7773, сбр 7775 +к нтж 5023(1), по 3733(1) +к стоп 3726(1), мода +к сч 4543(1), зп 7773 ; 3733 +к сч 7777, сл 7776 +к слп 7774, рзб 7775 +к нтж 5014(1), по 3740(1) +к стоп 3733(1), мода +к сч 7777, сл 7776 ; 3740 +к чед, нед +к нтж 5024(1), по 3744(1) +к стоп 3740(1), мода +к сч 7777, сл 7776 ; 3744 +к сбр 7775, чед +к нтж 4477(1), по 3750(1) +к стоп 3744(1), мода +к сч 7777, сл 7776 ; 3750 +к слц 7775, чед +к нтж 4477(1), по 3754(1) +к стоп 3750(1), мода +к сч 5014(1), зп 7774 ; 3754 +к сч 7777, сл 7776 +к нтж 7774, по 3760(1) +к стоп 3754(1), мода +к сч 5025(1), зп 7773 ; 3760 +к сч 7777, сл 7776 +к умн 7773, уи 5 +к счи 5, нтж 5026(1) +к по 3765(1), стоп 3760(1) +к сч 5027(1), зп 7773 ; 3765 +к сч 7777, сл 7776 +к сбр 7775, мод 7773 +к нтж, по 3772(1) +к стоп 3765(1), мода +к сч 7777, сл 7776 ; 3772 +к сда 151, и 7775 +к нтж 5030(1), по 3776(1) +к стоп 3772(1), мода +к сч 7777, сл 7776 ; 3776 +к и 7775, слц 7775 +к нтж 5014(1), по 4002(1) +к стоп 3776(1), мода +к сч 7775, мод 5027(1) ; 4002 +к зп, сч 7774 +к нтж 7775, по 4006(1) +к стоп 4002(1), мода +к сч 4466(1), зп 7774 ; 4006 +к сч 7774, сл +к сч 5026(1), слц 4424(1) +к слп 7775, нтж 5031(1) +к по 4013(1), стоп 4006(1) +к сч 7774, сл ; 4013 +к сч 5032(1), слц 4424(1) +к умн 7775, нтж 5033(1) +к по 4017(1), стоп 4013(1) +к сч 4532(1), слп 4421(1) ; 4017 +к слц 4437(1), нтж 5034(1) +к по 4022(1), стоп 4017(1) +к сч 7774, сл 4461(1) ; 4022 +к сч 5035(1), слц 4424(1) +к вчп 4451(1), нтж 5003(1) +к по 4026(1), стоп 4022(1) +к сч 7774, сл 4461(1) ; 4026 +к сч 5035(1), слц 4424(1) +к дел 4505(1), нтж 5036(1) +к по 4032(1), стоп 4026(1) +к слп 4412(1), нтж 4461(1) ; 4032 +к по 4034(1), стоп 4026(1) +к вчп 4412(1), нтж 4461(1) ; 4034 +к по 4036(1), стоп 4034(1) +к сч 4532(1), вчп 4421(1) ; 4036 +к слц 4437(1), нтж 5037(1) +к по 4041(1), стоп 4036(1) +к сч 7774, сл 4461(1) ; 4041 +к сч 5040(1), сда 32 +к сл 4532(1), нтж 4533(1) +к по 4045(1), стоп 4041(1) +к сч 7774, сл 4461(1) ; 4045 +к сч 5040(1), сда 32 +к знак 4437(1), нтж 5041(1) +к по 4051(1), стоп 4045(1) +к сч 7777, сл 7776 ; 4051 +к сда, слц 7775 +к нтж 7775, по 4055(1) +к стоп 4051(1), мода +к сч 7777, сл 7776 ; 4055 +к слц 7775, сда 50 +к нтж 5042(1), по 4061(1) +к стоп 4055(1), мода +к сч 7777, сл 7776 ; 4061 +к сда 100, сл 5043(1) +к нтж 5044(1), по 4065(1) +к стоп 4061(1), мода +к сч 7777, сл 7776 ; 4065 +к сбр 7775, сл 5043(1) +к нтж 5044(1), по 4071(1) +к стоп 4065(1), мода +к ржа 1, сч 7774 ; 4071 +к сл, сда 177 +к сл, нтж +к по 4075(1), стоп 4071(1) +к сч 7774, сл 4461(1) ; 4075 +к сч 4552(1), сда 111 +к умн 5020(1), счмр 100 +к нтж 5045(1), по 4102(1) +к стоп 4075(1), мода +к ржа, сч 4452(1) ; 4102 +к сл 4421(1), сбр 4476(1) +к нтж 5046(1), по 4106(1) +к стоп 4102(1), мода +к сч 4433(1), сл 4433(1) ; 4106 +к сда 161, по 4111(1) +к стоп 4106(1), мода +к ржа 1, сч 4542(1) ; 4111 +к сл 4433(1), сч 4461(1) +к по 4114(1), стоп 4111(1) +к ржа, сч 4653(1) ; 4114 +к зп 7776, сч 4651(1) +к зп 7777, сч 7776 +к сл 7777, сч +к по 4121(1), стоп 4114(1) +к сч 4466(1), сл ; 4121 +к сч 7776, сл 7776 +к сч, по 4125(1) +к стоп 4121(1), мода +к сч 4466(1), сл 4461(1) ; 4125 +к сч 4542(1), сл 5020(1) +к сч 4461(1), по 4131(1) +к стоп 4125(1), мода +к сч 4466(1), сл 4461(1) ; 4131 +к сч 4500(1), сл 5047(1) +к сч 4461(1), по 4135(1) +к стоп 4131(1), мода +к сч 5011(1), зп 7777 ; 4135 +к сч 4560(1), зп 7776 +к сч 5020(1), зп 7774 +к сч 7777, сл 7776 +к сл 7774, ржа 77 +к нтж 4712(1), по 4144(1) +к стоп 4135(1), мода +к ржа, мода ; 4144 +к сч 5011(1), зп 7777 +к сч 4560(1), зп 7776 +к сч 7777, сл 7776 +к сда 20, счрж 77 +к счмр, нтж 5014(1) +к по 4153(1), стоп 4145(1) +к ржа, уиа -20(4) ; 4153 +к сч, уиа (5) +к уиа -1(2), уиа -3(3) +к сл 4466(1), счи 5 +к зп 7773(3), счи 2 +к зп 7777(3), цикл 34156(3) +к уиа -3(3), мода +к сч, сч +к сч, сч 7773(3) +к по 4165(1), стоп 34162(3) +к цикл 34162(3), уиа -3(3) ; 4165 +к сч, сч +к сч 7777(3), нтж 5026(1) +к по 4171(1), стоп 34166(3) +к цикл 34166(3), уиа -3(3) ; 4171 +к сч 4412(1), и +к по 4174(1), стоп 4172(1) +к цикл 34172(3), мода ; 4174 +к уиа -7(5), уиа -10(2) +к слиа 1(2), цикл 34176(5) +к пио 34200(2), стоп 4175(1) +к сч, уи 5 +к пио 34202(5), стоп 4200(1) +к сч 5026(1), уи 5 +к слиа 1(5), пио 34205(5) +к стоп 4202(1), мода +к уиа -7(2), уиа (5) +к счи 5, нтж +к по 4211(1), стоп 4205(1) +к цикл 34206(2), мода +к уиа -7(2), уиа -1(5) ; 4211 +к счи 5, нтж 5026(1) +к по 4215(1), стоп 4211(1) +к цикл 34212(2), мода +к мода -1, сч 6431(1) ; 4215 +к нтж 4412(1), по 4220(1) +к стоп 4215(1), мода +к уиа 77000(5), уиа 1(2) ; 4220 +к мода (2), мода 777(5) +к сч 6430(1), нтж 4412(1) +к по 4224(1), стоп 4220(1) +к мод 5026(1), сч 6431(1) ; 4224 +к нтж 4412(1), по 4227(1) +к стоп 4224(1), мода +к мод 5026(1), уиа (5) ; 4227 +к счи 5, нтж 5026(1) +к по 4232(1), стоп 4227(1) +к уиа (5), пио 34234(5) ; 4232 +к стоп 4232(1), мода +к пино 34235(5), пб 4236(1) +к стоп 4232(1), мода +к уиа -1(5), пино 34240(5) ; 4236 +к стоп 4232(1), мода +к пио 34241(5), пб 4242(1) +к стоп 4232(1), мода +к уиа 1(5), уиа -17(2) ; 4242 +к пино 34245(5), пио 34247(2) ; 4243 +к стоп 4242(1), мода +к сли 5(5), слиа 1(2) +к пб 4243(1), мода +к сч, уиа -1(5) +к уи 5, пио 34252(5) +к стоп 4247(1), мода +к сч 5026(1), счи 5 +к по 4254(1), стоп 4252(1) +к сч 5026(1), уи 5 ; 4254 +к слиа 1(5), пио 34257(5) +к стоп 4254(1), мода +к сч, уиа -1(5) +к мод 5026(1), уи 6 +к пио 34262(5), стоп 4257(1) +к сч 6430(1), уиа (5) +к мод, уи 5 +к счи 5, нтж 5026(1) +к по 4266(1), стоп 4262(1) +к пв 34267(5), мода ; 4266 +к счи 5, нтж 6434(1) ; 4267 +к по 4271(1), стоп 4266(1) +к зп, пв 34272(5) ; 4271 +к счи 5, нтж 6435(1) ; 4272 +к по 4274(1), стоп 4271(1) +к сч 5050(1), мода 70000 ; 4274 +к зп 7777, мода +к сч, уи 17 +к сч (17), нтж 5050(1) +к по 4301(1), стоп 4274(1) +к счи 17, нтж 5026(1) ; 4301 +к по 4303(1), стоп 4276(1) +к сч 4412(1), зп (17) ; 4303 +к сч -1, нтж 4412(1) +к по 4306(1), стоп 4303(1) +к счи 17, по 4310(1) ; 4306 +к стоп 4306(1), мода +к сч, зп 100 ; 4310 +к мода 40000, зп +к уиа 1(5), мода +к уиа (17), мода (5) +к сч (17), счи 17 +к по 4316(1), стоп 4310(1) +к сли 5(5), пино 34313(5) ; 4316 +к сч 5026(1), уи 17 +к сч 4412(1), счм 4413(1) +к нтж 4413(1), по 4323(1) +к стоп 4310(1), мода +к сч -1, нтж 4412(1) ; 4323 +к по 4325(1), стоп 4310(1) +к сч 4413(1), зпм -2 ; 4325 +к нтж 4412(1), по 4330(1) +к стоп 4325(1), мода +к сч -2, нтж 4413(1) ; 4330 +к по 4332(1), стоп 4325(1) +к счи 17, нтж 5026(1) ; 4332 +к по 4334(1), стоп 4325(1) +к уиа -1(5), сч 4413(1) ; 4334 +к счим 5, нтж 5026(1) +к по 4337(1), стоп 4334(1) +к сч -1, нтж 4413(1) ; 4337 +к по 4341(1), стоп 4334(1) +к нтж 4414(1), уиа (5) ; 4341 +к уим 5, нтж 4413(1) +к по 4344(1), стоп 4341(1) +к счи 5, нтж 5051(1) ; 4344 +к по 4346(1), стоп 4341(1) +к уиа (5), слиа 1(17) ; 4346 +к пио 34350(17), стоп 4341(1) +к сч 6433(1), уи 17 +к уим 3, нтж 6432(1) +к по 4353(1), стоп 4350(1) +к уиа (17), сч 5026(1) ; 4353 +к зп -1, мод (17) +к уиа (5), слиа 1(5) +к пио 34357(5), стоп 4353(1) +к сч 4412(1), зп -1 +к сч 4414(1), зп -2 +к сч, уиа -1(17) +к зпм (17), нтж -2 +к по 4364(1), стоп 4366(1) +к сч -1, по 4366(1) ; 4364 +к стоп 4357(1), мода +к сч 4412(1), зп -2 ; 4366 +к зп -1, сч +к уиа -2(17), счм (17) +к нтж 4412(1), по 4373(1) +к стоп 4366(1), мода +к сч -2, по 4375(1) ; 4373 +к стоп 4366(1), мода +к уиа 17(17), сч 5026(1) ; 4375 +к уим (17), нтж 4412(1) +к по 4400(1), стоп 4375(1) +к счи 17, нтж 5026(1) ; 4400 +к по 4402(1), стоп 4375(1) +к уиа -2(17), сч 4412(1) ; 4402 +к счим 20(17), нтж 5026(1) +к по 4405(1), стоп 4375(1) +к сч -2, нтж 4412(1) ; 4405 +к по 35052, стоп 4375(1) +к пб 35052, мода +с 0000000007700000 +с 0000000000010177 +с 7777777777777777 +с 2525252525252525 +с 5252525252525252 +с 4021042104210421 +с 4010421042104210 +с 4004210421042104 +с 4002104210421042 +с 4077777777777777 +с 7750000000000000 +с 0037777777777777 +с 0000000000000001 +с 0000000000000002 +с 5125252525252525 +с 7767777777777777 +с 1775777777777777 +с 7737777777777777 +с 7677777777777776 +с 4010000000000000 +с 2525252500000000 +с 4000000000000000 +с 2525252525252524 +с 0020000000000000 +с 7000000000000000 +с 3725252525252525 +с 5252525252525250 +с 3712525252525252 +с 3765252525252525 +с 3752525252525252 +с 4114000000000000 +с 0057000000000000 +с 1717000000000000 +с 1760000000000000 +с 4050000000000000 +с 7740000000000001 +с 0010000000000000 +с 0050000000000000 +с 3740000000000000 +с 0200000000000000 +с 0400000000000000 +с 0000000000000000 +с 1000000000000000 +с 1740000000000000 +с 5710000000000000 +с 7710000000000001 +с 7740000000000000 +с 4020000077777765 +с 4020000000000677 +с 7724000000000000 +с 4777000000000000 +с 5252524000000000 +с 4052524000000000 +с 0000001703000000 +с 7777777777777760 +с 0000000000000006 +с 7030000000000000 +с 7777777777777776 +с 0000000000000027 +с 0044000000000000 +с 5777777777777777 +с 1750000000000000 +с 3750000000000000 +с 4040000000000000 +с 4100000000000001 +с 4150000000000000 +с 4000000000000001 +с 5410000000000000 +с 6760000000000000 +с 4004000000000000 +с 5560000000000000 +с 7410000000000000 +с 7760000000000000 +с 7720000000000000 +с 3744000000000000 +с 7744000000000000 +с 4042000000000000 +с 6467777777777777 +с 1250000000000000 +с 7710000000000000 +с 7750000000000001 +с 5710000000000001 +с 5757777777777777 +с 6011777777777777 +с 4002000000000000 +с 3754000000000000 +с 0003777777777777 +с 0002000000000000 +с 4030000000000000 +с 6410000000000000 +с 6357777777777777 +с 4037777777777777 +с 3757777777777776 +с 7700000000001000 +с 6010000000000001 +с 1510000000000000 +с 4140400000000000 +с 3750000000000002 +с 7710000000000777 +с 4064000000000000 +с 4010000000000001 +с 4020000000000001 +с 3740000000000002 +с 1450000000000000 +с 4017777777777777 +с 4054000000000000 +с 4007777777777777 +с 4050000000000001 +с 0054000000000000 +с 0010000000000001 +с 0110000000000001 +с 0110000000000000 +с 0112000000000000 +с 0201000000000000 +с 0010000000000010 +с 0054000000000004 +с 0410000000000000 +с 0030000000000001 +с 0357740000000001 +с 7754000000000001 +с 0027777777777777 +с 0040000000000000 +с 4700000000000000 +с 0014000000000000 +с 7717777777777000 +с 7717777777777777 +с 5357777777777777 +с 7754000000000000 +с 0140000000000000 +с 4044000000000000 +с 4137777777777777 +с 4025252525252525 +с 4025252525252521 +с 4012525252525252 +с 4012525252525246 +с 4025252525252523 +с 4012525252525250 +с 4037677777777776 +с 6400000000000000 +с 3527777777777600 +с 4037777577777776 +с 3127777777700000 +с 4037777777377776 +с 2527777740000000 +с 5367777777777777 +с 4340000000000000 +с 4300000000000000 +с 4012525252525247 +с 4025252525252522 +с 4012525252525251 +с 4025252525252524 +с 0000000000000060 +с 1217777500000000 +с 1010000000000000 +с 0030000000000004 +с 1010000037777777 +с 1017777400000000 +с 6010000000000000 +с 4017777400000000 +с 3757777777777777 +с 3757777777600000 +с 6420000000000000 +с 6417777777777777 +с 4020000000000000 +с 3760000000000000 +с 2020000000000000 +с 0020000000000001 +с 2017777777777401 +с 3717777777777776 +с 4020077451545465 +с 4017777777777776 +с 4020000000000003 +с 4025000000000000 +с 4025000000000001 +с 2525252525252527 +с 5252525252525251 +с 4024000000000000 +с 1414000000000000 +с 4020000000000005 +с 1417777777777773 +с 7740000000000005 +с 4037777777777776 +с 3166000000000000 +с 7740000000000011 +с 4037777777777756 +с 3365700000000000 +с 4010000000000025 +с 1427777777777753 +с 4023733455635120 +с 3763733455635120 +с 4011000000000000 +с 3753777777777777 +с 4006000000000000 +с 3717777777777777 +с 4014000000000000 +с 4027777777777777 +с 4010007270374325 +с 4010465735157101 +с 4017035107377453 +с 4010062167461731 +с 4010003737062525 +с 4017776537664567 +с 4010003216547543 +с 4010000002602602 +с 3750000002602603 +с 2452525252525254 +с 0017777777777766 +с 1252525252525254 +с 3752525252525274 +с 4012525252525241 +с 0012525252525276 +с 4007777777777753 +с 6317761217007126 +с 4010465771157101 +с 3757366630423726 +с 0015210643373603 +с 3710000000000000 +с 3757761217007126 +с 3753416060423726 +с 4015210643373603 +с 4000000000000002 +с 4000000000000004 +с 4000000000000010 +с 4100000000000000 +с 4057777777777777 +с 4217000000000000 +с 4221000000000000 +с 4353161703700000 +с 4153760742000000 +с 4364616074100000 +с 4050003216547543 +с 4057776537664567 +с 4010003737062524 +с 4027000000000000 +с 4015000000000000 +с 4024730473047305 +с 4026736000000000 +с 4011434000000000 +с 4020642511033154 +с 4017000000000000 +с 4021000000000000 +с 4124000000000000 +с 4060000000000000 +с 4324616074100000 +с 0000000000000007 +с 6410000000000001 +с 6376777777777777 +с 6160000000000001 +с 0053777777777773 +с 0020000000000077 +с 0024000000000077 +с 2010000000000000 +с 4020000000000002 +с 6317777777777777 +с 0000000000000401 +с 1460000000000000 +с 0052525252525252 +с 6367777777777777 +с 6310000000000000 +с 6317777777777776 +с 6320000000000001 +с 6257777777777776 +с 7257777777777774 +с 6347777777777776 +с 6350000000000001 +с 0000000000632000 +с 7777777777777774 +с 0000000000000003 +с 0000000000000056 +с 4011000000000001 +с 0000000000077777 +с 0000000000007774 +с 0000000000000146 +с 2350000000000000 +с 0000000037777777 +с 0320000000000000 +с 6037777777777777 +с 1500000000007777 +с 2050000000000000 +с 5737777777777777 +с 0000000000001371 +с 5660000000000000 +с 0000000100000000 +с 6320000000777777 +с 6360000000400000 +с 2347710000000000 +с 4017777777777760 +с 7020000000000000 +с 0330000003300000 +с 0000000000025252 +к уиа 30000(1), мода ; 5052 +к уиа -60(5), сч +к зп 7777, сч 6440(1) +к слц 7777, зп 7777 +к нтж, пе 5060(1) +к стоп 5053(1), мода +к цикл 35055(5), мода ; 5060 +к уиа -57(5), сч 6440(1) +к пе 5063(1), стоп 5061(1) +к сда 77, цикл 35062(5) ; 5063 +к по 5065(1), стоп 5061(1) +к уиа 77661(4), уиа 77660(5) ; 5065 +к счи 5, сда 27 +к сл 6441(1), нтж 6441(1) +к по 5071(1), стоп 5065(1) +к слиа 1(5), цикл 35066(4) ; 5071 +к уиа 77661(4), уиа 77660(5) +к счи 5, сда 27 +к зп 7773, сч 6441(1) +к сл 7773, нтж 6441(1) +к по 5077(1), стоп 5072(1) +к слиа 1(5), цикл 35073(4) ; 5077 +к уиа 77663(4), уиа 77662(5) +к счи 5, сда 27 +к сл 6442(1), нтж 6442(1) +к по 5104(1), стоп 5100(1) +к слиа 1(5), цикл 35101(4) ; 5104 +к уиа -7(4), уиа -3(2) +к сч 6443(1), мода +к зп 6537(1), нтж 6444(1) +к зп 6524(1), сч +к зп 6533(1), мода +к сч 6444(1), нтж 6537(1) +к сл 6533(1), нтж 6524(1) +к по 5115(1), стоп 5114(1) +к сч 6533(1), слц 6445(1) ; 5115 +к зп 6533(1), цикл 35112(4) +к уиа -7(4), сч 6537(1) +к сда 101, цикл 35107(2) +к уиа -57(4), уиа -60(5) +к счи 5, сда 27 +к сл 6446(1), зп 6532(1) +к по 5125(1), стоп 5122(1) +к слиа 1(5), цикл 35122 ; 5125 +к уиа 77660(4), уиа 77657(5) +к счи 5, сда 27 +к сл 6447(1), нтж 6447(1) +к по 5132(1), стоп 5126(1) +к слиа 1(5), цикл 35127(4) ; 5132 +к уиа 77660(4), уиа 77657(5) +к счи 5, сда 27 +к зп 7773, сч 6447(1) +к сл 7773, нтж 6447(1) +к по 5140(1), стоп 5133(1) +к слиа 1(5), цикл 35134(4) ; 5140 +к уиа -7(6), мода +к уиа -11(4), уиа 77667(5) +к счи 5, сда 27 +к мода (6), сл 5166(1) +к мода (6), нтж 5176(1) +к по 5147(1), стоп 5143(1) +к мода (6), сч 5166(1) ; 5147 +к нтж 6450(1), зп 6520(1) +к счи 5, сда 27 +к сл 6520(1), мода (6) +к нтж 5206(1), по 5155(1) +к стоп 5147(1), мода +к слиа 10(5), цикл 35143(4) ; 5155 +к цикл 35142(6), пб 5207(1) +с 3351240000000000 +с 3343240000000000 +с 3340440000000000 +с 3352400000000000 +с 3350040000000000 +с 3344100000000000 +с 3342400000000000 +с 3340140000000000 +с 3351240000000000 +с 3255200000000000 +с 3151000000000000 +с 3352400000000000 +с 3350040000000000 +с 3310200000000000 +с 3252000000000000 +с 3054000000000000 +с 3366500000000000 +с 3262400000000000 +с 3166000000000000 +с 3365340000000000 +с 3367700000000000 +с 3327500000000000 +с 3265600000000000 +с 3060000000000000 +к уиа -47(4), уиа 50(5) ; 5207 +к сч 6430(1), мода +к зп 6520(1), сч 6451(1) +к и 6520(1), зп 6521(1) +к счи 5, сда 27 +к сл 6451(1), нтж 6521(1) +к по 5216(1), стоп 5207(1) +к слиа 1(5), сч 6520(1) ; 5216 +к сда 77, цикл 35211(4) +к уиа -47(4), уиа (5) +к счи 5, сда 27 +к сл 6451(1), нтж 6451(1) +к по 5224(1), стоп 5220(1) +к слиа 1(5), цикл 35221(4) ; 5224 +к уиа -46(5), сч 6452(1) +к зп 6536(1), зп 6535(1) +к счи 5, слц 6453(1) +к сда 27, зп 6533(1) +к сч 6535(1), нтж 6430(1) +к сда 100(5), и 6454(1) +к или 6533(1), зп 6524(1) +к сч 6440(1), сда 101(5) +к зп 6530(1), мода +к сч 6535(1), или 6455(1) ; 5236 +к умн 6430(1), нтж 6524(1) +к по 5241(1), стоп 5236(1) +к сч 6535(1), или 6455(1) ; 5241 +к умн 6430(1), ржа 6 +к счмр, по 5245(1) +к стоп 5241(1), мода +к сч 6535(1), и 6456(1) ; 5245 +к пе 5254(1), сч 6535(1) +к сда 77, зп 6535(1) +к сч 6524(1), слц 6445(1) +к или 6530(1), зп 6524(1) +к сч 6530(1), сда 101 +к зп 6530(1), пб 5236(1) +к сч 6536(1), сда 77 ; 5254 +к зп 6536(1), или 6452(1) +к зп 6535(1), цикл 35227(5) +к уиа 100(3), уиа -47(4) +к сч 6457(1), зп 6546(1) +к сч 6456(1), зп 6527(1) +к зп 6540(1), зп 6547(1) +к или 6457(1), зп 6541(1) +к сч 6460(1), сда (3) +к и 6444(1), зп 6545(1) +к уии 5(4), мода +к сч 6546(1), или 6454(1) +к нтж 6527(1), и 6461(1) +к зп 6543(1), сч 6454(1) +к вч 6541(1), нтж 6543(1) +к по 5274(1), стоп 5267(1) +к сч 6454(1), вч 6541(1) ; 5274 +к ржа 6, счмр +к нтж 6545(1), по 5300(1) +к стоп 5274(1), мода +к сч 6547(1), сда 101 ; 5300 +к зп 6547(1), или 6540(1) +к или 6457(1), зп 6541(1) +к сч 6527(1), сда 101 +к зп 6527(1), цикл 35267(5) +к уиа -47(2), сч 6462(1) +к зп 6542(1), мода +к сч 6546(1), или 6454(1) +к нтж 6527(1), и 6461(1) +к зп 6543(1), сч 6542(1) +к сда 101, зп 6542(1) +к нтж 6454(1), вч 6541(1) +к нтж 6543(1), по 5316(1) +к стоп 5307(1), мода +к сч 6542(1), нтж 6444(1) ; 5316 +к сда (3), и 6444(1) +к зп 6544(1), сч 6542(1) +к нтж 6454(1), вч 6541(1) +к ржа 6, счмр +к зп 6532(1), нтж 6544(1) +к по 5325(1), стоп 5316(1) +к сч 6527(1), сда 101 ; 5325 +к зп 6527(1), цикл 35307(2) +к слиа -1(3), сч 6546(1) +к слц 6463(1), и 6464(1) +к зп 6546(1), сч 6456(1) +к зп 6527(1), сч 6540(1) +к сда 101, цикл 35262(4) +к уиа -57(3), уиа 100(4) +к уиа 100(5), мода +к сч 6465(1), сда (4) +к сда (5), нтж 6465(1) +к по 5341(1), стоп 5334(1) +к слиа 1(4), слиа -1(5) ; 5341 +к цикл 35336(3), мода +к уиа -57(4), уиа 160(5) +к сч, зп 6520(1) +к сч 6430(1), сда (5) +к нтж 6520(1), по 5350(1) +к стоп 5343(1), мода +к сч 6520(1), слц 6520(1) ; 5350 +к или 6440(1), зп 6520(1) +к слиа -1(5), цикл 35345(4) +к уиа -16(4), уиа 161(5) +к сч 6430(1), сда (5) +к по 5356(1), стоп 5353(1) +к слиа 1(5), цикл 35354(4) ; 5356 +к уиа 77602(4), сч 6461(1) +к зп 6523(1), мода +к сч, сд 6523(1) +к по 5363(1), стоп 5357(1) +к сд 6523(1), счмр ; 5363 +к по 5365(1), стоп 5363(1) +к сч 6523(1), слпа 77 ; 5365 +к зп 6523(1), цикл 35361(4) +к уиа -57(5), уиа -57(2) +к сч 6466(1), зп 6523(1) +к сч 6460(1), зп 6527(1) +к сч 6440(1), зп 6552(1) +к зп 6551(1), мода +к зп 6550(1), уии 3(2) +к сч 6527(1), нтж 6552(1) +к зп 6524(1), сч 6460(1) +к нтж 6550(1), сд 6523(1) +к нтж 6524(1), по 5402(1) +к стоп 5375(1), мода +к сч 6550(1), слц 6550(1) ; 5402 +к зп 6550(1), сч 6552(1) +к слц 6552(1), зп 6552(1) +к цикл 35375(3), сч 6440(1) +к зп 6552(1), сч 6527(1) +к сда 101, и 6467(1) +к зп 6527(1), сч 6523(1) +к слпа 101, зп 6523(1) +к слиа 1(2), сч 6551(1) +к слц 6551(1), зп 6551(1) +к цикл 35374(5), мода +к уиа -57(3), уиа 101(4) +к уиа 100(5), мода +к сч 6440(1), сда (4) +к счмр, сда (5) +к нтж 6465(1), по 5423(1) +к стоп 5415(1), мода +к слиа 1(4), слиа -1(5) ; 5423 +к цикл 35417(3), мода +к уиа -16(4), уиа 177(5) +к сч 6470(1), зп 6520(1) +к сч 6430(1), сда (5) +к счмр, нтж 6520(1) +к по 5432(1), стоп 5425(1) +к сч 6520(1), слц 6520(1) ; 5432 +к или 6440(1), зп 6520(1) +к слиа -1(5), цикл 35427(4) +к уиа -57(5), уиа -57(2) +к сч 6471(1), зп 6523(1) +к сч 6460(1), зп 6527(1) +к сч 6440(1), мода +к зп 6526(1), зп 6525(1) +к сч 6440(1), зп 6522(1) +к уии 3(2), мода +к сч 6527(1), нтж 6525(1) +к зп 6524(1), сч 6460(1) +к нтж 6522(1), сд 6523(1) +к счмр, нтж 6524(1) +к по 5451(1), стоп 5444(1) +к сч 6522(1), слц 6522(1) ; 5451 +к зп 6522(1), сч 6525(1) +к слц 6525(1), зп 6525(1) +к цикл 35444(3), сч 6523(1) +к слпа 77, зп 6523(1) +к слиа 1(2), сч 6527(1) +к слц 6527(1), и 6464(1) +к зп 6527(1), сч 6526(1) +к слц 6526(1), цикл 35441(5) +к уиа -16(5), уиа -56(2) +к сч 6471(1), зп 6523(1) +к сч 6467(1), зп 6527(1) +к сч 6452(1), мода +к зп 6551(1), зп 6550(1) +к сч 6440(1), зп 6552(1) +к уии 3(2), сч 6523(1) +к слпа 101, зп 6523(1) +к сч 6527(1), нтж 6552(1) +к зп 6524(1), сч 6460(1) +к нтж 6550(1), сд 6523(1) +к счмр, нтж 6524(1) +к по 5477(1), стоп 5472(1) +к сч 6550(1), слц 6550(1) ; 5477 +к зп 6550(1), сч 6552(1) +к слц 6552(1), зп 6552(1) +к цикл 35472(3), слиа 1(2) +к сч 6527(1), сда 101 +к зп 6527(1), сч 6551(1) +к слц 6551(1), цикл 35466(5) +к уиа -57(3), уиа 100(4) +к уиа 100(5), мода +к сч 6440(1), сда (4) +к сда (5), нтж 6440(1) +к по 5513(1), стоп 5506(1) +к слиа -1(4), слиа 1(5) ; 5513 +к цикл 35510(3), мода +к уиа -47(5), уиа -47(4) +к сч 6456(1), зп 6534(1) +к сч 6447(1), зп 6531(1) +к сч 6472(1), мода +к зп 6527(1), зп 6530(1) +к или 6531(1), зп 6524(1) +к сч 6473(1), зп 6533(1) +к уиа -50(3), уии 2(4) +к сч 6474(1), нтж 6534(1) +к сл 6533(1), зп 6532(1) +к нтж 6524(1), по 5531(1) +к стоп 5525(1), мода +к сч 6533(1), слц 6445(1) ; 5531 +к зп 6533(1), цикл 35525(3) +к уиа (3), сч 6530(1) +к слц 6530(1), и 6472(1) +к зп 6530(1), или 6531(1) +к зп 6524(1), цикл 35525(2) +к ржа 3, сч 6534(1) +к умн 6475(1), зп 6534(1) +к ржа, слиа 1(4) +к сч 6531(1), слц 6463(1) +к и 6476(1), зп 6531(1) +к сч 6527(1), слц 6527(1) +к и 6472(1), цикл 35521(5) +к уиа 100(5), уиа -60(4) +к сч 6430(1), зп 6520(1) +к сч 6430(1), сда (5) +к нтж 6520(1), по 5553(1) +к стоп 5546(1), мода +к сч 6520(1), слц 6520(1) ; 5553 +к и 6464(1), зп 6520(1) +к слиа -1(5), цикл 35550(4) +к уиа -17(4), уиа 17(5) +к сч 6430(1), сда (5) +к по 5561(1), стоп 5556(1) +к слиа -1(5), цикл 35557(4) ; 5561 +к уиа -57(3), уиа 77(4) +к уиа 100(5), мода +к сч 6465(1), сда (4) +к счмр, сда (5) +к нтж 6440(1), по 5570(1) +к стоп 5562(1), мода +к слиа -1(4), слиа 1(5) ; 5570 +к цикл 35564(3), мода +к уиа -17(4), уиа 17(5) +к сч 6464(1), зп 6520(1) +к сч 6430(1), сда (5) +к счмр, нтж 6520(1) +к по 5577(1), стоп 5572(1) +к сч 6520(1), слц 6520(1) ; 5577 +к и 6464(1), зп 6520(1) +к слиа -1(5), цикл 35574(4) +к уиа -37(4), сч 6477(1) +к зп 6543(1), сч 6464(1) +к зп 6520(1), сда +к счмр, нтж 6543(1) +к по 5607(1), стоп 5604(1) +к сч 6543(1), слц 6543(1) ; 5607 +к слц 6500(1), зп 6543(1) +к сч 6520(1), слц 6520(1) +к цикл 35604(4), мода +к уиа 77640(3), уиа 20(4) +к сч 6460(1), сда (4) +к зп 7777, счмр +к слц 7777, нтж 6460(1) +к по 5620(1), стоп 5613(1) +к слиа 1(4), цикл 35614(3) ; 5620 +к уиа -60(4), уиа 100(3) +к сч 6440(1), сда (3) +к зп 6520(1), нтж 6430(1) +к сч 6520(1), нед +к уи 5, сли 5(4) +к слиа -1(3), пио 35630(5) +к стоп 5621(1), мода +к цикл 35622(4), мода +к уиа (7), уиа (3) +к уиа -5(11), мода +к уиа -21(10), мода +к мода 6002(1), сч (10) +к сда 100(7), нтж 6430(1) +к мода 6574(1), зп (10) +к цикл 35634(10), уиа -3(4) +к слиа -1(3), мода +к мода 6556(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35645(5), стоп 5641(1) +к цикл 35641(4), слиа -1(3) +к уиа -2(4), мода +к мода 6561(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35653(5), стоп 5647(1) +к цикл 35647(4), слиа -1(3) +к уиа -2(4), мода +к мода 6564(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35661(5), стоп 5655(1) +к цикл 35655(4), слиа -1(3) +к сч 6565(1), мода +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35666(5), стоп 5663(1) +к слиа -1(3), уиа -3(4) +к мода 6571(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35673(5), стоп 5666(1) +к цикл 35667(4), слиа -1(3) +к сч 6572(1), мода +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35700(5), стоп 5675(1) +к слиа -1(3), сч 6573(1) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35704(5), стоп 5700(1) +к слиа -1(3), сч 6574(1) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35710(5), стоп 5704(1) +к слиа 10(7), цикл 35633(11) +к уиа -17(10), мода +к мода 6022(1), сч (10) +к нтж 6430(1), мода 6572(1) +к зп (10), цикл 35712(10) +к уиа -3(4), уиа -1(3) +к мода 6556(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35722(5), стоп 5716(1) +к цикл 35716(4), уиа -2(4) +к слиа -10(3), мода +к мода 6561(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35730(5), стоп 5724(1) +к цикл 35724(4), уиа -2(4) +к слиа -10(3), мода +к мода 6564(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35736(5), стоп 5732(1) +к цикл 35732(4), слиа -10(3) +к мода 6565(1), сч +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35743(5), стоп 5737(1) +к уиа -1(4), слиа -10(3) +к мода 6567(1), сч (4) +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35750(5), стоп 5743(1) +к цикл 35744(4), слиа -10(3) +к мода 6570(1), сч +к нтж 6430(1), нед +к уи 5, сли 5(3) +к пио 35755(5), стоп 5751(1) +к мода 6571(1), сч +к нтж 6430(1), нед +к по 6022(1), стоп 5755(1) +к пб 6022(1), мода +с 6537777777777777 +с 5057777777777777 +с 4437777777777777 +с 4377777777777777 +с 3777777777777777 +с 3477777777777777 +с 2377777777777777 +с 1777777777777777 +с 1137777777777777 +с 1377777777777777 +с 0777777777777777 +с 0377777777777777 +с 0337777777777777 +с 0277777777777777 +с 0217777777777777 +с 0177777777777777 +с 0077777777777777 +с 0037777777777777 +с 7777740077600377 +с 7760037700000000 +с 7760000077600000 +с 7760000000177777 +с 0017777777777777 +с 0017777777600000 +с 0017740000177777 +с 0000037777777777 +с 0000037700000377 +с 0000037700177777 +с 0000000077777777 +с 0000000000177777 +с 0000000000177400 +с 0000000000000377 +с 0000000000000000 +к ржа 3, мода ; 6022 +к сч 6501(1), вчоб 6445(1) +к зп 6520(1), счмр 77 +к нтж 6456(1), по 6027(1) +к стоп 6023(1), мода +к сч 6520(1), нтж 6502(1) ; 6027 +к по 6031(1), стоп 6027(1) +к уиа -44(5), сч 6122(1) ; 6031 +к сда 77, зп 6520(1) +к сч 6123(1), зп 6521(1) +к сч 6520(1), вчоб 6445(1) +к нтж 6521(1), по 6037(1) +к стоп 6034(1), мода +к сч 6520(1), сда 77 ; 6037 +к зп 6520(1), сч 6521(1) +к сда 77, и 6503(1) +к зп 6521(1), цикл 36034(5) +к сч 6504(1), вчоб 6445(1) +к нтж 6505(1), по 6046(1) +к стоп 6043(1), мода +к сч 6462(1), вчоб 6445(1) ; 6046 +к нтж 6506(1), по 6051(1) +к стоп 6046(1), мода +к сч 6445(1), вч 6501(1) ; 6051 +к зп 6520(1), счмр 77 +к нтж 6456(1), по 6055(1) +к стоп 6051(1), мода +к сч 6520(1), нтж 6502(1) ; 6055 +к по 6057(1), стоп 6055(1) +к уиа -44(5), сч 6122(1) ; 6057 +к сда 77, зп 6520(1) +к сч 6123(1), зп 6521(1) +к сч 6445(1), вч 6520(1) +к нтж 6521(1), по 6065(1) +к стоп 6062(1), мода +к сч 6520(1), сда 77 ; 6065 +к зп 6520(1), сч 6521(1) +к сда 77, и 6503(1) +к зп 6521(1), цикл 36062(5) +к сч 6445(1), вч 6504(1) +к нтж 6505(1), по 6074(1) +к стоп 6071(1), мода +к сч 6445(1), вч 6462(1) ; 6074 +к нтж 6506(1), по 6077(1) +к стоп 6074(1), мода +к сч 6124(1), зп 6520(1) ; 6077 +к уиа -50(5), мода +к сч 6507(1), вчоб 6520(1) +к нтж 6520(1), по 6104(1) +к стоп 6101(1), мода +к сч 6520(1), сда 77 ; 6104 +к и 6503(1), зп 6520(1) +к цикл 36101(5), мода +к сч 6124(1), зп 6520(1) +к уиа -47(5), мода +к сч 6520(1), вч 6507(1) +к нтж 6520(1), по 6114(1) +к стоп 6111(1), мода +к сч 6520(1), сда 77 ; 6114 +к и 6503(1), зп 6520(1) +к цикл 36111(5), мода +к сч 6510(1), вч 6507(1) +к нтж 6510(1), по 6125(1) +к стоп 6117(1), пб 6125(1) +с 0000000000000003 +с 0077777777777775 +с 0040000000000001 +к уиа -47(5), сч 6436(1) ; 6125 +к зп 7776, мода +к сч 6511(1), умн 7776 +к зп 7777, счмр 100 +к слц 7777, нтж 6444(1) +к по 6133(1), стоп 6125(1) +к сч 7776, сда 77 ; 6133 +к зп 7776, цикл 36127(5) +к уиа -60(4), ржа +к сч 6460(1), сда 160(4) +к чед, уи 5 +к сли 5(4), пио 36142(5) +к стоп 6135(1), мода +к цикл 36136(4), мода +к сч 6443(1), уиа 77601(2) +к слпа 101, зп 7777 +к сч 6443(1), уиа 77601(5) +к слпа 101, зп 7776 +к сл 7777, зп 7775 +к сч 7777, сл 7776 +к нтж 7775, по 6154(1) +к стоп 6143(1), сч 7777 +к сч 7776, мода +к сч 7777, нтж 6512(1) ; 6154 +к пе 6157(1), сч 7776 +к нтж 6513(1), по 6161(1) +к сч 7776, цикл 36146(5) ; 6157 +к сч 7777, цикл 36144(2) +к уиа -57(4), уиа -60(5) ; 6161 +к сч 6462(1), зп 6520(1) +к счи 5, сда 27 +к сл 6462(1), нтж 6520(1) +к по 6166(1), стоп 6161(1) +к слиа 1(5), сч 6520(1) ; 6166 +к слпа 101, зп 6520(1) +к цикл 36163(4), мода +к ржа 3, уиа -47(4) +к сч 6440(1), зп 6520(1) +к сч 6454(1), зп 6521(1) +к сч, вч 6520(1) +к нтж 6521(1), по 6177(1) +к стоп 6171(1), мода +к сч 6520(1), слц 6520(1) ; 6177 +к зп 6520(1), сч 6521(1) +к слц 6521(1), и 6454(1) +к зп 6521(1), цикл 36174(4) +к уиа -46(4), сч 6440(1) +к зп 6520(1), сч 6503(1) +к зп 6521(1), мода +к сч 6445(1), вч 6520(1) ; 6206 +к нтж 6521(1), по 6211(1) +к стоп 6203(1), мода +к сч 6520(1), слц 6520(1) ; 6211 +к зп 6520(1), нтж 6452(1) +к по 6206(1), сч 6521(1) +к слц 6521(1), и 6503(1) +к зп 6521(1), цикл 36206(4) +к уиа -46(4), сч 6440(1) +к зп 6520(1), сч 6503(1) +к зп 6521(1), мода +к сч 6520(1), вчоб 6445(1) ; 6221 +к нтж 6521(1), по 6224(1) +к стоп 6216(1), мода +к сч 6520(1), слц 6520(1) ; 6224 +к зп 6520(1), нтж 6452(1) +к по 6221(1), сч 6521(1) +к слц 6521(1), и 6503(1) +к зп 6521(1), цикл 36221(4) +к ржа, мода +к уиа -1(4), уиа -17(3) +к уиа -67(5), сч 6464(1) +к зп 6777, пио 36236(4) +к зп 7067(5), пб 6240(1) +к нтж 7067(5), по 6240(1) +к стоп 6232(1), мода +к сч 6777, слц 6777 ; 6240 +к цикл 36234(5), цикл 36233(4) +к цикл 36233(3), мода +к сч -12, пе 6246(1) +к сч 6460(1), зп -12 +к сч 6514(1), зп 7767 +к уиа 77400(12), мода ; 6246 +к сч 7767, слц 6 +к зп 7777, сда 115 +к слц 7777, зп 7776 +к сч 7777, сда 63 +к слц 7776, слц 6440(1) +к зп 7777, зп 7767 +к сч 7777, нтж 6460(1) +к зп 7774, сч 7776 +к нтж 6460(1), или 7774 +к зп 7775, сч 7777 +к и 7776, нтж 6460(1) +к нтж 7775, по 6264(1) +к стоп 6255(1), мода +к сч 7777, слц 7776 ; 6264 +к зп 7775, сч 7777 +к нтж 6460(1), слц 7775 +к нтж 7776, по 6271(1) +к стоп 6264(1), мода +к сч 7777, сбр 7776 ; 6271 +к рзб 7776, зп 7775 +к сч 7776, нтж 6460(1) +к зп 7774, сч 7777 +к сбр 7774, рзб 7774 +к слц 7775, нтж 7777 +к по 6300(1), стоп 6271(1) +к сч 7777, чед ; 6300 +к зп 7775, уиа -57(5) +к сч, зп 7774 +к сч 7777, зп 7773 +к сч 7773, и 6440(1) +к слц 7774, зп 7774 +к сч 7773, сда 101 +к зп 7773, цикл 36304(5) +к сч 7775, нтж 7774 +к по 6312(1), стоп 6310(1) +к сч 7777, нед ; 6312 +к зп 7775, сч 7777 +к уиа 61(5), мода +к слиа -1(5), сда 101 ; 6315 +к пе 6315(1), счи 5 +к нтж 7775, по 6321(1) +к стоп 6312(1), мода +к сч 7777, знак 7776 ; 6321 +к знак 7776, вч 7777 +к нтж, по 6325(1) +к стоп 6321(1), мода +к сч 7777, вчп 7777 ; 6325 +к слп 7777, вч 7777 +к нтж, по 6331(1) +к стоп 6325(1), мода +к сч 7777, знак 6462(1) ; 6331 +к сл 7777, нтж +к по 6334(1), стоп 6331(1) +к сч 7777, вч 7777 ; 6334 +к нтж, по 6337(1) +к стоп 6334(1), мода +к сч 7777, вчоб 7777 ; 6337 +к нтж, по 6342(1) +к стоп 6337(1), мода +к сч 7777, вчаб 7777 ; 6342 +к нтж, по 6345(1) +к стоп 6342(1), мода +к сч 7777, вчпа 101 ; 6345 +к зп 7774, сч 7776 +к вчпа 101, зп 7773 +к сл 7774, зп 7775 +к сч 7774, сл 7773 +к знак 6462(1), сл 7775 +к нтж, по 6355(1) +к стоп 6345(1), мода +к сч 7777, и 6515(1) ; 6355 +к зп 7774, сч 7776 +к и 6515(1), зп 7773 +к умн 7774, зп 7775 +к сч 7774, умн 7773 +к нтж 7775, по 6364(1) +к стоп 6355(1), мода +к сч 7774, сл ; 6364 +к нтж, по 6375(1) +к зп 7772, сч 7773 +к сл, зп 7771 +к умн 7772, нтж +к по 6375(1), дел 7772 +к вчоб 7771, вчп 7771 +к слпа 145, пе 6375(1) +к стоп 6364(1), мода +к сч 7774, зп 7772 ; 6375 +к сч 7773, зп 7771 +к умн 7772, счмр 100 +к зп 7775, сч 7772 +к умн 7771, счмр 100 +к нтж 7775, по 6404(1) +к стоп 6375(1), мода +к сч 7776, сда 116 ; 6404 +к сда 21, сда 141 +к пе 6407(1), или 6516(1) +к или 7776, уи 17 ; 6407 +к и 6517(1), зп 7775 +к сч 7776, счм 7777 +к нтж 7777, по 6414(1) +к стоп 6404(1), мода +к мода -1, сч (17) ; 6414 +к нтж 7776, по 6417(1) +к стоп 6414(1), мода +к сч 7777, зпм 7774 ; 6417 +к нтж 7776, по 6422(1) +к стоп 6417(1), мода +к счи 17, нтж 7775 ; 6422 +к по 6424(1), стоп 6422(1) +к сч 7774, нтж 7777 ; 6424 +к по 6426(1), стоп 6424(1) +к цикл 36247(12), мода ; 6426 +к пб 32013, мода +с 7777777777777777 +с 0000000000036430 +с 0000000000036432 +с 0000000000036433 +с 0000000000034267 +с 0000000000034272 +с 0000000000000001 +с 0000000000000002 +с 0000000000000001 +с 3010000000000000 +с 3116000000000000 +с 0004000000000000 +с 0017777777777777 +с 0040000000000000 +с 0017777777777177 +с 3020000000000000 +с 0037740000000000 +с 0027777777777777 +с 0000000000000002 +с 0000000000000127 +с 0037777777777777 +с 4000000000000001 +с 0010000000000000 +с 2400000000000000 +с 7777777777777777 +с 7767777777777777 +с 0020000000000000 +с 7740000000000000 +с 7777777777777776 +с 4000000000000000 +с 4020000000000000 +с 3777777777777777 +с 0000077777777777 +с 7020000000000000 +с 0007777777777777 +с 3000000000000000 +с 3037777777777777 +с 4010000000000000 +с 7760000000000000 +с 7777777777400000 +с 0000000000177777 +с 0000000000000003 +с 0077777777777776 +с 0077777777777777 +с 0030000000000000 +с 0044000000000000 +с 0050000000000000 +с 0000000000000000 +с 0060000000000000 +с 4017777777777777 +с 7750000000000000 +с 7710000000000000 +с 3572414425543217 +с 5777777777777777 +с 0000000000037700 +с 0000000000077777 + +; Тест выполняет чтение данных по адресам вида 1<