| /* | |
| * 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 | |
| }; |