From 659600ff595fcf55adcb5f6e0453bb5dd2ce470f Mon Sep 17 00:00:00 2001 From: Leo Broukhis Date: Mon, 29 Dec 2014 21:13:00 -0800 Subject: [PATCH] BESM6: Added target 'besm6' to makefile; added new files in the BESM6 subdirectory. --- BESM6/besm6_arith.c | 483 +++++++++++ BESM6/besm6_cpu.c | 1768 +++++++++++++++++++++++++++++++++++++++++ BESM6/besm6_defs.h | 426 ++++++++++ BESM6/besm6_disk.c | 648 +++++++++++++++ BESM6/besm6_drum.c | 372 +++++++++ BESM6/besm6_mmu.c | 652 +++++++++++++++ BESM6/besm6_panel.c | 594 ++++++++++++++ BESM6/besm6_printer.c | 345 ++++++++ BESM6/besm6_punch.c | 472 +++++++++++ BESM6/besm6_sys.c | 719 +++++++++++++++++ BESM6/besm6_tty.c | 1243 +++++++++++++++++++++++++++++ makefile | 26 + 12 files changed, 7748 insertions(+) create mode 100644 BESM6/besm6_arith.c create mode 100644 BESM6/besm6_cpu.c create mode 100644 BESM6/besm6_defs.h create mode 100644 BESM6/besm6_disk.c create mode 100644 BESM6/besm6_drum.c create mode 100644 BESM6/besm6_mmu.c create mode 100644 BESM6/besm6_panel.c create mode 100644 BESM6/besm6_printer.c create mode 100644 BESM6/besm6_punch.c create mode 100644 BESM6/besm6_sys.c create mode 100644 BESM6/besm6_tty.c diff --git a/BESM6/besm6_arith.c b/BESM6/besm6_arith.c new file mode 100644 index 00000000..d29685e0 --- /dev/null +++ b/BESM6/besm6_arith.c @@ -0,0 +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; + } + } +} diff --git a/BESM6/besm6_cpu.c b/BESM6/besm6_cpu.c new file mode 100644 index 00000000..50b92fe9 --- /dev/null +++ b/BESM6/besm6_cpu.c @@ -0,0 +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 +}; diff --git a/BESM6/besm6_defs.h b/BESM6/besm6_defs.h new file mode 100644 index 00000000..d782109a --- /dev/null +++ b/BESM6/besm6_defs.h @@ -0,0 +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 diff --git a/BESM6/besm6_disk.c b/BESM6/besm6_disk.c new file mode 100644 index 00000000..37fbd23c --- /dev/null +++ b/BESM6/besm6_disk.c @@ -0,0 +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; +} diff --git a/BESM6/besm6_drum.c b/BESM6/besm6_drum.c new file mode 100644 index 00000000..2766baa2 --- /dev/null +++ b/BESM6/besm6_drum.c @@ -0,0 +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; +} diff --git a/BESM6/besm6_mmu.c b/BESM6/besm6_mmu.c new file mode 100644 index 00000000..beed6dc2 --- /dev/null +++ b/BESM6/besm6_mmu.c @@ -0,0 +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 ("'"); + } +} diff --git a/BESM6/besm6_panel.c b/BESM6/besm6_panel.c new file mode 100644 index 00000000..6ed387e8 --- /dev/null +++ b/BESM6/besm6_panel.c @@ -0,0 +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 */ diff --git a/BESM6/besm6_printer.c b/BESM6/besm6_printer.c new file mode 100644 index 00000000..9618f76e --- /dev/null +++ b/BESM6/besm6_printer.c @@ -0,0 +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; +} diff --git a/BESM6/besm6_punch.c b/BESM6/besm6_punch.c new file mode 100644 index 00000000..d9d974f1 --- /dev/null +++ b/BESM6/besm6_punch.c @@ -0,0 +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); +} diff --git a/BESM6/besm6_sys.c b/BESM6/besm6_sys.c new file mode 100644 index 00000000..5d6e6927 --- /dev/null +++ b/BESM6/besm6_sys.c @@ -0,0 +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); +} diff --git a/BESM6/besm6_tty.c b/BESM6/besm6_tty.c new file mode 100644 index 00000000..489de9b0 --- /dev/null +++ b/BESM6/besm6_tty.c @@ -0,0 +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]; +} diff --git a/makefile b/makefile index 871cea54..74f5f698 100644 --- a/makefile +++ b/makefile @@ -68,6 +68,8 @@ ifneq (,$(or $(findstring pdp11,$(MAKECMDGOALS)),$(findstring vax,$(MAKECMDGOALS VIDEO_USEFUL = true DISPLAY_USEFUL = true endif +else ifeq ($(MAKECMDGOALS),besm6) + VIDEO_USEFUL = true else ifeq ($(MAKECMDGOALS),) # default target is all @@ -1159,6 +1161,24 @@ SSEMD = SSEM SSEM = ${SSEMD}/ssem_cpu.c ${SSEMD}/ssem_sys.c SSEM_OPT = -I ${SSEMD} +### +### Experimental simulators +### + +BESM6D = BESM6 +BESM6 = ${BESM6D}/besm6_cpu.c ${BESM6D}/besm6_sys.c ${BESM6D}/besm6_mmu.c \ + ${BESM6D}/besm6_arith.c ${BESM6D}/besm6_disk.c ${BESM6D}/besm6_drum.c \ + ${BESM6D}/besm6_tty.c ${BESM6D}/besm6_panel.c ${BESM6D}/besm6_printer.c \ + ${BESM6D}/besm6_punch.c + +ifeq (,${VIDEO_LDFLAGS}) + BESM6_OPT = -I ${BESM6D} -DUSE_INT64 +else ifneq (,$(and $(findstring,SDL2,${VIDEO_LDFLAGS})),$(and($(find_include, SDL2/SDL_ttf),$(find_lib,SDL2_ttf)))) + BESM6_OPT = -I ${BESM6D} -DUSE_INT64 ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} -lSDL2_ttf +else ifneq (,$(and $(find_include, SDL/SDL_ttf),$(find_lib,SDL_ttf))) + BESM6_OPT = -I ${BESM6D} -DUSE_INT64 ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} -lSDL_ttf +endif + ### ### Unsupported/Incomplete simulators ### @@ -1462,6 +1482,12 @@ ${BIN}ssem${EXE} : ${SSEM} ${SIM} ${MKDIRBIN} ${CC} ${SSEM} ${SIM} ${SSEM_OPT} $(CC_OUTSPEC) ${LDFLAGS} +besm6 : ${BIN}besm6${EXE} + +${BIN}besm6${EXE} : ${BESM6} ${SIM} + ${MKDIRBIN} + ${CC} ${BESM6} ${SIM} ${BESM6_OPT} $(CC_OUTSPEC) ${LDFLAGS} + sigma : ${BIN}sigma${EXE} ${BIN}sigma${EXE} : ${SIGMA} ${SIM}