BESM6: Added target 'besm6' to makefile; added new files in the BESM6 subdirectory.

This commit is contained in:
Leo Broukhis 2014-12-29 21:13:00 -08:00
parent de9fc84fd4
commit 659600ff59
12 changed files with 7748 additions and 0 deletions

483
BESM6/besm6_arith.c Normal file
View file

@ -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 <math.h>
#include "besm6_defs.h"
typedef struct {
t_uint64 mantissa;
unsigned exponent; /* offset by 64 */
} alureg_t; /* ALU register type */
static alureg_t toalu (t_value val)
{
alureg_t ret;
ret.mantissa = val & BITS41;
ret.exponent = (val >> 41) & BITS(7);
if (ret.mantissa & BIT41)
ret.mantissa |= BIT42;
return ret;
}
static int inline is_negative (alureg_t *word)
{
return (word->mantissa & BIT41) != 0;
}
static void negate (alureg_t *val)
{
if (is_negative (val))
val->mantissa |= BIT42;
val->mantissa = (~val->mantissa + 1) & BITS42;
if (((val->mantissa >> 1) ^ val->mantissa) & BIT41) {
val->mantissa >>= 1;
++val->exponent;
}
if (is_negative (val))
val->mantissa |= BIT42;
}
/*
* 48-й разряд -> 1, 47-й -> 2 и т.п.
* Единица 1-го разряда и нулевое слово -> 48,
* как в первоначальном варианте системы команд.
*/
int besm6_highest_bit (t_value val)
{
int n = 32, cnt = 0;
do {
t_value tmp = val;
if (tmp >>= n) {
cnt += n;
val = tmp;
}
} while (n >>= 1);
return 48 - cnt;
}
/*
* Нормализация и округление.
* Результат помещается в регистры ACC и 40-1 разряды RMR.
* 48-41 разряды RMR сохраняются.
*/
static void normalize_and_round (alureg_t acc, t_uint64 mr, int rnd_rq)
{
t_uint64 rr = 0;
int i;
t_uint64 r;
if (RAU & RAU_NORM_DISABLE)
goto chk_rnd;
i = (acc.mantissa >> 39) & 3;
if (i == 0) {
r = acc.mantissa & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
r <<= cnt;
rr = mr >> (40 - cnt);
acc.mantissa = r | rr;
mr <<= cnt;
acc.exponent -= cnt;
goto chk_zero;
}
r = mr & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
rr = mr;
r <<= cnt;
acc.mantissa = r;
mr = 0;
acc.exponent -= 40 + cnt;
goto chk_zero;
}
goto zero;
} else if (i == 3) {
r = ~acc.mantissa & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
r = (r << cnt) | ((1LL << cnt) - 1);
rr = mr >> (40 - cnt);
acc.mantissa = BIT41 | (~r & BITS40) | rr;
mr <<= cnt;
acc.exponent -= cnt;
goto chk_zero;
}
r = ~mr & BITS40;
if (r) {
int cnt = besm6_highest_bit (r) - 9;
rr = mr;
r = (r << cnt) | ((1LL << cnt) - 1);
acc.mantissa = BIT41 | (~r & BITS40);
mr = 0;
acc.exponent -= 40 + cnt;
goto chk_zero;
} else {
rr = 1;
acc.mantissa = BIT41;
mr = 0;
acc.exponent -= 80;
goto chk_zero;
}
}
chk_zero:
if (rr)
rnd_rq = 0;
chk_rnd:
if (acc.exponent & 0x8000)
goto zero;
if (! (RAU & RAU_ROUND_DISABLE) && rnd_rq)
acc.mantissa |= 1;
if (! acc.mantissa && ! (RAU & RAU_NORM_DISABLE)) {
zero: ACC = 0;
RMR &= ~BITS40;
return;
}
ACC = (t_value) (acc.exponent & BITS(7)) << 41 |
(acc.mantissa & BITS41);
RMR = (RMR & ~BITS40) | (mr & BITS40);
/* При переполнении мантисса и младшие разряды порядка верны */
if (acc.exponent & 0x80) {
if (! (RAU & RAU_OVF_DISABLE))
longjmp (cpu_halt, STOP_OVFL);
}
}
/*
* Сложение и все варианты вычитаний.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC и 40-1 разряды RMR.
*/
void besm6_add (t_value val, int negate_acc, int negate_val)
{
t_uint64 mr;
alureg_t acc, word, a1, a2;
int diff, neg, rnd_rq = 0;
acc = toalu (ACC);
word = toalu (val);
if (! negate_acc) {
if (! negate_val) {
/* Сложение */
} else {
/* Вычитание */
negate (&word);
}
} else {
if (! negate_val) {
/* Обратное вычитание */
negate (&acc);
} else {
/* Вычитание модулей */
if (is_negative (&acc))
negate (&acc);
if (! is_negative (&word))
negate (&word);
}
}
diff = acc.exponent - word.exponent;
if (diff < 0) {
diff = -diff;
a1 = acc;
a2 = word;
} else {
a1 = word;
a2 = acc;
}
mr = 0;
neg = is_negative (&a1);
if (diff == 0) {
/* Nothing to do. */
} else if (diff <= 40) {
rnd_rq = (mr = (a1.mantissa << (40 - diff)) & BITS40) != 0;
a1.mantissa = ((a1.mantissa >> diff) |
(neg ? (~0ll << (40 - diff)) : 0)) & BITS42;
} else if (diff <= 80) {
diff -= 40;
rnd_rq = a1.mantissa != 0;
mr = ((a1.mantissa >> diff) |
(neg ? (~0ll << (40 - diff)) : 0)) & BITS40;
if (neg) {
a1.mantissa = BITS42;
} else
a1.mantissa = 0;
} else {
rnd_rq = a1.mantissa != 0;
if (neg) {
mr = BITS40;
a1.mantissa = BITS42;
} else
mr = a1.mantissa = 0;
}
acc.exponent = a2.exponent;
acc.mantissa = a1.mantissa + a2.mantissa;
/* Если требуется нормализация вправо, биты 42:41
* принимают значение 01 или 10. */
switch ((acc.mantissa >> 40) & 3) {
case 2:
case 1:
rnd_rq |= acc.mantissa & 1;
mr = (mr >> 1) | ((acc.mantissa & 1) << 39);
acc.mantissa >>= 1;
++acc.exponent;
}
normalize_and_round (acc, mr, rnd_rq);
}
/*
* non-restoring division
*/
#define ABS(x) ((x) < 0 ? -x : x)
#define INT64(x) ((x) & BIT41 ? (-1LL << 40) | (x) : x)
static alureg_t nrdiv (alureg_t n, alureg_t d)
{
t_int64 nn, dd, q, res;
alureg_t quot;
/* to compensate for potential normalization to the right */
nn = INT64(n.mantissa)*2;
dd = INT64(d.mantissa)*2;
res = 0, q = BIT41;
if (ABS(nn) >= ABS(dd)) {
/* normalization to the right */
nn/=2;
n.exponent++;
}
while (q > 1) {
if (nn == 0)
break;
if (ABS(nn) < BIT40)
nn *= 2; /* magic shortcut */
else if ((nn > 0) ^ (dd > 0)) {
res -= q;
nn = 2*nn+dd;
} else {
res += q;
nn = 2*nn-dd;
}
q /= 2;
}
quot.mantissa = res/2;
quot.exponent = n.exponent-d.exponent+64;
return quot;
}
/*
* Деление.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC, содержимое RMR не определено.
*/
void besm6_divide (t_value val)
{
alureg_t acc;
alureg_t dividend, divisor;
if (((val ^ (val << 1)) & BIT41) == 0) {
/* Ненормализованный делитель: деление на ноль. */
longjmp (cpu_halt, STOP_DIVZERO);
}
dividend = toalu(ACC);
divisor = toalu(val);
acc = nrdiv(dividend, divisor);
normalize_and_round (acc, 0, 0);
}
/*
* Умножение.
* Исходные значения: регистр ACC и аргумент 'val'.
* Результат помещается в регистр ACC и 40-1 разряды RMR.
*/
void besm6_multiply (t_value val)
{
uint8 neg = 0;
alureg_t acc, word, a, b;
t_uint64 mr, alo, blo, ahi, bhi;
register t_uint64 l;
if (! ACC || ! val) {
/* multiplication by zero is zero */
ACC = 0;
RMR &= ~BITS40;
return;
}
acc = toalu (ACC);
word = toalu (val);
a = acc;
b = word;
mr = 0;
if (is_negative (&a)) {
neg = 1;
negate (&a);
}
if (is_negative (&b)) {
neg ^= 1;
negate (&b);
}
acc.exponent = a.exponent + b.exponent - 64;
alo = a.mantissa & BITS(20);
ahi = a.mantissa >> 20;
blo = b.mantissa & BITS(20);
bhi = b.mantissa >> 20;
l = alo * blo + ((alo * bhi + ahi * blo) << 20);
mr = l & BITS40;
l >>= 40;
acc.mantissa = l + ahi * bhi;
if (neg) {
mr = (~mr & BITS40) + 1;
acc.mantissa = ((~acc.mantissa & BITS40) + (mr >> 40))
| BIT41 | BIT42;
mr &= BITS40;
}
normalize_and_round (acc, mr, mr != 0);
}
/*
* Изменение знака числа на сумматоре ACC.
* Результат помещается в регистр ACC, RMR гасится.
*/
void besm6_change_sign (int negate_acc)
{
alureg_t acc;
acc = toalu (ACC);
if (negate_acc)
negate (&acc);
RMR = 0;
normalize_and_round (acc, 0, 0);
}
/*
* Изменение порядка числа на сумматоре ACC.
* Результат помещается в регистр ACC, RMR гасится.
*/
void besm6_add_exponent (int val)
{
alureg_t acc;
acc = toalu (ACC);
acc.exponent += val;
RMR = 0;
normalize_and_round (acc, 0, 0);
}
/*
* Сборка значения по маске.
*/
t_value besm6_pack (t_value val, t_value mask)
{
t_value result;
result = 0;
for (; mask; mask>>=1, val>>=1)
if (mask & 1) {
result >>= 1;
if (val & 1)
result |= BIT48;
}
return result;
}
/*
* Разборка значения по маске.
*/
t_value besm6_unpack (t_value val, t_value mask)
{
t_value result;
int i;
result = 0;
for (i=0; i<48; ++i) {
result <<= 1;
if (mask & BIT48) {
if (val & BIT48)
result |= 1;
val <<= 1;
}
mask <<= 1;
}
return result;
}
/*
* Подсчёт количества единиц в слове.
*/
int besm6_count_ones (t_value word)
{
int c;
for (c=0; word; ++c)
word &= word-1;
return c;
}
/*
* Сдвиг сумматора ACC с выдвижением в регистр младших разрядов RMR.
* Величина сдвига находится в диапазоне -64..63.
*/
void besm6_shift (int i)
{
RMR = 0;
if (i > 0) {
/* Сдвиг вправо. */
if (i < 48) {
RMR = (ACC << (48-i)) & BITS48;
ACC >>= i;
} else {
RMR = ACC >> (i-48);
ACC = 0;
}
} else if (i < 0) {
/* Сдвиг влево. */
i = -i;
if (i < 48) {
RMR = ACC >> (48-i);
ACC = (ACC << i) & BITS48;
} else {
RMR = (ACC << (i-48)) & BITS48;
ACC = 0;
}
}
}

