/* | |
* 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> | |
#include <time.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 */ | |
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 (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 ) | |
/* 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; | |
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, char *cptr, void *desc); | |
t_stat cpu_set_pult (UNIT *u, int32 val, char *cptr, void *desc); | |
t_stat cpu_show_pult (FILE *st, UNIT *up, int32 v, 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) }; | |
REG cpu_reg[] = { | |
{ "СчАС", &PC, 8, 15, 0, 1 }, /* счётчик адреса команды */ | |
{ "РК", &RK, 8, 24, 0, 1 }, /* регистр выполняемой команды */ | |
{ "Аисп", &Aex, 8, 15, 0, 1 }, /* исполнительный адрес */ | |
{ "СМ", &ACC, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* сумматор */ | |
{ "РМР", &RMR, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* регистр младших разрядов */ | |
{ "РАУ", &RAU, 2, 6, 0, 1 }, /* режимы АУ */ | |
{ "М1", &M[1], 8, 15, 0, 1 }, /* регистры-модификаторы */ | |
{ "М2", &M[2], 8, 15, 0, 1 }, | |
{ "М3", &M[3], 8, 15, 0, 1 }, | |
{ "М4", &M[4], 8, 15, 0, 1 }, | |
{ "М5", &M[5], 8, 15, 0, 1 }, | |
{ "М6", &M[6], 8, 15, 0, 1 }, | |
{ "М7", &M[7], 8, 15, 0, 1 }, | |
{ "М10", &M[010], 8, 15, 0, 1 }, | |
{ "М11", &M[011], 8, 15, 0, 1 }, | |
{ "М12", &M[012], 8, 15, 0, 1 }, | |
{ "М13", &M[013], 8, 15, 0, 1 }, | |
{ "М14", &M[014], 8, 15, 0, 1 }, | |
{ "М15", &M[015], 8, 15, 0, 1 }, | |
{ "М16", &M[016], 8, 15, 0, 1 }, | |
{ "М17", &M[017], 8, 15, 0, 1 }, /* указатель магазина */ | |
{ "М20", &M[020], 8, 15, 0, 1 }, /* MOD - модификатор адреса */ | |
{ "М21", &M[021], 8, 15, 0, 1 }, /* PSW - режимы УУ */ | |
{ "М27", &M[027], 8, 15, 0, 1 }, /* SPSW - упрятывание режимов УУ */ | |
{ "М32", &M[032], 8, 15, 0, 1 }, /* ERET - адрес возврата из экстракода */ | |
{ "М33", &M[033], 8, 15, 0, 1 }, /* IRET - адрес возврата из прерывания */ | |
{ "М34", &M[034], 8, 16, 0, 1 }, /* IBP - адрес прерывания по выполнению */ | |
{ "М35", &M[035], 8, 16, 0, 1 }, /* DWP - адрес прерывания по чтению/записи */ | |
{ "РУУ", &RUU, 2, 9, 0, 1 }, /* ПКП, ПКЛ, РежЭ, РежПр, ПрИК, БРО, ПрК */ | |
{ "ГРП", &GRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* главный регистр прерываний */ | |
{ "МГРП", &MGRP, 8, 48, 0, 1, NULL, NULL, REG_VMIO}, /* маска ГРП */ | |
{ "ПРП", &PRP, 8, 24, 0, 1 }, /* периферийный регистр прерываний */ | |
{ "МПРП", &MPRP, 8, 24, 0, 1 }, /* маска ПРП */ | |
{ 0 } | |
}; | |
MTAB cpu_mod[] = { | |
{ 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[] = { | |
{ "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 }, | |
{ "M5", &M[5], 8, 15, 0, 1 }, | |
{ "M6", &M[6], 8, 15, 0, 1 }, | |
{ "M7", &M[7], 8, 15, 0, 1 }, | |
{ "M10", &M[010], 8, 15, 0, 1 }, | |
{ "M11", &M[011], 8, 15, 0, 1 }, | |
{ "M12", &M[012], 8, 15, 0, 1 }, | |
{ "M13", &M[013], 8, 15, 0, 1 }, | |
{ "M14", &M[014], 8, 15, 0, 1 }, | |
{ "M15", &M[015], 8, 15, 0, 1 }, | |
{ "M16", &M[016], 8, 15, 0, 1 }, | |
{ "M17", &M[017], 8, 15, 0, 1 }, /* 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 }, | |
{ "BRZ2", &BRZ[2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BRZ3", &BRZ[3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BRZ4", &BRZ[4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BRZ5", &BRZ[5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BRZ6", &BRZ[6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BRZ7", &BRZ[7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "BAZ0", &BAZ[0], 8, 16, 0, 1 }, | |
{ "BAZ1", &BAZ[1], 8, 16, 0, 1 }, | |
{ "BAZ2", &BAZ[2], 8, 16, 0, 1 }, | |
{ "BAZ3", &BAZ[3], 8, 16, 0, 1 }, | |
{ "BAZ4", &BAZ[4], 8, 16, 0, 1 }, | |
{ "BAZ5", &BAZ[5], 8, 16, 0, 1 }, | |
{ "BAZ6", &BAZ[6], 8, 16, 0, 1 }, | |
{ "BAZ7", &BAZ[7], 8, 16, 0, 1 }, | |
{ "TABST", &TABST, 8, 28, 0, 1 }, | |
{ "RP0", &RP[0], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP1", &RP[1], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP2", &RP[2], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP3", &RP[3], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP4", &RP[4], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP5", &RP[5], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP6", &RP[6], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RP7", &RP[7], 8, 48, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "RZ", &RZ, 8, 32, 0, 1 }, | |
{ "FP1", &pult[0][1], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP2", &pult[0][2], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP3", &pult[0][3], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP4", &pult[0][4], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP5", &pult[0][5], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP6", &pult[0][6], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ "FP7", &pult[0][7], 8, 50, 0, 1, NULL, NULL, REG_VMIO }, | |
{ 0 } | |
}; | |
UNIT reg_unit = { | |
UDATA (NULL, 0, 8) | |
}; | |
DEVICE reg_dev = { | |
"REG", ®_unit, reg_reg, NULL, | |
1, 8, 1, 1, 8, 50, | |
}; | |
/* | |
* SCP data structures and interface routines | |
* | |
* sim_name simulator name string | |
* sim_PC pointer to saved PC register descriptor | |
* sim_emax maximum number of words for examine/deposit | |
* sim_devices array of pointers to simulated devices | |
* sim_stop_messages array of pointers to stop messages | |
* sim_load binary loader | |
*/ | |
char sim_name[] = "БЭСМ-6"; | |
REG *sim_PC = &cpu_reg[0]; | |
int32 sim_emax = 1; /* max number of addressable units per instruction */ | |
DEVICE *sim_devices[] = { | |
&cpu_dev, | |
®_dev, | |
&drum_dev, | |
&disk_dev, | |
&mmu_dev, | |
&clock_dev, | |
&printer_dev, | |
&fs_dev, | |
&tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */ | |
0 | |
}; | |
const char *sim_stop_messages[] = { | |
"Неизвестная ошибка", /* Unknown error */ | |
"Останов", /* STOP */ | |
"Точка останова", /* Emulator breakpoint */ | |
"Точка останова по считыванию", /* Emulator read watchpoint */ | |
"Точка останова по записи", /* Emulator write watchpoint */ | |
"Выход за пределы памяти", /* Run out end of memory */ | |
"Запрещенная команда", /* Invalid instruction */ | |
"Контроль команды", /* A data-tagged word fetched */ | |
"Команда в чужом листе", /* Paging error during fetch */ | |
"Число в чужом листе", /* Paging error during load/store */ | |
"Контроль числа МОЗУ", /* RAM parity error */ | |
"Контроль числа БРЗ", /* Write cache parity error */ | |
"Переполнение АУ", /* Arith. overflow */ | |
"Деление на нуль", /* Division by zero or denorm */ | |
"Двойное внутреннее прерывание", /* SIMH: Double internal interrupt */ | |
"Чтение неформатированного барабана", /* Reading unformatted drum */ | |
"Чтение неформатированного диска", /* Reading unformatted disk */ | |
"Останов по КРА", /* Hardware breakpoint */ | |
"Останов по считыванию", /* Load watchpoint */ | |
"Останов по записи", /* Store watchpoint */ | |
"Не реализовано", /* Unimplemented I/O or special reg. access */ | |
}; | |
/* | |
* Memory examine | |
*/ | |
t_stat cpu_examine (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) | |
{ | |
if (addr >= MEMSIZE) | |
return SCPE_NXM; | |
if (vptr) { | |
if (addr < 010) { | |
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, char *cptr, void *desc) | |
{ | |
GRP |= GRP_PANEL_REQ; | |
return SCPE_OK; | |
} | |
/* | |
* Hardwired program selector validation | |
*/ | |
t_stat cpu_set_pult (UNIT *u, int32 val, 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, 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: | |
/* TODO: управление выводом на перфокарты */ | |
longjmp (cpu_halt, STOP_UNIMPLEMENTED); | |
break; | |
case 0160: case 0167: | |
/* TODO: управление электромагнитами пробивки перфокарт */ | |
longjmp (cpu_halt, STOP_UNIMPLEMENTED); | |
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 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 (04160 <= val && val <= 04167) { | |
/* 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; | |
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) ? | |
scp_errors [r - SCPE_BASE] : | |
sim_stop_messages [r]; | |
besm6_debug ("/// %05o%s: %s", PC, | |
(RUU & RUU_RIGHT_INSTR) ? "п" : "л", | |
message); | |
} | |
/* | |
* ПоП и ПоК вызывают останов при любом внутреннем прерывании | |
* или прерывании по контролю, соответственно. | |
* Если произошёл останов по ПоП или ПоК, | |
* то продолжение выполнения начнётся с команды, следующей | |
* за вызвавшей прерывание. Как если бы кнопка "ТП" (тип | |
* перехода) была включена. Подробнее на странице 119 ТО9. | |
*/ | |
switch (r) { | |
default: | |
ret: besm6_draw_panel(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 & 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. It can be sped up | |
* for faster console response (16 ms might be a good choice). | |
*/ | |
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 | |
}; |