From 8f6e849ecfa82189bdd60be9724ec1a798e42db4 Mon Sep 17 00:00:00 2001 From: Leo Broukhis Date: Sun, 11 Jan 2015 17:43:39 -0800 Subject: [PATCH] BESM6: Implemented WRU when no local console, added binary punchtape mode, translated some comments. --- BESM6/besm6_cpu.c | 223 +++++++++++++++++++++++++------------------- BESM6/besm6_defs.h | 47 ++++++---- BESM6/besm6_punch.c | 108 ++++++++++++++------- BESM6/besm6_tty.c | 16 ++++ BESM6/dispak.ini | 70 ++++++++++---- BESM6/expect.ini | 7 +- BESM6/input.txt | 4 +- 7 files changed, 302 insertions(+), 173 deletions(-) diff --git a/BESM6/besm6_cpu.c b/BESM6/besm6_cpu.c index e92cef46..9b7295f0 100644 --- a/BESM6/besm6_cpu.c +++ b/BESM6/besm6_cpu.c @@ -65,16 +65,27 @@ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ extern const char *scp_errors[]; -/* нехранящие биты ГРП должны сбрасываться путем обнуления тех регистров, - * сборкой которых они являются +/* Wired (non-registered) bits of interrupt registers (GRP and PRP) + * cannot be cleared by writing to the GRP and must be cleared by clearing + * the registers generating the corresponding interrupts. */ -#define GRP_WIRED_BITS 01400743700000000LL +#define GRP_WIRED_BITS (GRP_DRUM1_FREE | GRP_DRUM2_FREE |\ + GRP_CHAN3_DONE | GRP_CHAN4_DONE |\ + GRP_CHAN5_DONE | GRP_CHAN6_DONE |\ + GRP_CHAN3_FREE | GRP_CHAN4_FREE |\ + GRP_CHAN5_FREE | GRP_CHAN6_FREE |\ + GRP_CHAN7_FREE ) -#define PRP_WIRED_BITS 0770000 +/* So far irrelevant as none of the devices - + * punchcard I/O and punchtape output - had been implemented. + */ +#define PRP_WIRED_BITS (PRP_UVVK1_END | PRP_UVVK2_END |\ + PRP_PCARD1_CHECK | PRP_PCARD2_CHECK |\ + PRP_PCARD1_PUNCH | PRP_PCARD2_PUNCH |\ + PRP_PTAPE1_PUNCH | PRP_PTAPE2_PUNCH ) int corr_stack; int redraw_panel; -uint32 delay; jmp_buf cpu_halt; t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); @@ -94,13 +105,13 @@ t_stat cpu_req (UNIT *u, int32 val, char *cptr, void *desc); UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MEMSIZE) }; REG cpu_reg[] = { - { "СчАС", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ - { "РК", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ - { "Аисп", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ - { "СМ", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ - { "РМР", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ - { "РАУ", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ - { "М1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ + { "СчАС", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ + { "РК", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ + { "Аисп", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ + { "СМ", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ + { "РМР", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ + { "РАУ", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ + { "М1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ { "М2", &M[2], 8, 15, 0, 1 }, { "М3", &M[3], 8, 15, 0, 1 }, { "М4", &M[4], 8, 15, 0, 1 }, @@ -114,24 +125,24 @@ REG cpu_reg[] = { { "М14", &M[014], 8, 15, 0, 1 }, { "М15", &M[015], 8, 15, 0, 1 }, { "М16", &M[016], 8, 15, 0, 1 }, - { "М17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ - { "М20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ - { "М21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ - { "М27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ - { "М32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ - { "М33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ - { "М34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ - { "М35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ - { "РУУ", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ - { "ГРП", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ - { "МГРП", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ - { "ПРП", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ - { "МПРП", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ + { "М17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ + { "М20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ + { "М21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ + { "М27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ + { "М32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ + { "М33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ + { "М34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ + { "М35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ + { "РУУ", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ + { "ГРП", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ + { "МГРП", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ + { "ПРП", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ + { "МПРП", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ { 0 } }; MTAB cpu_mod[] = { - { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle, NULL, "Display idle detection mode" }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle, NULL, "Enables idle detection mode" }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL, NULL, "Disables idle detection" }, { MTAB_XTD|MTAB_VDV, 0, NULL, "REQ", &cpu_req, NULL, NULL, "Sends a request interrupt" }, { MTAB_XTD|MTAB_VDV, 0, "PANEL", "PANEL", &besm6_init_panel, NULL, NULL, "Displays graphical panel" }, @@ -148,16 +159,16 @@ DEVICE cpu_dev = { }; /* - * REG: псевдоустройство, содержащее латинские синонимы всех регистров. + * REG: A pseudo-device containing Latin synonyms of all CPU registers. */ REG reg_reg[] = { - { "PC", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ - { "RK", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ - { "Aex", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ - { "ACC", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ - { "RMR", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ - { "RAU", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ - { "M1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ + { "PC", &PC, 8, 15, 0, 1 }, /* program counter */ + { "RK", &RK, 8, 24, 0, 1 }, /* instruction register */ + { "Aex", &Aex, 8, 15, 0, 1 }, /* effective address */ + { "ACC", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* accumulator */ + { "RMR", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* LSB register */ + { "RAU", &RAU, 2, 6, 0, 1 }, /* ALU modes */ + { "M1", &M[1], 8, 15, 0, 1 }, /* index (modifier) registers */ { "M2", &M[2], 8, 15, 0, 1 }, { "M3", &M[3], 8, 15, 0, 1 }, { "M4", &M[4], 8, 15, 0, 1 }, @@ -171,19 +182,19 @@ REG reg_reg[] = { { "M14", &M[014], 8, 15, 0, 1 }, { "M15", &M[015], 8, 15, 0, 1 }, { "M16", &M[016], 8, 15, 0, 1 }, - { "M17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ - { "M20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ - { "M21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ - { "M27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ - { "M32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ - { "M33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ - { "M34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ - { "M35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ - { "RUU", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ - { "GRP", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ - { "MGRP", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ - { "PRP", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ - { "MPRP", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ + { "M17", &M[017], 8, 15, 0, 1 }, /* also the stack pointer */ + { "M20", &M[020], 8, 15, 0, 1 }, /* MOD - address modifier register */ + { "M21", &M[021], 8, 15, 0, 1 }, /* PSW - CU modes */ + { "M27", &M[027], 8, 15, 0, 1 }, /* SPSW - saved CU modes */ + { "M32", &M[032], 8, 15, 0, 1 }, /* ERET - extracode return address */ + { "M33", &M[033], 8, 15, 0, 1 }, /* IRET - interrupt return address */ + { "M34", &M[034], 8, 16, 0, 1 }, /* IBP - instruction bkpt address */ + { "M35", &M[035], 8, 16, 0, 1 }, /* DWP - watchpoint address */ + { "RUU", &RUU, 2, 9, 0, 1 }, /* execution modes */ + { "GRP", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* main interrupt reg */ + { "MGRP", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* mask of the above */ + { "PRP", &PRP, 8, 24, 0, 1 }, /* peripheral interrupt reg */ + { "MPRP", &MPRP, 8, 24, 0, 1 }, /* mask of the above*/ { "BRZ0", &BRZ[0], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, { "BRZ1", &BRZ[1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, @@ -245,7 +256,7 @@ char sim_name[] = "БЭСМ-6"; REG *sim_PC = &cpu_reg[0]; -int32 sim_emax = 1; /* максимальное количество слов в машинной команде */ +int32 sim_emax = 1; /* max number of addressable units per instruction */ DEVICE *sim_devices[] = { &cpu_dev, @@ -261,27 +272,27 @@ DEVICE *sim_devices[] = { }; const char *sim_stop_messages[] = { - "Неизвестная ошибка", /* Unknown error */ - "Останов", /* STOP */ - "Точка останова", /* Emulator breakpoint */ - "Точка останова по считыванию", /* Emulator read watchpoint */ - "Точка останова по записи", /* Emulator write watchpoint */ - "Выход за пределы памяти", /* Run out end of memory */ - "Запрещенная команда", /* Invalid instruction */ - "Контроль команды", /* A data-tagged word fetched */ - "Команда в чужом листе", /* Paging error during fetch */ - "Число в чужом листе", /* Paging error during load/store */ - "Контроль числа МОЗУ", /* RAM parity error */ - "Контроль числа БРЗ", /* Write cache parity error */ - "Переполнение АУ", /* Arith. overflow */ - "Деление на нуль", /* Division by zero or denorm */ - "Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */ - "Чтение неформатированного барабана", /* Reading unformatted drum */ - "Чтение неформатированного диска", /* Reading unformatted disk */ - "Останов по КРА", /* Hardware breakpoint */ - "Останов по считыванию", /* Load watchpoint */ - "Останов по записи", /* Store watchpoint */ - "Не реализовано", /* Unimplemented I/O or special reg. access */ + "Неизвестная ошибка", /* Unknown error */ + "Останов", /* STOP */ + "Точка останова", /* Emulator breakpoint */ + "Точка останова по считыванию", /* Emulator read watchpoint */ + "Точка останова по записи", /* Emulator write watchpoint */ + "Выход за пределы памяти", /* Run out end of memory */ + "Запрещенная команда", /* Invalid instruction */ + "Контроль команды", /* A data-tagged word fetched */ + "Команда в чужом листе", /* Paging error during fetch */ + "Число в чужом листе", /* Paging error during load/store */ + "Контроль числа МОЗУ", /* RAM parity error */ + "Контроль числа БРЗ", /* Write cache parity error */ + "Переполнение АУ", /* Arith. overflow */ + "Деление на нуль", /* Division by zero or denorm */ + "Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */ + "Чтение неформатированного барабана", /* Reading unformatted drum */ + "Чтение неформатированного диска", /* Reading unformatted disk */ + "Останов по КРА", /* Hardware breakpoint */ + "Останов по считыванию", /* Load watchpoint */ + "Останов по записи", /* Store watchpoint */ + "Не реализовано", /* Unimplemented I/O or special reg. access */ }; /* @@ -328,7 +339,7 @@ t_stat cpu_reset (DEVICE *dptr) for (i=0; i BITS(15)) { /* выход за пределы памяти */ + if (PC > BITS(15) && IS_SUPERVISOR(RUU)) { + /* + * Runaway instruction execution in supervisor mode + * warrants attention. + */ besm6_draw_panel(); return STOP_RUNOUT; /* stop simulation */ } @@ -1680,7 +1707,8 @@ t_stat sim_instr (void) } if (PRP & MPRP) { - /* регистр хранящий, сбрасывается программно */ + /* There are interrupts pending in the peripheral + * interrupt register */ GRP |= GRP_SLAVE; } @@ -1701,8 +1729,10 @@ t_stat sim_instr (void) } /* - * В 9-й части частота таймера 250 Гц (4 мс), - * в жизни - 50 Гц (20 мс). + * A 250 Hz clock as per the original documentation, + * and matching the available software binaries. + * Some installations used 50 Hz with a modified OS + * for a better user time/system time ratio. */ t_stat fast_clk (UNIT * this) { @@ -1712,18 +1742,17 @@ t_stat fast_clk (UNIT * this) ++counter; ++tty_counter; - /*besm6_debug ("*** таймер 20 мсек");*/ GRP |= GRP_TIMER; - /* Медленный таймер: должен быть 16 Гц. - * Но от него почему-то зависит вывод на терминалы, - * поэтому ускорим. */ - if ((counter & 3) == 0) { - /*besm6_debug ("*** таймер 80 мсек");*/ + if ((counter & 15) == 0) { + /* + * The OS used the (undocumented, later addition) slow clock interrupt to initiate servicing + * terminal I/O. Its frequency was reportedly 16 Hz; 64 ms is a good enough approximation. + */ GRP |= GRP_SLOW_CLK; } - /* Перерисовка панели каждые 64 миллисекунды. */ + /* Requesting panel redraw every 64 ms. */ if ((counter & 15) == 0) { redraw_panel = 1; } @@ -1741,7 +1770,7 @@ t_stat fast_clk (UNIT * this) } UNIT clocks[] = { - { UDATA(fast_clk, UNIT_IDLE, 0), CLK_DELAY }, /* 40 р, 50 Гц */ + { UDATA(fast_clk, UNIT_IDLE, 0), CLK_DELAY }, /* Bit 40 of the GRP, 250 Hz */ }; t_stat clk_reset (DEVICE * dev) diff --git a/BESM6/besm6_defs.h b/BESM6/besm6_defs.h index 8be817d2..ebae7176 100644 --- a/BESM6/besm6_defs.h +++ b/BESM6/besm6_defs.h @@ -114,19 +114,15 @@ enum { ((x) >> 48) == CONVOL_NUMBER) /* - * Вычисление правдоподобного времени выполнения команды, - * зная количество тактов в УУ и среднее в АУ. - * Предполагаем, что в 50% случаев происходит совмещение - * выполнения, поэтому суммируем большее и половину - * от меньшего значения. + * An attempt to approximate instruction execution times. + * The arguments number of clock ticks spent on an instruction + * in the ALU and in the CU; the computed result assumes + * a 50% overlap in execution. */ #define MEAN_TIME(x,y) (x>y ? x+y/2 : x/2+y) -/* - * Считаем, что моделируеммая машина имеет опорную частоту 10 МГц. - */ -#define USEC 1 /* одна микросекунда - десять тактов */ -#define MSEC (1000*USEC) /* одна миллисекунда */ +#define USEC 1 /* 1 microsecond */ +#define MSEC (1000*USEC) /* 1 millisecond */ #define CLK_TPS 250 /* Fast Clock Ticks Per Second (every 4ms) */ #define CLK_DELAY 4000 /* Uncalibrated instructions per clock tick */ @@ -135,6 +131,7 @@ extern UNIT tty_unit[]; extern UNIT clocks[]; 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; @@ -379,14 +376,15 @@ t_value besm6_pack (t_value val, t_value mask); t_value besm6_unpack (t_value val, t_value mask); /* - * Разряды главного регистра прерываний (ГРП) - * Внешние: + * Bits of the main interrupt register ГРП (GRP) + * External: */ #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_UVVK1_SYNC 00200000000000000LL /* 44 */ +#define GRP_UVVK2_SYNC 00100000000000000LL /* 43 */ #define GRP_FS1_SYNC 00040000000000000LL /* 42 */ #define GRP_FS2_SYNC 00020000000000000LL /* 41 */ #define GRP_TIMER 00010000000000000LL /* 40 */ @@ -405,10 +403,10 @@ t_value besm6_unpack (t_value val, t_value mask); #define GRP_CHAN5_FREE 00000000400000000LL /* 27 */ #define GRP_CHAN6_FREE 00000000200000000LL /* 26 */ #define GRP_CHAN7_FREE 00000000100000000LL /* 25 */ -#define GRP_SERIAL 00000000001000000LL /* 19 */ +#define GRP_SERIAL 00000000001000000LL /* 19, nonstandard */ #define GRP_WATCHDOG 00000000000002000LL /* 11 */ -#define GRP_SLOW_CLK 00000000000001000LL /* 10 */ -/* Внутренние: */ +#define GRP_SLOW_CLK 00000000000001000LL /* 10, nonstandard */ +/* Internal: */ #define GRP_DIVZERO 00000000034000000LL /* 23-21 */ #define GRP_OVERFLOW 00000000014000000LL /* 22-21 */ #define GRP_CHECK 00000000004000000LL /* 21 */ @@ -426,6 +424,23 @@ t_value besm6_unpack (t_value val, t_value mask); #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)) +/* + * Bits of the peripheral interrupt register ПРП (PRP) + */ +#define PRP_UVVK1_END 010000000 /* 22 */ +#define PRP_UVVK2_END 004000000 /* 21 */ +#define PRP_PCARD1_CHECK 002000000 /* 20 */ +#define PRP_PCARD2_CHECK 001000000 /* 19 */ +#define PRP_PCARD1_PUNCH 000400000 /* 18 */ +#define PRP_PCARD2_PUNCH 000200000 /* 17 */ +#define PRP_PTAPE1_PUNCH 000100000 /* 16 */ +#define PRP_PTAPE2_PUNCH 000040000 /* 15 */ + /* 14-13 unused */ +#define PRP_CONS1_INPUT 000004000 /* 12 */ +#define PRP_CONS2_INPUT 000002000 /* 11 */ +#define PRP_CONS1_DONE 000001000 /* 10 */ +#define PRP_CONS2_DONE 000000400 /* 9 */ + /* Номер блока ОЗУ или номер страницы, вызвавших прерывание */ extern uint32 iintr_data; diff --git a/BESM6/besm6_punch.c b/BESM6/besm6_punch.c index add2d5bb..aa5579e5 100644 --- a/BESM6/besm6_punch.c +++ b/BESM6/besm6_punch.c @@ -89,6 +89,7 @@ DEVICE fs_dev = { enum { FS_IDLE, FS_STARTING, + FS_BINARY, FS_RUNNING, FS_IMAGE, FS_IMAGE_LAST = FS_IMAGE + CARD_LEN - 1, @@ -100,6 +101,8 @@ enum { FS_TAIL, } fs_state[2]; +int fs_textmode[2]; + /* * Reset routine */ @@ -116,10 +119,16 @@ t_stat fs_reset (DEVICE *dptr) return SCPE_OK; } +/* + * Attaches a raw binary file by default, + * with a -t switch attaches a prepared text file in UTF-8. + */ t_stat fs_attach (UNIT *u, char *cptr) { t_stat s; int num = u - fs_unit; + fs_textmode[num] = sim_switches & SWMASK('T'); + sim_switches &= ~SWMASK('T'); s = attach_unit (u, cptr); if (s != SCPE_OK) return s; @@ -186,6 +195,20 @@ void fs_control (int num, uint32 cmd) } unsigned char unicode_to_gost (unsigned short val); + +/* + * The UPP code is the GOST 10859 code with odd parity. + * UPP stood for "unit for preparation of punchards". + */ +static unsigned char unicode_to_upp (unsigned short ch) { + unsigned char ret; + ch = ret = unicode_to_gost (ch); + ch = (ch & 0x55) + ((ch >> 1) & 0x55); + ch = (ch & 0x33) + ((ch >> 2) & 0x33); + ch = (ch & 0x0F) + ((ch >> 4) & 0x0F); + return (ch & 1) ? ret : ret | 0x80; +} + static int utf8_getc (FILE *fin); /* @@ -194,64 +217,66 @@ static int utf8_getc (FILE *fin); */ t_stat fs_event (UNIT *u) { + static int cnt; int num = u - fs_unit; again: if (fs_state[num] == FS_STARTING) { - /* По первому прерыванию после запуска двигателя ничего не читаем */ + /* The first interrupt after starting the motor is dummy, + * no need to read anything from the attached file. + */ FS[num] = 0; - fs_state[num] = FS_RUNNING; - } else if (fs_state[num] == FS_RUNNING) { - int ch; - /* переводы строк игнорируются */ - while ((ch = utf8_getc (u->fileref)) == '\n'); + cnt = 0; + fs_state[num] = fs_textmode[num] ? FS_RUNNING : FS_BINARY; + } else if (fs_state[num] == FS_BINARY) { + int ch = getc (u->fileref); if (ch < 0) { - /* хвост ленты без пробивок */ FS[num] = 0; fs_state[num] = FS_TAIL; - } else if (ch == '\f') { + } else { + FS[num] = ch; + } + } else if (fs_state[num] == FS_RUNNING) { + int ch; + /* Line separators are ignored in running text mode */ + do ch = utf8_getc (u->fileref); while (ch == '\n' || ch == '\r'); + if (ch < 0) { + /* the tail end of the tape has no holes */ + FS[num] = 0; + fs_state[num] = FS_TAIL; + } else if (ch == (']' & 037)) { + /* Switching from running text mode to "virtual punchcard" mode and back + * is done with an ASCII GS (group separator) symbol ctrl-]. + */ 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; + FS[num] = unicode_to_upp (ch); } } else if (FS_IMAGE <= fs_state[num] && fs_state[num] <= FS_IMAGE_LAST) { int ch = utf8_getc (u->fileref); if (ch < 0) { - /* обрыв ленты */ + /* premature end of tape */ FS[num] = 0; fs_state[num] = FS_TAIL; + } else if (ch == '\r') { + /* always ignored */ + goto again; } else if (ch == '\n') { - /* идем дополнять образ карты нулевыми байтами */ + /* Start returning zero bytes up to the end of the current "virtual punchard" */ fs_state[num] = FS_FILLUP + (fs_state[num] - FS_IMAGE); goto again; - } else if (ch == '\f') { + } else if (ch == (']' & 037)) { 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]; + FS[num] = unicode_to_upp (ch); + if (++fs_state[num] == FS_TOOLONG) { + /* If a line is too long (> 120 chars), start the next "virtual punchcard" */ + fs_state[num] = FS_IMAGE; + } } - } else if (fs_state[num] == 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; } else if (FS_FILLUP <= fs_state[num] && fs_state[num] <= FS_FILLUP_LAST) { FS[num] = 0; if (++fs_state[num] == FS_ENDA3) { @@ -278,15 +303,21 @@ int fs_read(int num) { return FS[num]; } - +/* + * Unlike the OS which uses GOST overline (approximated by ^) as a line separator + * in running text mode, the BESM-ALGOL programming system used a nonprintable + * character (0174) from the unused part of the codetable to allow compressing multiple + * source lines on a punchcard. To specify that character, + * we use ASCII RS (record separator) symbol ctrl-^. + */ 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, + /* 08 - 0f */ 017, 017, 0214, 017, 017, 017, 017, 017, /* 10 - 17 */ 017, 017, 017, 017, 017, 017, 017, 017, - /* 18 - 1f */ 017, 017, 017, 017, 017, 017, 017, 017, + /* 18 - 1f */ 017, 017, 017, 017, 017, 017, 0174, 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, @@ -413,6 +444,11 @@ unicode_to_gost (unsigned short val) case 0x83: return 0122; } break; + case 0x23: + switch ((unsigned char) val) { + case 0xe8: return 0020; + } + break; case 0x25: switch ((unsigned char) val) { case 0xc7: return 0127; diff --git a/BESM6/besm6_tty.c b/BESM6/besm6_tty.c index 418f257b..0f0e661f 100644 --- a/BESM6/besm6_tty.c +++ b/BESM6/besm6_tty.c @@ -97,6 +97,8 @@ t_stat vt_clk(UNIT *); extern char *get_sim_sw (char *cptr); extern int32 tmr_poll; /* calibrated clock timer poll */ +int attached_console; + UNIT tty_unit [] = { { UDATA (vt_clk, UNIT_DIS|UNIT_IDLE, 0) }, /* fake unit, clock */ { UDATA (NULL, UNIT_SEQ, 0) }, @@ -228,6 +230,19 @@ t_stat vt_clk (UNIT * this) t->rxb [t->rxbpi++] = '\3'; } + /* + * It the operator console is remote, we still need to probe the local keyboard + * for a WRU, say, 10 times a second. + */ + if (!attached_console) { + static int divider; + if (++divider == CLK_TPS/10) { + divider == 0; + if (SCPE_STOP == sim_poll_kbd()) + stop_cpu = 1; + } + } + /* Polling sockets for transmission. */ tmxr_poll_tx (&tty_desc); /* If the TTY system is not idle, schedule the next interrupt @@ -343,6 +358,7 @@ t_stat tty_attach (UNIT *u, char *cptr) if (num <= TTY_MAX) vt_mask |= 1 << (TTY_MAX - num); besm6_debug ("*** console on T%03o", num); + attached_console = 1; return SCPE_OK; } return SCPE_ALATT; diff --git a/BESM6/dispak.ini b/BESM6/dispak.ini index ac2170c2..d9d41b39 100644 --- a/BESM6/dispak.ini +++ b/BESM6/dispak.ini @@ -7,18 +7,18 @@ set cpu idle ;set disk debug ; -; Приводим барабаны в исходное состояние. +; Initializing the magnetic drums. ; attach -n drum0 drum1x.bin attach -n drum1 drum2x.bin ; -; Создаем рабочий диск. +; Initializing a scratch disk. ; attach -n disk6 2052.bin ; -; Подключаем диски. +; Attaching system disks. ; attach -e disk7 sbor2053.bin attach -e disk5 krab2063.bin @@ -27,27 +27,47 @@ attach -e disk1 svs2048.bin attach -e disk2 alt2048.bin ; -; Подключаем АЦПУ. +; Attaching an output file. ; -attach prn0 output.txt +attach -n prn0 output.txt ; -; Активируем операторский терминал, -; на который идут сообщения. +; Allowing telnet connections, port 4199. ; +attach tty 4199 + +; +; Attaching a terminal serving as the operator console. +; +; This works on UNIX-like systems. attach tty1 console +; On Windows, a UTF-8 connnection works better over telnet. +; Use a different port for the operator console just in case. +;attach tty Line=1,4198 +;set env PATH %PATH%;C:\Program Files (x86)\PuTTY +;! start putty telnet://localhost:4198 ; -; Режимы по вкусу +; On Unix, telnet can also be used for the operator console. +; ! gnome-terminal -x sh -c "telnet localhost 4198" & ; +; Terminal modes (how to enter Cyrillics, should the backspace be erasing), etc. +; (authbs == authentic non-erasing) +; +; Using UTF-8 for input +; set tty1 unicode,authbs +; +; Entering Russian letters as lowercase Latin letters +; according to the standard Russian layout ;set tty1 jcuken,authbs +; +; Entering Russian letters as corresponding lowercase Latin letters. +; Q = "ya", W = "ve", Y = "yeru", J = "short I", X = "soft sign", +; C = "ts", V = "zhe", grave = "yu", tilde = "ch", { = "sh", } = "shch", +; | = "reverse e" set tty1 qwerty,authbs set -n tty1 log=tty1.txt -; -; Разрешаем подключение пользователей по telnet, порт 4199. -; -attach tty 4199 set tty2 authbs set tty3 authbs set tty4 authbs @@ -73,18 +93,30 @@ set tty23 authbs set tty24 authbs ; -; Включение БРС/БРЗ для совместимости. -; Замедляет работу на 20%. +; Enabling the true LRU behavior of caches +; slows down the simulation speed ~20% ; ;set mmu cache +echo ### +echo ### Zeroing out the first page of RAM (as would be entered from +echo ### switch registers after powering up the machine, +echo ### likely about once a year or less). +echo ### + +d -ml 1 xta, vtm 1777(1) +d -ml 2 atx (1), utm -1(1) +d -ml 3 v1m 2(1), stop +run 1 + +echo ### +echo ### Done; data cache registers are displayed above. An error message +echo ### would be usually displayed there after a STOP instruction. +echo ### Naturally, at the moment they all contain zeros. Booting the OS now... +echo ### ; -; Запуск ОС ДИСПАК. +; Booting OS DISPAK. ; load boot_dispak.b6 - -; Происходит контроль числа по адресу 1031 -d 1031 0 - run 2000 ;quit diff --git a/BESM6/expect.ini b/BESM6/expect.ini index 368ba9cb..cad2d9b0 100755 --- a/BESM6/expect.ini +++ b/BESM6/expect.ini @@ -1,10 +1,10 @@ # Attaching the input device -at fs0 input.txt +at -t fs0 input.txt # Waiting for the end of initial setup expect -r "ДATA.*\n" do dispak.ini # Now we're ready to send commands from the front panel -echo Requesting input from the punch tape reader (FS8) +echo Requesting input from the punch tape reader (FS8), it takes a few seconds d 6 1 d 5 10 set cpu req @@ -14,6 +14,7 @@ go # The process had been started, check state every 10 model seconds send after=1000000 "WCPP\r" expect -p -r "4199.*\n" send after=10000000 "WCPP\r"; go +expect -c "KЗ" step 10000000 expect -c "HET\r\n" step 10000 go echo Enabling the printer (ONL A0) @@ -24,7 +25,7 @@ expect -r "ECT.*\n" go echo Checking for the printing to finish send "HOMB\r" -expect -p -r "4199.*\n" send "HOMB\r"; go +expect -p -r "A0.*\n" send "HOMB\r"; go expect -c "HET\r\n" go echo Done diff --git a/BESM6/input.txt b/BESM6/input.txt index 708c9a92..bf9598f0 100644 --- a/BESM6/input.txt +++ b/BESM6/input.txt @@ -1,6 +1,6 @@ ШИФР 419999 ЗС5^ EEВ1А3 - *NAME PRIME NUMBERS +*NAME PRIME NUMBERS * The ^L char before *NAME is important * NO LIST Disable source listing by removing spaces between * and NO *NO LOAD LIST Enable loader listing by adding 5 spaces between * and NO @@ -50,4 +50,4 @@ c------ Printing every 1000th prime number *EXECUTE * The ^L char after *END FILE is important *END FILE - ЕКОНЕЦ +ЕКОНЕЦ