simh-testsetgenerator/BESM6/besm6_cpu.c

1853 lines
69 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

/*
* BESM-6 CPU simulator.
*
* Copyright (c) 1997-2009, Leonid Broukhis
* Copyright (c) 2009, Serge Vakulenko
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SERGE VAKULENKO OR LEONID BROUKHIS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
* Except as contained in this notice, the name of Leonid Broukhis or
* Serge Vakulenko shall not be used in advertising or otherwise to promote
* the sale, use or other dealings in this Software without prior written
* authorization from Leonid Broukhis and Serge Vakulenko.
* For more information about BESM-6 computer, visit sites:
* - http://www.computer-museum.ru/english/besm6.htm
* - http://mailcom.com/besm6/
* - http://groups.google.com/group/besm6
*
* Release notes for BESM-6/SIMH
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 1) All addresses and data values are displayed in octal.
* 2) Memory size is 128 kwords.
* 3) Interrupt system is to be synchronized with wallclock time.
* 4) Execution times are in 1/10 of microsecond.
* 5) Magnetic drums are implemented as a single "DRUM" device.
* 6) Magnetic disks are implemented.
* 7) Magnetic tape is not implemented.
* 8) Punch tape reader is implemented, punch card reader is planned.
* 9) Card puncher is not implemented.
* 10) Displays are implemented.
* 11) Printer АЦПУ-128 is implemented.
* 12) Instruction mnemonics, register names and stop messages
* are in Russian using UTF-8 encoding. It is assumed, that
* user locale is UTF-8.
* 13) A lot of comments in Russian (UTF-8).
*/
#include "besm6_defs.h"
#include <math.h>
#include <float.h>
t_value memory [MEMSIZE];
uint32 PC, RK, Aex, M [NREGS], RAU, RUU;
t_value ACC, RMR, GRP, MGRP;
uint32 PRP, MPRP;
uint32 READY, READY2; /* ready flags of various devices */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
/* 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 (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 (PRP_UVVK1_END | PRP_UVVK2_END |\
PRP_PCARD1_PUNCH | PRP_PCARD2_PUNCH |\
PRP_PTAPE1_PUNCH | PRP_PTAPE2_PUNCH )
int corr_stack;
int redraw_panel;
jmp_buf cpu_halt;
t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_req (UNIT *u, int32 val, CONST char *cptr, void *desc);
t_stat cpu_set_pult (UNIT *u, int32 val, CONST char *cptr, void *desc);
t_stat cpu_show_pult (FILE *st, UNIT *up, int32 v, CONST void *dp);
/*
* CPU data structures
*
* cpu_dev CPU device descriptor
* cpu_unit CPU unit descriptor
* cpu_reg CPU register list
* cpu_mod CPU modifiers list
*/
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MEMSIZE) };
#define ORDATAVM(nm,loc,wd) REGDATA(nm,(loc),8,wd,0,1,NULL,NULL,REG_VMIO,0,0)
REG cpu_reg[] = {
{ ORDATA ( "СчАС", PC, 15) }, /* счётчик адреса команды */
{ ORDATA ( "РК", RK, 24) }, /* регистр выполняемой команды */
{ ORDATA ( "Аисп", Aex, 15) }, /* исполнительный адрес */
{ ORDATAVM ( "СМ", ACC, 48) }, /* сумматор */
{ ORDATAVM ( "РМР", RMR, 48) }, /* регистр младших разрядов */
{ BINRDATA ( "РАУ", RAU, 6) }, /* режимы АУ */
{ ORDATA ( "М1", M[1], 15) }, /* регистры-модификаторы */
{ ORDATA ( "М2", M[2], 15) },
{ ORDATA ( "М3", M[3], 15) },
{ ORDATA ( "М4", M[4], 15) },
{ ORDATA ( "М5", M[5], 15) },
{ ORDATA ( "М6", M[6], 15) },
{ ORDATA ( "М7", M[7], 15) },
{ ORDATA ( "М10", M[010], 15) },
{ ORDATA ( "М11", M[011], 15) },
{ ORDATA ( "М12", M[012], 15) },
{ ORDATA ( "М13", M[013], 15) },
{ ORDATA ( "М14", M[014], 15) },
{ ORDATA ( "М15", M[015], 15) },
{ ORDATA ( "М16", M[016], 15) },
{ ORDATA ( "М17", M[017], 15) }, /* указатель магазина */
{ ORDATA ( "М20", M[020], 15) }, /* MOD - модификатор адреса */
{ ORDATA ( "М21", M[021], 15) }, /* PSW - режимы УУ */
{ ORDATA ( "М27", M[027], 15) }, /* SPSW - упрятывание режимов УУ */
{ ORDATA ( "М32", M[032], 15) }, /* ERET - адрес возврата из экстракода */
{ ORDATA ( "М33", M[033], 15) }, /* IRET - адрес возврата из прерывания */
{ ORDATA ( "М34", M[034], 16) }, /* IBP - адрес прерывания по выполнению */
{ ORDATA ( "М35", M[035], 16) }, /* DWP - адрес прерывания по чтению/записи */
{ BINRDATA ( "РУУ", RUU, 9) }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */
{ ORDATAVM ( "ГРП", GRP, 48) }, /* главный регистр прерываний */
{ ORDATAVM ( "МГРП", MGRP, 48) }, /* маска ГРП */
{ ORDATA ( "ПРП", PRP, 24) }, /* периферийный регистр прерываний */
{ ORDATA ( "МПРП", MPRP, 24) }, /* маска ПРП */
{ 0 }
};
MTAB cpu_mod[] = {
{ 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, &besm6_show_panel, NULL,
"Displays graphical panel" },
{ MTAB_XTD|MTAB_VDV,
0, NULL, "NOPANEL", &besm6_close_panel, NULL, NULL,
"Closes graphical panel" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALO,
0, "PULT", "PULT", &cpu_set_pult, &cpu_show_pult, NULL,
"Selects a hardwired program or switch reg." },
{ 0 }
};
DEVICE cpu_dev = {
"CPU", &cpu_unit, cpu_reg, cpu_mod,
1, 8, 17, 1, 8, 50,
&cpu_examine, &cpu_deposit, &cpu_reset,
NULL, NULL, NULL, NULL,
DEV_DEBUG
};
/*
* REG: A pseudo-device containing Latin synonyms of all CPU registers.
*/
REG reg_reg[] = {
{ ORDATA ( "PC", PC, 15) }, /* program counter */
{ ORDATA ( "RK", RK, 24) }, /* instruction register */
{ ORDATA ( "Aex", Aex, 15) }, /* effective address */
{ ORDATAVM ( "ACC", ACC, 48) }, /* accumulator */
{ ORDATAVM ( "RMR", RMR, 48) }, /* LSB register */
{ BINRDATA ( "RAU", RAU, 6) }, /* ALU modes */
{ ORDATA ( "M1", M[1], 15) }, /* index (modifier) registers */
{ ORDATA ( "M2", M[2], 15) },
{ ORDATA ( "M3", M[3], 15) },
{ ORDATA ( "M4", M[4], 15) },
{ ORDATA ( "M5", M[5], 15) },
{ ORDATA ( "M6", M[6], 15) },
{ ORDATA ( "M7", M[7], 15) },
{ ORDATA ( "M10", M[010], 15) },
{ ORDATA ( "M11", M[011], 15) },
{ ORDATA ( "M12", M[012], 15) },
{ ORDATA ( "M13", M[013], 15) },
{ ORDATA ( "M14", M[014], 15) },
{ ORDATA ( "M15", M[015], 15) },
{ ORDATA ( "M16", M[016], 15) },
{ ORDATA ( "M17", M[017], 15) }, /* also the stack pointer */
{ ORDATA ( "M20", M[020], 15) }, /* MOD - address modifier register */
{ ORDATA ( "M21", M[021], 15) }, /* PSW - CU modes */
{ ORDATA ( "M27", M[027], 15) }, /* SPSW - saved CU modes */
{ ORDATA ( "M32", M[032], 15) }, /* ERET - extracode return address */
{ ORDATA ( "M33", M[033], 15) }, /* IRET - interrupt return address */
{ ORDATA ( "M34", M[034], 16) }, /* IBP - instruction bkpt address */
{ ORDATA ( "M35", M[035], 16) }, /* DWP - watchpoint address */
{ BINRDATA ( "RUU", RUU, 9) }, /* execution modes */
{ ORDATAVM ( "GRP", GRP, 48) }, /* main interrupt reg */
{ ORDATAVM ( "MGRP", MGRP, 48) }, /* mask of the above */
{ ORDATA ( "PRP", PRP, 24) }, /* peripheral interrupt reg */
{ ORDATA ( "MPRP", MPRP, 24) }, /* mask of the above*/
{ ORDATAVM ( "BRZ0", BRZ[0], 50) },
{ ORDATAVM ( "BRZ1", BRZ[1], 50) },
{ ORDATAVM ( "BRZ2", BRZ[2], 50) },
{ ORDATAVM ( "BRZ3", BRZ[3], 50) },
{ ORDATAVM ( "BRZ4", BRZ[4], 50) },
{ ORDATAVM ( "BRZ5", BRZ[5], 50) },
{ ORDATAVM ( "BRZ6", BRZ[6], 50) },
{ ORDATAVM ( "BRZ7", BRZ[7], 50) },
{ ORDATA ( "BAZ0", BAZ[0], 16) },
{ ORDATA ( "BAZ1", BAZ[1], 16) },
{ ORDATA ( "BAZ2", BAZ[2], 16) },
{ ORDATA ( "BAZ3", BAZ[3], 16) },
{ ORDATA ( "BAZ4", BAZ[4], 16) },
{ ORDATA ( "BAZ5", BAZ[5], 16) },
{ ORDATA ( "BAZ6", BAZ[6], 16) },
{ ORDATA ( "BAZ7", BAZ[7], 16) },
{ ORDATA ( "TABST", TABST, 28) },
{ ORDATAVM ( "RP0", RP[0], 48) },
{ ORDATAVM ( "RP1", RP[1], 48) },
{ ORDATAVM ( "RP2", RP[2], 48) },
{ ORDATAVM ( "RP3", RP[3], 48) },
{ ORDATAVM ( "RP4", RP[4], 48) },
{ ORDATAVM ( "RP5", RP[5], 48) },
{ ORDATAVM ( "RP6", RP[6], 48) },
{ ORDATAVM ( "RP7", RP[7], 48) },
{ ORDATA ( "RZ", RZ, 32) },
{ ORDATAVM ( "FP1", pult[0][1], 50) },
{ ORDATAVM ( "FP2", pult[0][2], 50) },
{ ORDATAVM ( "FP3", pult[0][3], 50) },
{ ORDATAVM ( "FP4", pult[0][4], 50) },
{ ORDATAVM ( "FP5", pult[0][5], 50) },
{ ORDATAVM ( "FP6", pult[0][6], 50) },
{ ORDATAVM ( "FP7", pult[0][7], 50) },
{ 0 }
};
UNIT reg_unit = {
UDATA (NULL, 0, 8)
};
DEVICE reg_dev = {
"REG", &reg_unit, reg_reg, NULL,
1, 8, 1, 1, 8, 50,
};
/*
* SCP data structures and interface routines
*
* sim_name simulator name string
* sim_PC pointer to saved PC register descriptor
* sim_emax maximum number of words for examine/deposit
* sim_devices array of pointers to simulated devices
* sim_stop_messages array of pointers to stop messages
* sim_load binary loader
*/
char sim_name[] = "БЭСМ-6";
REG *sim_PC = &cpu_reg[0];
int32 sim_emax = 1; /* max number of addressable units per instruction */
DEVICE *sim_devices[] = {
&cpu_dev,
&reg_dev,
&drum_dev,
&disk_dev,
&mmu_dev,
&clock_dev,
&printer_dev,
&fs_dev,
&pi_dev,
&tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */
0
};
const char *sim_stop_messages[SCPE_BASE] = {
"Неизвестная ошибка", /* Unknown error */
"Останов", /* STOP */
"Точка останова", /* Emulator breakpoint */
"Точка останова по считыванию", /* Emulator read watchpoint */
"Точка останова по записи", /* Emulator write watchpoint */
"Выход за пределы памяти", /* Run out end of memory */
"Запрещенная команда", /* Invalid instruction */
"Контроль команды", /* A data-tagged word fetched */
"Команда в чужом листе", /* Paging error during fetch */
"Число в чужом листе", /* Paging error during load/store */
"Контроль числа МОЗУ", /* RAM parity error */
"Контроль числа БРЗ", /* Write cache parity error */
"Переполнение АУ", /* Arith. overflow */
"Деление на нуль", /* Division by zero or denorm */
"Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */
"Чтение неформатированного барабана", /* Reading unformatted drum */
"Чтение неформатированного диска", /* Reading unformatted disk */
"Останов по КРА", /* Hardware breakpoint */
"Останов по считыванию", /* Load watchpoint */
"Останов по записи", /* Store watchpoint */
"Не реализовано", /* Unimplemented I/O or special reg. access */
};
/*
* Memory examine
*/
t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
return SCPE_NXM;
if (vptr) {
if (addr < 010) {
if ((pult[pult_packet_switch][0] >> addr) & 1) {
/* hardwired */
*vptr = pult[pult_packet_switch][addr];
} else {
/* from switch regs */
*vptr = pult[0][addr];
}
} else
*vptr = memory [addr];
}
return SCPE_OK;
}
/*
* Memory deposit
*/
t_stat cpu_deposit (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
return SCPE_NXM;
if (addr < 010) {
/* Deposited values for the switch register address range
* always go to switch registers.
*/
pult [0][addr] = SET_PARITY (val, PARITY_INSN);
} else
memory [addr] = SET_PARITY (val, PARITY_INSN);
return SCPE_OK;
}
/*
* Reset routine
*/
t_stat cpu_reset (DEVICE *dptr)
{
int i;
ACC = 0;
RMR = 0;
RAU = 0;
RUU = RUU_EXTRACODE | RUU_AVOST_DISABLE;
for (i=0; i<NREGS; ++i)
M[i] = 0;
/* Punchcard readers not yet implemented thus not ready */
READY2 |= 042000000;
/* Регистр 17: БлП, БлЗ, ПОП, ПОК, БлПр */
M[PSW] = PSW_MMAP_DISABLE | PSW_PROT_DISABLE | PSW_INTR_HALT |
PSW_CHECK_HALT | PSW_INTR_DISABLE;
/* Регистр 23: БлП, БлЗ, РежЭ, БлПр */
M[SPSW] = SPSW_MMAP_DISABLE | SPSW_PROT_DISABLE | SPSW_EXTRACODE |
SPSW_INTR_DISABLE;
GRP = MGRP = 0;
// Disabled due to a conflict with loading
// PC = 1; /* "reset cpu; go" should start from 1 */
sim_brk_types = SWMASK ('E') | SWMASK('R') | SWMASK('W');
sim_brk_dflt = SWMASK ('E');
besm6_draw_panel(1);
return SCPE_OK;
}
/*
* Request routine
*/
t_stat cpu_req (UNIT *u, int32 val, CONST char *cptr, void *desc)
{
GRP |= GRP_PANEL_REQ;
return SCPE_OK;
}
/*
* Hardwired program selector validation
*/
t_stat cpu_set_pult (UNIT *u, int32 val, CONST char *cptr, void *desc)
{
int sw;
if (cptr) sw = atoi(cptr); else sw = 0;
if (sw >= 0 && sw <= 10) {
pult_packet_switch = sw;
if (sw)
sim_printf("Pult packet switch set to hardwired program %d\n", sw);
else
sim_printf("Pult packet switch set to switch registers\n");
return SCPE_OK;
}
printf("Illegal value %s\n", cptr);
return SCPE_ARG;
}
t_stat cpu_show_pult (FILE *st, UNIT *up, int32 v, CONST void *dp)
{
fprintf(st, "Pult packet switch position is %d", pult_packet_switch);
return SCPE_OK;
}
/*
* Write Unicode symbol to file.
* Convert to UTF-8 encoding:
* 00000000.0xxxxxxx -> 0xxxxxxx
* 00000xxx.xxyyyyyy -> 110xxxxx, 10yyyyyy
* xxxxyyyy.yyzzzzzz -> 1110xxxx, 10yyyyyy, 10zzzzzz
*/
void
utf8_putc (unsigned ch, FILE *fout)
{
if (ch < 0x80) {
putc (ch, fout);
return;
}
if (ch < 0x800) {
putc (ch >> 6 | 0xc0, fout);
putc ((ch & 0x3f) | 0x80, fout);
return;
}
putc (ch >> 12 | 0xe0, fout);
putc (((ch >> 6) & 0x3f) | 0x80, fout);
putc ((ch & 0x3f) | 0x80, fout);
}
/*
* *call ОКНО - так называлась служебная подпрограмма в мониторной
* системе "Дубна", которая печатала полное состояние всех регистров.
*/
void besm6_okno (const char *message)
{
besm6_log_cont ("_%%%%%% %s: ", message);
if (sim_log)
besm6_fprint_cmd (sim_log, RK);
besm6_log ("_");
/* СчАС, системные индекс-регистры 020-035. */
besm6_log ("_ СчАС:%05o 20:%05o 21:%05o 27:%05o 32:%05o 33:%05o 34:%05o 35:%05o",
PC, M[020], M[021], M[027], M[032], M[033], M[034], M[035]);
/* Индекс-регистры 1-7. */
besm6_log ("_ 1:%05o 2:%05o 3:%05o 4:%05o 5:%05o 6:%05o 7:%05o",
M[1], M[2], M[3], M[4], M[5], M[6], M[7]);
/* Индекс-регистры 010-017. */
besm6_log ("_ 10:%05o 11:%05o 12:%05o 13:%05o 14:%05o 15:%05o 16:%05o 17:%05o",
M[010], M[011], M[012], M[013], M[014], M[015], M[016], M[017]);
/* Сумматор, РМР, режимы АУ и УУ. */
besm6_log ("_ СМ:%04o %04o %04o %04o РМР:%04o %04o %04o %04o РАУ:%02o РУУ:%03o",
(int) (ACC >> 36) & BITS(12), (int) (ACC >> 24) & BITS(12),
(int) (ACC >> 12) & BITS(12), (int) ACC & BITS(12),
(int) (RMR >> 36) & BITS(12), (int) (RMR >> 24) & BITS(12),
(int) (RMR >> 12) & BITS(12), (int) RMR & BITS(12),
RAU, RUU);
}
/*
* Команда "рег"
*/
static void cmd_002 ()
{
#if 0
besm6_debug ("*** рег %03o", Aex & 0377);
#endif
switch (Aex & 0377) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
/* Запись в БРЗ */
mmu_setcache (Aex & 7, ACC);
break;
case 020: case 021: case 022: case 023:
case 024: case 025: case 026: case 027:
/* Запись в регистры приписки */
mmu_setrp (Aex & 7, ACC);
break;
case 030: case 031: case 032: case 033:
/* Запись в регистры защиты */
mmu_setprotection (Aex & 3, ACC);
break;
case 036:
/* Запись в маску главного регистра прерываний */
MGRP = ACC;
break;
case 037:
/* Clearing the main interrupt register: */
/* it is impossible to clear wired (stateless) bits this way */
GRP &= ACC | GRP_WIRED_BITS;
break;
case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71:
case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79:
case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87:
case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95:
/* 0100 - 0137:
* Бит 1: управление блокировкой режима останова БРО.
* Биты 2 и 3 - признаки формирования контрольных
* разрядов (ПКП и ПКЛ). */
if (Aex & 1)
RUU |= RUU_AVOST_DISABLE;
else
RUU &= ~RUU_AVOST_DISABLE;
if (Aex & 2)
RUU |= RUU_PARITY_RIGHT;
else
RUU &= ~RUU_PARITY_RIGHT;
if (Aex & 4)
RUU |= RUU_PARITY_LEFT;
else
RUU &= ~RUU_PARITY_LEFT;
break;
case 0200: case 0201: case 0202: case 0203:
case 0204: case 0205: case 0206: case 0207:
/* Чтение БРЗ */
ACC = mmu_getcache (Aex & 7);
break;
case 0237:
/* Чтение главного регистра прерываний */
ACC = GRP;
break;
default:
if ((Aex & 0340) == 0140) {
/* TODO: watchdog reset mechanism */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
}
/* Неиспользуемые адреса */
besm6_debug ("*** %05o%s: РЕГ %o - неправильный адрес спец.регистра",
PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex);
break;
}
}
/*
* Команда "увв"
*/
static void cmd_033 ()
{
#if 0
besm6_debug ("*** увв %04o, СМ[24:1]=%08o",
Aex & 04177, (uint32) ACC & BITS(24));
#endif
switch (Aex & 04177) {
case 0:
/*
* Releasing the drum printer solenoids. No effect on simulation.
*/
break;
case 1: case 2:
/* Управление обменом с магнитными барабанами */
drum (Aex - 1, (uint32) ACC);
break;
case 3: case 4:
/* Передача управляющего слова для обмена
* с магнитными дисками */
disk_io (Aex - 3, (uint32) ACC);
break;
case 5: case 6: case 7:
/* TODO: управление обменом с магнитными лентами */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 010: case 011:
/* управление устройствами ввода с перфоленты */
fs_control (Aex - 010, (uint32) (ACC & 07));
break;
case 012: case 013:
/* TODO: управление устройствами ввода с перфоленты по запаянной программе */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 014: case 015:
/* Управление АЦПУ */
printer_control (Aex - 014, (uint32) (ACC & 017));
break;
case 023: case 024:
/* Управление обменом с магнитными дисками */
disk_ctl (Aex - 023, (uint32) ACC);
break;
case 030:
/* Гашение ПРП */
/* besm6_debug(">>> гашение ПРП");*/
PRP &= ACC | PRP_WIRED_BITS;
break;
case 031:
/* Имитация сигналов прерывания ГРП */
/*besm6_debug ("*** %05o%s: имитация прерываний ГРП %016llo",
PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", ACC << 24);*/
GRP |= (ACC & BITS(24)) << 24;
break;
case 032: case 033:
/* TODO: имитация сигналов из КМБ в КВУ */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 034:
/* Запись в МПРП */
/* besm6_debug(">>> запись в МПРП");*/
MPRP = ACC & 077777777;
break;
case 035:
/* TODO: управление режимом имитации обмена
* с МБ и МЛ, имитация обмена */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 040: case 041: case 042: case 043:
case 044: case 045: case 046: case 047:
case 050: case 051: case 052: case 053:
case 054: case 055: case 056: case 057:
/* Управление молоточками АЦПУ */
printer_hammer (Aex >= 050, Aex & 7, (uint32) (ACC & BITS(16)));
break;
case 0140:
/* Запись в регистр телеграфных каналов */
tty_send ((uint32) ACC & BITS(24));
break;
case 0141:
/* TODO: formatting magnetic tape */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 0142:
/* TODO: имитация сигналов прерывания ПРП */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 0147:
/* Writing to the power supply control register
* does not have any observable effect
*/
break;
case 0150: case 0151:
/* TODO: reading from punchcards */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 0153:
/* гашение аппаратуры сопряжения с терминалами */
/* besm6_debug(">>> гашение АС: %08o", (uint32) ACC & BITS(24));*/
break;
case 0154: case 0155:
/*
* Punchcard output: the two selected bits control the motor
* and the culling mechanism.
*/
pi_control (Aex & 1, (uint32) ACC & 011);
break;
case 0160: case 0161: case 0162: case 0163:
case 0164: case 0165: case 0166: case 0167:
/* Punchcard output: activating the punching solenoids, 20 at a time. */
pi_write (Aex & 7, (uint32) ACC & BITS(20));
break;
case 0170: case 0171:
/* TODO: пробивка строки на перфоленте */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 0174: case 0175:
/* Выдача кода в пульт оператора */
consul_print (Aex & 1, (uint32) ACC & BITS(8));
break;
case 0177:
/* управление табло ГПВЦ СО АН СССР */
/* besm6_debug(">>> ТАБЛО: %08o", (uint32) ACC & BITS(24));*/
break;
case 04001: case 04002:
/* TODO: считывание слога в режиме имитации обмена */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04003: case 04004:
/* Запрос статуса контроллера магнитных дисков */
ACC = disk_state (Aex - 04003);
break;
case 04006:
/* TODO: считывание строки с устройства ввода
* с перфоленты в запаянной программе */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04007:
/* TODO: опрос синхроимпульса ненулевой строки
* в запаянной программе ввода с перфоленты */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04014: case 04015:
/* считывание строки с устройства ввода с перфоленты */
ACC = fs_read (Aex - 04014);
break;
case 04016: case 04017:
/* TODO: считывание строки с устройства
* ввода с перфоленты */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04020: case 04021: case 04022: case 04023:
/* TODO: считывание слога в режиме имитации
* внешнего обмена */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04030:
/* Чтение старшей половины ПРП */
ACC = PRP & 077770000;
break;
case 04031:
/* Опрос сигналов готовности (АЦПУ и пр.) */
/* besm6_debug("Reading READY");*/
ACC = READY;
break;
case 04034:
/* Чтение младшей половины ПРП */
ACC = (PRP & 07777) | 0377;
break;
case 04035:
/* Опрос триггера ОШМi - наличие ошибок при внешнем обмене. */
ACC = drum_errors() | disk_errors();
break;
case 04100:
/* Опрос телеграфных каналов связи */
ACC = tty_query ();
break;
case 04102:
/* Опрос сигналов готовности перфокарт и перфолент */
/* besm6_debug("Reading punchcard/punchtape READY @%05o", PC);*/
ACC = READY2;
break;
case 04103: case 04104: case 04105: case 04106:
/* Опрос состояния лентопротяжных механизмов.
* Все устройства не готовы. */
ACC = BITS(24);
break;
case 04107:
/* TODO: опрос схемы контроля записи на МЛ */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04115:
/* Неизвестное обращение. ДИСПАК выдаёт эту команду
* группами по 8 штук каждые несколько секунд. */
ACC = 0;
break;
case 04160: case 04161: case 04162: case 04163:
case 04164: case 04165: case 04166: case 04167:
/* Punchcard output: reading a punched line for checking, 20 bit at a time */
ACC = pi_read (Aex & 7);
break;
case 04170: case 04171: case 04172: case 04173:
/* TODO: считывание контрольного кода
* строки перфоленты */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
break;
case 04174: case 04175:
/* Считывание кода с пульта оператора */
ACC = consul_read (Aex & 1);
break;
case 04177:
/* чтение табло ГПВЦ СО АН СССР */
ACC = 0;
break;
default: {
unsigned val = Aex & 04177;
if (0100 <= val && val <= 0137) {
/* Управление лентопротяжными механизмами
* и гашение разрядов регистров признаков
* окончания подвода зоны. Игнорируем. */
} else if (04140 <= val && val <= 04157) {
/* TODO: считывание строки перфокарты */
longjmp (cpu_halt, STOP_UNIMPLEMENTED);
} else {
/* Неиспользуемые адреса */
/* if (sim_deb && cpu_dev.dctrl)*/
besm6_debug ("*** %05o%s: УВВ %o - неправильный адрес ввода-вывода",
PC, (RUU & RUU_RIGHT_INSTR) ? "п" : "л", Aex);
ACC = 0;
}
} break;
}
}
void check_initial_setup ()
{
const int MGRP_COPY = 01455; /* OS version specific? */
const int TAKEN = 0442; /* fixed? */
const int YEAR = 0221; /* fixed */
/* 47 р. яч. ЗАНЯТА - разр. приказы вообще */
const t_value SETUP_REQS_ENABLED = 1LL << 46;
/* 7 р. яч. ЗАНЯТА - разр любые приказы */
const t_value ALL_REQS_ENABLED = 1 << 6;
if (!vt_is_idle()) {
/* Avoid sending setup requests while the OS
* is still printing boot-up messages.
*/
return;
}
if ((memory[TAKEN] & SETUP_REQS_ENABLED) == 0 || /* not ready for setup */
(memory[TAKEN] & ALL_REQS_ENABLED) != 0 || /* all done */
(MGRP & GRP_PANEL_REQ) == 0) { /* not at the moment */
return;
}
/* Выдаем приказы оператора СМЕ и ВРЕ,
* а дату корректируем непосредственно в памяти.
*/
/* Номер смены в 22-24 рр. МГРП: если еще не установлен, установить */
if (((memory[MGRP_COPY] >> 21) & 3) == 0) {
/* приказ СМЕ: ТР6 = 010, ТР4 = 1, 22-24 р ТР5 - #смены */
pult[0][6] = 010;
pult[0][4] = 1;
pult[0][5] = 1 << 21;
GRP |= GRP_PANEL_REQ;
} else {
struct tm * d;
/* Яч. ГОД обновляем самостоятельно */
time_t t;
t_value date;
sim_get_time(&t);
d = localtime(&t);
++d->tm_mon;
date = (t_value) (d->tm_mday / 10) << 33 |
(t_value) (d->tm_mday % 10) << 29 |
(d->tm_mon / 10) << 28 |
(d->tm_mon % 10) << 24 |
(d->tm_year % 10) << 20 |
((d->tm_year / 10) % 10) << 16 |
(memory[YEAR] & 7);
memory[YEAR] = SET_PARITY (date, PARITY_NUMBER);
/* приказ ВРЕ: ТР6 = 016, ТР5 = 9-14 р.-часы, 1-8 р.-минуты */
pult[0][6] = 016;
pult[0][4] = 0;
pult[0][5] = (d->tm_hour / 10) << 12 |
(d->tm_hour % 10) << 8 |
(d->tm_min / 10) << 4 |
(d->tm_min % 10);
GRP |= GRP_PANEL_REQ;
}
}
/*
* Execute one instruction, placed on address PC:RUU_RIGHT_INSTR.
* When stopped, perform a longjmp to cpu_halt,
* sending a stop code.
*/
void cpu_one_inst ()
{
int reg, opcode, addr, nextpc, next_mod;
t_value word;
/*
* Instruction execution time in 100 ns ticks; not really used
* as the amortized 1 MIPS instruction rate is assumed.
* The assignments of MEAN_TIME(x,y) to the delay variable
* are kept as a reference.
*/
uint32 delay;
corr_stack = 0;
word = mmu_fetch (PC);
if (RUU & RUU_RIGHT_INSTR)
RK = (uint32)word; /* get right instruction */
else
RK = (uint32)(word >> 24);/* get left instruction */
RK &= BITS(24);
reg = RK >> 20;
if (RK & BBIT(20)) {
addr = RK & BITS(15);
opcode = (RK >> 12) & 0370;
} else {
addr = RK & BITS(12);
if (RK & BBIT(19))
addr |= 070000;
opcode = (RK >> 12) & 077;
}
if (sim_deb && cpu_dev.dctrl) {
fprintf (sim_deb, "*** %05o%s: ", PC,
(RUU & RUU_RIGHT_INSTR) ? "п" : "л");
besm6_fprint_cmd (sim_deb, RK);
fprintf (sim_deb, "\tСМ=");
fprint_sym (sim_deb, 0, &ACC, 0, 0);
fprintf (sim_deb, "\tРАУ=%02o", RAU);
if (reg)
fprintf (sim_deb, "\tМ[%o]=%05o", reg, M[reg]);
fprintf (sim_deb, "\n");
}
nextpc = ADDR(PC + 1);
if (RUU & RUU_RIGHT_INSTR) {
PC += 1; /* increment PC */
RUU &= ~RUU_RIGHT_INSTR;
} else {
mmu_prefetch(nextpc | (IS_SUPERVISOR(RUU) ? BBIT(16) : 0), 0);
RUU |= RUU_RIGHT_INSTR;
}
if (RUU & RUU_MOD_RK) {
addr = ADDR (addr + M[MOD]);
}
next_mod = 0;
delay = 0;
switch (opcode) {
case 000: /* зп, atx */
Aex = ADDR (addr + M[reg]);
mmu_store (Aex, ACC);
if (! addr && reg == 017)
M[017] = ADDR (M[017] + 1);
delay = MEAN_TIME (3, 3);
break;
case 001: /* зпм, stx */
Aex = ADDR (addr + M[reg]);
mmu_store (Aex, ACC);
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
ACC = mmu_load (M[017]);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (6, 6);
break;
case 002: /* рег, mod */
Aex = ADDR (addr + M[reg]);
if (! IS_SUPERVISOR (RUU))
longjmp (cpu_halt, STOP_BADCMD);
cmd_002 ();
/* Режим АУ - логический, если операция была "чтение" */
if (Aex & 0200)
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 3);
break;
case 003: /* счм, xts */
mmu_store (M[017], ACC);
M[017] = ADDR (M[017] + 1);
corr_stack = -1;
Aex = ADDR (addr + M[reg]);
ACC = mmu_load (Aex);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (6, 6);
break;
case 004: /* сл, a+x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add (mmu_load (Aex), 0, 0);
RAU = SET_ADDITIVE (RAU);
delay = MEAN_TIME (3, 11);
break;
case 005: /* вч, a-x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add (mmu_load (Aex), 0, 1);
RAU = SET_ADDITIVE (RAU);
delay = MEAN_TIME (3, 11);
break;
case 006: /* вчоб, x-a */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add (mmu_load (Aex), 1, 0);
RAU = SET_ADDITIVE (RAU);
delay = MEAN_TIME (3, 11);
break;
case 007: /* вчаб, amx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add (mmu_load (Aex), 1, 1);
RAU = SET_ADDITIVE (RAU);
delay = MEAN_TIME (3, 11);
break;
case 010: /* сч, xta */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC = mmu_load (Aex);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 3);
break;
case 011: /* и, aax */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC &= mmu_load (Aex);
RMR = 0;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 4);
break;
case 012: /* нтж, aex */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
RMR = ACC;
ACC ^= mmu_load (Aex);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 3);
break;
case 013: /* слц, arx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC += mmu_load (Aex);
if (ACC & BIT49)
ACC = (ACC + 1) & BITS48;
RMR = 0;
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 6);
break;
case 014: /* знак, avx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_change_sign (mmu_load (Aex) >> 40 & 1);
RAU = SET_ADDITIVE (RAU);
delay = MEAN_TIME (3, 5);
break;
case 015: /* или, aox */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC |= mmu_load (Aex);
RMR = 0;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 4);
break;
case 016: /* дел, a/x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_divide (mmu_load (Aex));
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 50);
break;
case 017: /* умн, a*x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_multiply (mmu_load (Aex));
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 18);
break;
case 020: /* сбр, apx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC = besm6_pack (ACC, mmu_load (Aex));
RMR = 0;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 53);
break;
case 021: /* рзб, aux */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC = besm6_unpack (ACC, mmu_load (Aex));
RMR = 0;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 53);
break;
case 022: /* чед, acx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
ACC = besm6_count_ones (ACC) + mmu_load (Aex);
if (ACC & BIT49)
ACC = (ACC + 1) & BITS48;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 56);
break;
case 023: /* нед, anx */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
if (ACC) {
int n = besm6_highest_bit (ACC);
/* "Остаток" сумматора, исключая бит,
* номер которого определен, помещается в РМР,
* начиная со старшего бита РМР. */
besm6_shift (48 - n);
/* Циклическое сложение номера со словом по Аисп. */
ACC = n + mmu_load (Aex);
if (ACC & BIT49)
ACC = (ACC + 1) & BITS48;
} else {
RMR = 0;
ACC = mmu_load (Aex);
}
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 32);
break;
case 024: /* слп, e+x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add_exponent ((mmu_load (Aex) >> 41) - 64);
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 5);
break;
case 025: /* вчп, e-x */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
besm6_add_exponent (64 - (mmu_load (Aex) >> 41));
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 5);
break;
case 026: { /* сд, asx */
int n;
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
n = (mmu_load (Aex) >> 41) - 64;
besm6_shift (n);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 4 + abs (n));
break;
}
case 027: /* рж, xtr */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
RAU = (mmu_load (Aex) >> 41) & 077;
delay = MEAN_TIME (3, 3);
break;
case 030: /* счрж, rte */
Aex = ADDR (addr + M[reg]);
ACC = (t_value) (RAU & Aex & 0177) << 41;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 3);
break;
case 031: /* счмр, yta */
Aex = ADDR (addr + M[reg]);
if (IS_LOGICAL (RAU)) {
ACC = RMR;
} else {
t_value x = RMR;
ACC = (ACC & ~BITS41) | (RMR & BITS40);
besm6_add_exponent ((Aex & 0177) - 64);
RMR = x;
}
delay = MEAN_TIME (3, 5);
break;
case 032: /* э32, ext */
/* Fall through... */
case 033: /* увв, ext */
Aex = ADDR (addr + M[reg]);
if (! IS_SUPERVISOR (RUU))
longjmp (cpu_halt, STOP_BADCMD);
cmd_033 ();
/* Режим АУ - логический, если операция была "чтение" */
if (Aex & 04000)
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 8);
break;
case 034: /* слпа, e+n */
Aex = ADDR (addr + M[reg]);
besm6_add_exponent ((Aex & 0177) - 64);
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 5);
break;
case 035: /* вчпа, e-n */
Aex = ADDR (addr + M[reg]);
besm6_add_exponent (64 - (Aex & 0177));
RAU = SET_MULTIPLICATIVE (RAU);
delay = MEAN_TIME (3, 5);
break;
case 036: { /* сда, asn */
int n;
Aex = ADDR (addr + M[reg]);
n = (Aex & 0177) - 64;
besm6_shift (n);
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (3, 4 + abs (n));
break;
}
case 037: /* ржа, ntr */
Aex = ADDR (addr + M[reg]);
RAU = Aex & 077;
delay = MEAN_TIME (3, 3);
break;
case 040: /* уи, ati */
Aex = ADDR (addr + M[reg]);
if (IS_SUPERVISOR (RUU)) {
int reg = Aex & 037;
M[reg] = ADDR (ACC);
/* breakpoint/watchpoint regs will match physical
* or virtual addresses depending on the current
* mapping mode.
*/
if ((M[PSW] & PSW_MMAP_DISABLE) &&
(reg == IBP || reg == DWP))
M[reg] |= BBIT(16);
} else
M[Aex & 017] = ADDR (ACC);
M[0] = 0;
delay = MEAN_TIME (14, 3);
break;
case 041: { /* уим, sti */
unsigned rg, ad;
Aex = ADDR (addr + M[reg]);
rg = Aex & (IS_SUPERVISOR (RUU) ? 037 : 017);
ad = ADDR (ACC);
if (rg != 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
ACC = mmu_load (rg != 017 ? M[017] : ad);
M[rg] = ad;
if ((M[PSW] & PSW_MMAP_DISABLE) && (rg == IBP || rg == DWP))
M[rg] |= BBIT(16);
M[0] = 0;
RAU = SET_LOGICAL (RAU);
delay = MEAN_TIME (14, 3);
break;
}
case 042: /* счи, ita */
delay = MEAN_TIME (6, 3);
load_modifier: Aex = ADDR (addr + M[reg]);
ACC = ADDR(M[Aex & (IS_SUPERVISOR (RUU) ? 037 : 017)]);
RAU = SET_LOGICAL (RAU);
break;
case 043: /* счим, its */
mmu_store (M[017], ACC);
M[017] = ADDR (M[017] + 1);
delay = MEAN_TIME (9, 6);
goto load_modifier;
case 044: /* уии, mtj */
Aex = addr;
if (IS_SUPERVISOR (RUU)) {
transfer_modifier: M[Aex & 037] = M[reg];
if ((M[PSW] & PSW_MMAP_DISABLE) &&
((Aex & 037) == IBP || (Aex & 037) == DWP))
M[Aex & 037] |= BBIT(16);
} else
M[Aex & 017] = M[reg];
M[0] = 0;
delay = 6;
break;
case 045: /* сли, j+m */
Aex = addr;
if ((Aex & 020) && IS_SUPERVISOR (RUU))
goto transfer_modifier;
M[Aex & 017] = ADDR (M[Aex & 017] + M[reg]);
M[0] = 0;
delay = 6;
break;
case 046: /* э46, x46 */
Aex = addr;
if (! IS_SUPERVISOR (RUU))
longjmp (cpu_halt, STOP_BADCMD);
M[Aex & 017] = ADDR (Aex);
M[0] = 0;
delay = 6;
break;
case 047: /* э47, x47 */
Aex = addr;
if (! IS_SUPERVISOR (RUU))
longjmp (cpu_halt, STOP_BADCMD);
M[Aex & 017] = ADDR (M[Aex & 017] + Aex);
M[0] = 0;
delay = 6;
break;
case 050: case 051: case 052: case 053:
case 054: case 055: case 056: case 057:
case 060: case 061: case 062: case 063:
case 064: case 065: case 066: case 067:
case 070: case 071: case 072: case 073:
case 074: case 075: case 076: case 077: /* э50...э77 */
case 0200: /* э20 */
case 0210: /* э21 */
stop_as_extracode:
Aex = ADDR (addr + M[reg]);
if (! sim_deb && sim_log && cpu_dev.dctrl && opcode != 075) {
/* Если включен console log и cpu debug,
* но нет console debug, то печатаем только экстракоды.
* Пропускаем э75, их обычно слишком много. */
t_value word = mmu_load (Aex);
fprintf (sim_log, "*** %05o%s: ", PC,
(RUU & RUU_RIGHT_INSTR) ? "п" : "л");
besm6_fprint_cmd (sim_log, RK);
fprintf (sim_log, "\tАисп=%05o (=", Aex);
fprint_sym (sim_log, 0, &word, 0, 0);
fprintf (sim_log, ") СМ=");
fprint_sym (sim_log, 0, &ACC, 0, 0);
if (reg)
fprintf (sim_log, " М[%o]=%05o", reg, M[reg]);
fprintf (sim_log, "\n");
}
/*besm6_okno ("экстракод");*/
/* Адрес возврата из экстракода. */
M[ERET] = nextpc;
/* Сохранённые режимы УУ. */
M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE |
PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU);
/* Текущие режимы УУ. */
M[PSW] = PSW_INTR_DISABLE | PSW_MMAP_DISABLE |
PSW_PROT_DISABLE | /*?*/ PSW_INTR_HALT;
M[14] = Aex;
RUU = SET_SUPERVISOR (RUU, SPSW_EXTRACODE);
if (opcode <= 077)
PC = 0500 + opcode; /* э50-э77 */
else
PC = 0540 + (opcode >> 3); /* э20, э21 */
RUU &= ~RUU_RIGHT_INSTR;
delay = 7;
break;
case 0220: /* мода, utc */
Aex = ADDR (addr + M[reg]);
next_mod = Aex;
delay = 4;
break;
case 0230: /* мод, wtc */
if (! addr && reg == 017) {
M[017] = ADDR (M[017] - 1);
corr_stack = 1;
}
Aex = ADDR (addr + M[reg]);
next_mod = ADDR (mmu_load (Aex));
delay = MEAN_TIME (13, 3);
break;
case 0240: /* уиа, vtm */
Aex = addr;
M[reg] = addr;
M[0] = 0;
if (IS_SUPERVISOR (RUU) && reg == 0) {
M[PSW] &= ~(PSW_INTR_DISABLE |
PSW_MMAP_DISABLE | PSW_PROT_DISABLE);
M[PSW] |= addr & (PSW_INTR_DISABLE |
PSW_MMAP_DISABLE | PSW_PROT_DISABLE);
}
delay = 4;
break;
case 0250: /* слиа, utm */
Aex = ADDR (addr + M[reg]);
M[reg] = Aex;
M[0] = 0;
if (IS_SUPERVISOR (RUU) && reg == 0) {
M[PSW] &= ~(PSW_INTR_DISABLE |
PSW_MMAP_DISABLE | PSW_PROT_DISABLE);
M[PSW] |= addr & (PSW_INTR_DISABLE |
PSW_MMAP_DISABLE | PSW_PROT_DISABLE);
}
delay = 4;
break;
case 0260: /* по, uza */
Aex = ADDR (addr + M[reg]);
RMR = ACC;
delay = MEAN_TIME (12, 3);
if (IS_ADDITIVE (RAU)) {
if (ACC & BIT41)
break;
} else if (IS_MULTIPLICATIVE (RAU)) {
if (! (ACC & BIT48))
break;
} else if (IS_LOGICAL (RAU)) {
if (ACC)
break;
} else
break;
PC = Aex;
RUU &= ~RUU_RIGHT_INSTR;
delay += 3;
break;
case 0270: /* пе, u1a */
Aex = ADDR (addr + M[reg]);
RMR = ACC;
delay = MEAN_TIME (12, 3);
if (IS_ADDITIVE (RAU)) {
if (! (ACC & BIT41))
break;
} else if (IS_MULTIPLICATIVE (RAU)) {
if (ACC & BIT48)
break;
} else if (IS_LOGICAL (RAU)) {
if (! ACC)
break;
} else
/* fall thru, i.e. branch */;
PC = Aex;
RUU &= ~RUU_RIGHT_INSTR;
delay += 3;
break;
case 0300: /* пб, uj */
Aex = ADDR (addr + M[reg]);
PC = Aex;
RUU &= ~RUU_RIGHT_INSTR;
delay = 7;
break;
case 0310: /* пв, vjm */
Aex = addr;
M[reg] = nextpc;
M[0] = 0;
PC = addr;
RUU &= ~RUU_RIGHT_INSTR;
delay = 7;
break;
case 0320: /* выпр, iret */
Aex = addr;
if (! IS_SUPERVISOR (RUU)) {
longjmp (cpu_halt, STOP_BADCMD);
}
M[PSW] = (M[PSW] & PSW_WRITE_WATCH) |
(M[SPSW] & (SPSW_INTR_DISABLE |
SPSW_MMAP_DISABLE | SPSW_PROT_DISABLE));
PC = M[(reg & 3) | 030];
RUU &= ~RUU_RIGHT_INSTR;
if (M[SPSW] & SPSW_RIGHT_INSTR)
RUU |= RUU_RIGHT_INSTR;
else
RUU &= ~RUU_RIGHT_INSTR;
RUU = SET_SUPERVISOR (RUU,
M[SPSW] & (SPSW_EXTRACODE | SPSW_INTERRUPT));
if (M[SPSW] & SPSW_MOD_RK)
next_mod = M[MOD];
/*besm6_okno ("Выход из прерывания");*/
delay = 7;
break;
case 0330: /* стоп, stop */
Aex = ADDR (addr + M[reg]);
delay = 7;
if (! IS_SUPERVISOR(RUU)) {
if (M[PSW] & PSW_CHECK_HALT)
break;
else {
opcode = 063;
goto stop_as_extracode;
}
}
mmu_print_brz ();
longjmp (cpu_halt, STOP_STOP);
break;
case 0340: /* пио, vzm */
branch_zero: Aex = addr;
delay = 4;
if (! M[reg]) {
PC = addr;
RUU &= ~RUU_RIGHT_INSTR;
delay += 3;
}
break;
case 0350: /* пино, v1m */
Aex = addr;
delay = 4;
if (M[reg]) {
PC = addr;
RUU &= ~RUU_RIGHT_INSTR;
delay += 3;
}
break;
case 0360: /* э36, *36 */
goto branch_zero;
case 0370: /* цикл, vlm */
Aex = addr;
delay = 4;
if (! M[reg])
break;
M[reg] = ADDR (M[reg] + 1);
PC = addr;
RUU &= ~RUU_RIGHT_INSTR;
delay += 3;
break;
default:
/* Unknown instruction - cannot happen. */
longjmp (cpu_halt, STOP_STOP);
break;
}
if (next_mod) {
/* Модификация адреса следующей команды. */
M[MOD] = next_mod;
RUU |= RUU_MOD_RK;
} else
RUU &= ~RUU_MOD_RK;
/* Не находимся ли мы в цикле "ЖДУ" диспака? */
if (RUU == 047 && PC == 04440 && RK == 067704440) {
check_initial_setup ();
sim_idle(0, TRUE);
}
}
/*
* Операция прерывания 1: внутреннее прерывание.
* Описана в 9-м томе технического описания БЭСМ-6, страница 119.
*/
void op_int_1 (const char *msg)
{
/*besm6_okno (msg);*/
M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE |
PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU);
if (RUU & RUU_RIGHT_INSTR)
M[SPSW] |= SPSW_RIGHT_INSTR;
M[IRET] = PC;
M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE;
if (RUU & RUU_MOD_RK) {
M[SPSW] |= SPSW_MOD_RK;
RUU &= ~RUU_MOD_RK;
}
PC = 0500;
RUU &= ~RUU_RIGHT_INSTR;
RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT);
}
/*
* Операция прерывания 2: внешнее прерывание.
* Описана в 9-м томе технического описания БЭСМ-6, страница 129.
*/
void op_int_2 ()
{
/*besm6_okno ("Внешнее прерывание");*/
M[SPSW] = (M[PSW] & (PSW_INTR_DISABLE | PSW_MMAP_DISABLE |
PSW_PROT_DISABLE)) | IS_SUPERVISOR (RUU);
M[IRET] = PC;
M[PSW] |= PSW_INTR_DISABLE | PSW_MMAP_DISABLE | PSW_PROT_DISABLE;
if (RUU & RUU_MOD_RK) {
M[SPSW] |= SPSW_MOD_RK;
RUU &= ~RUU_MOD_RK;
}
PC = 0501;
RUU &= ~RUU_RIGHT_INSTR;
RUU = SET_SUPERVISOR (RUU, SPSW_INTERRUPT);
}
/*
* Main instruction fetch/decode loop
*/
t_stat sim_instr (void)
{
t_stat r;
int iintr = 0;
/* Restore register state */
PC = PC & BITS(15); /* mask PC */
mmu_setup (); /* copy RP to TLB */
/* An internal interrupt or user intervention */
r = setjmp (cpu_halt);
if (r) {
M[017] += corr_stack;
if (cpu_dev.dctrl) {
const char *message = (r >= SCPE_BASE) ?
sim_error_text (r) :
sim_stop_messages [r];
besm6_debug ("/// %05o%s: %s", PC,
(RUU & RUU_RIGHT_INSTR) ? "п" : "л",
message);
}
/*
* ПоП и ПоК вызывают останов при любом внутреннем прерывании
* или прерывании по контролю, соответственно.
* Если произошёл останов по ПоП или ПоК,
* то продолжение выполнения начнётся с команды, следующей
* за вызвавшей прерывание. Как если бы кнопка "ТП" (тип
* перехода) была включена. Подробнее на странице 119 ТО9.
*/
switch (r) {
default:
ret: besm6_draw_panel(1);
return r;
case STOP_RWATCH:
case STOP_WWATCH:
/* Step back one insn to reexecute it */
if (! (RUU & RUU_RIGHT_INSTR)) {
--PC;
}
RUU ^= RUU_RIGHT_INSTR;
goto ret;
case STOP_BADCMD:
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
op_int_1 (sim_stop_messages[r]);
// SPSW_NEXT_RK is not important for this interrupt
GRP |= GRP_ILL_INSN;
break;
case STOP_INSN_CHECK:
if (M[PSW] & PSW_CHECK_HALT) /* ПоК */
goto ret;
op_int_1 (sim_stop_messages[r]);
// SPSW_NEXT_RK must be 0 for this interrupt; it is already
GRP |= GRP_INSN_CHECK;
break;
case STOP_INSN_PROT:
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
if (RUU & RUU_RIGHT_INSTR) {
++PC;
}
RUU ^= RUU_RIGHT_INSTR;
op_int_1 (sim_stop_messages[r]);
// SPSW_NEXT_RK must be 1 for this interrupt
M[SPSW] |= SPSW_NEXT_RK;
GRP |= GRP_INSN_PROT;
break;
case STOP_OPERAND_PROT:
#if 0
/* ДИСПАК держит признак ПоП установленным.
* При запуске СЕРП возникает обращение к чужому листу. */
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
#endif
if (RUU & RUU_RIGHT_INSTR) {
++PC;
}
RUU ^= RUU_RIGHT_INSTR;
op_int_1 (sim_stop_messages[r]);
M[SPSW] |= SPSW_NEXT_RK;
// The offending virtual page is in bits 5-9
GRP |= GRP_OPRND_PROT;
GRP = GRP_SET_PAGE (GRP, iintr_data);
break;
case STOP_RAM_CHECK:
if (M[PSW] & PSW_CHECK_HALT) /* ПоК */
goto ret;
op_int_1 (sim_stop_messages[r]);
// The offending interleaved block # is in bits 1-3.
GRP |= GRP_CHECK | GRP_RAM_CHECK;
GRP = GRP_SET_BLOCK (GRP, iintr_data);
break;
case STOP_CACHE_CHECK:
if (M[PSW] & PSW_CHECK_HALT) /* ПоК */
goto ret;
op_int_1 (sim_stop_messages[r]);
// The offending BRZ # is in bits 1-3.
GRP |= GRP_CHECK;
GRP &= ~GRP_RAM_CHECK;
GRP = GRP_SET_BLOCK (GRP, iintr_data);
break;
case STOP_INSN_ADDR_MATCH:
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
if (RUU & RUU_RIGHT_INSTR) {
++PC;
}
RUU ^= RUU_RIGHT_INSTR;
op_int_1 (sim_stop_messages[r]);
M[SPSW] |= SPSW_NEXT_RK;
GRP |= GRP_BREAKPOINT;
break;
case STOP_LOAD_ADDR_MATCH:
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
if (RUU & RUU_RIGHT_INSTR) {
++PC;
}
RUU ^= RUU_RIGHT_INSTR;
op_int_1 (sim_stop_messages[r]);
M[SPSW] |= SPSW_NEXT_RK;
GRP |= GRP_WATCHPT_R;
break;
case STOP_STORE_ADDR_MATCH:
if (M[PSW] & PSW_INTR_HALT) /* ПоП */
goto ret;
if (RUU & RUU_RIGHT_INSTR) {
++PC;
}
RUU ^= RUU_RIGHT_INSTR;
op_int_1 (sim_stop_messages[r]);
M[SPSW] |= SPSW_NEXT_RK;
GRP |= GRP_WATCHPT_W;
break;
case STOP_OVFL:
/* Прерывание по АУ вызывает останов, если БРО=0
* и установлен ПоП или ПоК.
* Страница 118 ТО9.*/
if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */
((M[PSW] & PSW_INTR_HALT) || /* ПоП */
(M[PSW] & PSW_CHECK_HALT))) /* ПоК */
goto ret;
op_int_1 (sim_stop_messages[r]);
GRP |= GRP_OVERFLOW|GRP_RAM_CHECK;
break;
case STOP_DIVZERO:
if (! (RUU & RUU_AVOST_DISABLE) && /* ! БРО */
((M[PSW] & PSW_INTR_HALT) || /* ПоП */
(M[PSW] & PSW_CHECK_HALT))) /* ПоК */
goto ret;
op_int_1 (sim_stop_messages[r]);
GRP |= GRP_DIVZERO|GRP_RAM_CHECK;
break;
}
++iintr;
}
if (iintr > 1) {
besm6_draw_panel(1);
return STOP_DOUBLE_INTR;
}
/* Main instruction fetch/decode loop */
for (;;) {
if (sim_interval <= 0) { /* check clock queue */
r = sim_process_event ();
if (r) {
besm6_draw_panel(1);
return r;
}
}
if (PC > BITS(15) && IS_SUPERVISOR(RUU)) {
/*
* Runaway instruction execution in supervisor mode
* warrants attention.
*/
besm6_draw_panel(1);
return STOP_RUNOUT; /* stop simulation */
}
if (sim_brk_summ & SWMASK('E') && /* breakpoint? */
sim_brk_test (PC, SWMASK ('E'))) {
besm6_draw_panel(1);
return STOP_IBKPT; /* stop simulation */
}
if (redraw_panel) {
/* Periodic panel redraw is not forcing */
besm6_draw_panel(0);
redraw_panel = 0;
}
if (PRP & MPRP) {
/* There are interrupts pending in the peripheral
* interrupt register */
GRP |= GRP_SLAVE;
}
if (! iintr && ! (RUU & RUU_RIGHT_INSTR) &&
! (M[PSW] & PSW_INTR_DISABLE) && (GRP & MGRP)) {
/* external interrupt */
op_int_2();
}
cpu_one_inst (); /* one instr */
iintr = 0;
sim_interval -= 1; /* count down instructions */
}
}
/*
* 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)
{
static unsigned counter;
static unsigned tty_counter;
++counter;
++tty_counter;
GRP |= GRP_TIMER;
if ((counter & 3) == 0) {
/*
* The OS used the (undocumented, later addition)
* slow clock interrupt to initiate servicing
* terminal I/O. Its frequency was reportedly about 50-60 Hz;
* 16 ms is a good enough approximation.
*/
GRP |= GRP_SLOW_CLK;
}
/* Requesting a panel sample every 32 ms
* (a redraw actually happens at every other sample). */
if ((counter & 7) == 0) {
redraw_panel = 1;
}
/* Baudot TTYs are synchronised to the main timer rather than the
* serial line clock. Their baud rate is 50.
*/
if (tty_counter == CLK_TPS/50) {
tt_print();
tty_counter = 0;
}
tmr_poll = sim_rtcn_calb (CLK_TPS, 0); /* calibrate clock */
return sim_activate_after (this, 1000000/CLK_TPS); /* reactivate unit */
}
UNIT clocks[] = {
{ UDATA(fast_clk, UNIT_IDLE, 0), CLK_DELAY }, /* Bit 40 of the GRP, 250 Hz */
};
t_stat clk_reset (DEVICE * dev)
{
sim_register_clock_unit (&clocks[0]);
/* Схема автозапуска включается по нереализованной кнопке "МР" */
if (!sim_is_running) { /* RESET (not IORESET)? */
tmr_poll = sim_rtcn_init (clocks[0].wait, 0); /* init timer */
sim_activate (&clocks[0], tmr_poll); /* activate unit */
}
return SCPE_OK;
}
DEVICE clock_dev = {
"CLK", clocks, NULL, NULL,
1, 0, 0, 0, 0, 0,
NULL, NULL, &clk_reset,
NULL, NULL, NULL, NULL,
DEV_DEBUG
};