1768
BESM6/besm6_cpu.c Normal file

File diff suppressed because it is too large Load diff

426
BESM6/besm6_defs.h Normal file
View file

@ -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 <setjmp.h>
/*
* 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

648
BESM6/besm6_disk.c Normal file
View file

@ -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<nwords; ++i) {
val = data[i];
fprintf (sim_log, " %04o-%04o-%04o-%04o",
(int) (val >> 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;
}

372
BESM6/besm6_drum.c Normal file
View file

@ -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; i<drum_nwords; ++i)
print_word (data[i]);
}
fprintf (sim_log, "\n");
}
#endif
/*
* Запись на барабан.
*/
void drum_write (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);
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;
}

652
BESM6/besm6_mmu.c Normal file
View file

@ -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 ("'");
}
}

594
BESM6/besm6_panel.c Normal file
View file

@ -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 <stdlib.h>
#include <ftw.h>
/*
* 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 <SDL.h>
#include <SDL_ttf.h>
/* 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; y<height; ++y) {
s = (unsigned*) (sprite->pixels + y * sprite->pitch);
for (x=0; x<width; ++x) {
r = *data++;
g = *data++;
b = *data++;
*s++ = SDL_MapRGB (sprite->format, 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"
"<<D\21\21\0\0\0\13\2\2\313,,\377\243\243\377\373\373\377\356\356\377NN\377"
">>\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<<D\21\21\0\0\0\0\0\0\0\0\0D\21\21\313"
"33\377DD\377CC\377CC\377DD\31333D\21\21\0\0\0\0\0\0\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";
static const unsigned char lamp_off [12 * 12 * 3 + 1] =
"\0\0\0\0\0\0\0\0\0\0\0\0\14\2\2\14\2\2\14\2\2\14\2\2\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\25\5\5A\21\21h\32\32c\30\30c\30\30h\32\32A\21\21\25\5\5"
"\0\0\0\0\0\0\0\0\0\25\5\5\\\30\30""8\16\16\0\0\0\0\0\0\0\0\0\0\0\0""8\16"
"\16\\\30\30\25\5\5\0\0\0\0\0\0A\21\21""8\16\16\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0""8\16\16A\21\21\0\0\0\14\2\2h\32\32\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0h\32\32\14\2\2\14\2\2c\30\30\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0c\30\30\14\2\2\14\2\2c\30\30\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0c\30\30\14\2\2\14\2\2h\32\32\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0h\32\32\14\2\2\0\0\0A\21\21""8\16\16\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\16\16A\21\21\0\0\0\0\0\0\25\5\5\\\30"
"\30""8\16\16\0\0\0\0\0\0\0\0\0\0\0\0""8\16\16\\\30\30\25\5\5\0\0\0\0\0\0"
"\0\0\0\25\5\5A\21\21h\32\32c\30\30c\30\30h\32\32A\21\21\25\5\5\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\14\2\2\14\2\2\14\2\2\14\2\2\0\0\0\0\0\0\0\0\0"
"\0\0\0";
static SDL_Surface *sprite_on, *sprite_off;
SDL_Rect area;
if (! sprite_on) {
sprite_on = sprite_from_data (lamp_width, lamp_height,
lamp_on);
}
if (! sprite_off) {
sprite_off = sprite_from_data (lamp_width, lamp_height,
lamp_off);
}
area.x = left;
area.y = top;
area.w = lamp_width;
area.h = lamp_height;
SDL_BlitSurface (on ? sprite_on : sprite_off, 0, screen, &area);
}
/*
* Отрисовка лампочек БРЗ.
*/
static void draw_modifiers_periodic (int group, int left, int top)
{
int x, y, reg, val;
for (y=0; y<8; ++y) {
reg = regnum [y + group*8];
val = M [reg];
if (val == old_M [reg])
continue;
old_M [reg] = val;
for (x=0; x<15; ++x) {
draw_lamp (left+76 + x*STEPX, top+28 + y*STEPY, val >> (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 */

345
BESM6/besm6_printer.c Normal file
View file

@ -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;
}

472
BESM6/besm6_punch.c Normal file
View file

@ -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 <sys/stat.h>
#include <sys/fcntl.h>
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);
}

719
BESM6/besm6_sys.c Normal file
View file

@ -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 <math.h>
#include <unistd.h>
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 6353 521
* знак порядок мантисса
* Старший (53-й) бит мантиссы не хранится и всегда равен 1.
*
* Представление чисел в БЭСМ-6:
* 4842 41 401
* порядок знак мантисса в доп. коде
*/
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, &reg); /* 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, &reg);
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<MEMSIZE; ++addr) {
if (addr < 010)
word = pult [addr];
else
word = memory [addr];
if (word == 0)
continue;
if (addr != last_addr+1) {
fprintf (of, "\nв %05o\n", addr);
}
last_addr = addr;
if (IS_INSN (word)) {
fprintf (of, "к ");
besm6_fprint_cmd (of, word >> 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);
}

1243
BESM6/besm6_tty.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -68,6 +68,8 @@ ifneq (,$(or $(findstring pdp11,$(MAKECMDGOALS)),$(findstring vax,$(MAKECMDGOALS
VIDEO_USEFUL = true VIDEO_USEFUL = true
DISPLAY_USEFUL = true DISPLAY_USEFUL = true
endif endif
else ifeq ($(MAKECMDGOALS),besm6)
VIDEO_USEFUL = true
else else
ifeq ($(MAKECMDGOALS),) ifeq ($(MAKECMDGOALS),)
# default target is all # default target is all
@ -1159,6 +1161,24 @@ SSEMD = SSEM
SSEM = ${SSEMD}/ssem_cpu.c ${SSEMD}/ssem_sys.c SSEM = ${SSEMD}/ssem_cpu.c ${SSEMD}/ssem_sys.c
SSEM_OPT = -I ${SSEMD} 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 ### Unsupported/Incomplete simulators
### ###
@ -1462,6 +1482,12 @@ ${BIN}ssem${EXE} : ${SSEM} ${SIM}
${MKDIRBIN} ${MKDIRBIN}
${CC} ${SSEM} ${SIM} ${SSEM_OPT} $(CC_OUTSPEC) ${LDFLAGS} ${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} sigma : ${BIN}sigma${EXE}
${BIN}sigma${EXE} : ${SIGMA} ${SIM} ${BIN}sigma${EXE} : ${SIGMA} ${SIM}