| /* | |
| * besm6_drum.c: BESM-6 magnetic drum device | |
| * | |
| * Copyright (c) 2009, Serge Vakulenko | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a | |
| * copy of this software and associated documentation files (the "Software"), | |
| * to deal in the Software without restriction, including without limitation | |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| * and/or sell copies of the Software, and to permit persons to whom the | |
| * Software is furnished to do so, subject to the following conditions: | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| * SERGE VAKULENKO OR LEONID BROUKHIS BE LIABLE FOR ANY CLAIM, DAMAGES | |
| * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | |
| * OR OTHER DEALINGS IN THE SOFTWARE. | |
| * Except as contained in this notice, the name of Leonid Broukhis or | |
| * Serge Vakulenko shall not be used in advertising or otherwise to promote | |
| * the sale, use or other dealings in this Software without prior written | |
| * authorization from Leonid Broukhis and Serge Vakulenko. | |
| */ | |
| #include "besm6_defs.h" | |
| /* | |
| * Управляющее слово обмена с магнитным барабаном. | |
| */ | |
| #define DRUM_READ_OVERLAY 020000000 /* считывание с наложением */ | |
| #define DRUM_PARITY_FLAG 010000000 /* блокировка считывания слов с неверной | |
| * чётностью или запись с неверной чётностью */ | |
| #define DRUM_READ_SYSDATA 004000000 /* считывание только служебных слов */ | |
| #define DRUM_PAGE_MODE 001000000 /* обмен целой страницей */ | |
| #define DRUM_READ 000400000 /* чтение с барабана в память */ | |
| #define DRUM_PAGE 000370000 /* номер страницы памяти */ | |
| #define DRUM_BLOCK 0740000000 /* номер блока памяти - 27-24 рр */ | |
| #define DRUM_PARAGRAF 000006000 /* номер абзаца */ | |
| #define DRUM_UNIT 000001600 /* номер барабана */ | |
| #define DRUM_CYLINDER 000000174 /* номер тракта на барабане */ | |
| #define DRUM_SECTOR 000000003 /* номер сектора */ | |
| /* | |
| * Параметры обмена с внешним устройством. | |
| */ | |
| int drum_op; /* Условное слово обмена */ | |
| int drum_zone; /* Номер зоны на барабане */ | |
| int drum_sector; /* Начальный номер сектора на барабане */ | |
| int drum_memory; /* Начальный адрес памяти */ | |
| int drum_nwords; /* Количество слов обмена */ | |
| int drum_fail; /* Маска ошибок по направлениям */ | |
| t_stat drum_event (UNIT *u); | |
| /* | |
| * DRUM data structures | |
| * | |
| * drum_dev DRUM device descriptor | |
| * drum_unit DRUM unit descriptor | |
| * drum_reg DRUM register list | |
| */ | |
| UNIT drum_unit [] = { | |
| { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, | |
| { UDATA (drum_event, UNIT_FIX+UNIT_ATTABLE+UNIT_ROABLE, DRUM_SIZE) }, | |
| }; | |
| REG drum_reg[] = { | |
| { "УС", &drum_op, 8, 24, 0, 1 }, | |
| { "ЗОНА", &drum_zone, 8, 10, 0, 1 }, | |
| { "СЕКТОР", &drum_sector, 8, 2, 0, 1 }, | |
| { "МОЗУ", &drum_memory, 8, 15, 0, 1 }, | |
| { "СЧСЛОВ", &drum_nwords, 8, 11, 0, 1 }, | |
| { 0 } | |
| }; | |
| MTAB drum_mod[] = { | |
| { 0 } | |
| }; | |
| t_stat drum_reset (DEVICE *dptr); | |
| t_stat drum_attach (UNIT *uptr, char *cptr); | |
| t_stat drum_detach (UNIT *uptr); | |
| DEVICE drum_dev = { | |
| "DRUM", drum_unit, drum_reg, drum_mod, | |
| 2, 8, 19, 1, 8, 50, | |
| NULL, NULL, &drum_reset, NULL, &drum_attach, &drum_detach, | |
| NULL, DEV_DISABLE | DEV_DEBUG | |
| }; | |
| /* | |
| * Reset routine | |
| */ | |
| t_stat drum_reset (DEVICE *dptr) | |
| { | |
| drum_op = 0; | |
| drum_zone = 0; | |
| drum_sector = 0; | |
| drum_memory = 0; | |
| drum_nwords = 0; | |
| sim_cancel (&drum_unit[0]); | |
| sim_cancel (&drum_unit[1]); | |
| return SCPE_OK; | |
| } | |
| t_stat drum_attach (UNIT *u, char *cptr) | |
| { | |
| t_stat s; | |
| s = attach_unit (u, cptr); | |
| if (s != SCPE_OK) | |
| return s; | |
| if (u == &drum_unit[0]) | |
| GRP |= GRP_DRUM1_FREE; | |
| else | |
| GRP |= GRP_DRUM2_FREE; | |
| return SCPE_OK; | |
| } | |
| t_stat drum_detach (UNIT *u) | |
| { | |
| if (u == &drum_unit[0]) | |
| GRP &= ~GRP_DRUM1_FREE; | |
| else | |
| GRP &= ~GRP_DRUM2_FREE; | |
| return detach_unit (u); | |
| } | |
| /* | |
| * Отладочная печать массива данных обмена. | |
| */ | |
| #if 0 | |
| static void log_io (UNIT *u) | |
| { | |
| t_value *data, *sysdata; | |
| int i; | |
| void print_word (t_value val) { | |
| fprintf (sim_log, " %o-%04o-%04o-%04o-%04o", | |
| (int) (val >> 48) & 07, | |
| (int) (val >> 36) & 07777, | |
| (int) (val >> 24) & 07777, | |
| (int) (val >> 12) & 07777, (int) val & 07777); | |
| } | |
| data = &memory [drum_memory]; | |
| sysdata = (u == &drum_unit[0]) ? &memory [010] : &memory [020]; | |
| if (drum_nwords == 1024) { | |
| fprintf (sim_log, "=== зона МБ %d.%03o:", | |
| (u == &drum_unit[0]) ? 1 : 2, drum_zone); | |
| for (i=0; i<8; ++i) | |
| print_word (sysdata[i]); | |
| } else { | |
| sysdata += drum_sector*2; | |
| fprintf (sim_log, "=== сектор МБ %d.%03o.%o:", | |
| (u == &drum_unit[0]) ? 1 : 2, | |
| drum_zone, drum_sector); | |
| for (i=0; i<2; ++i) | |
| print_word (sysdata[i]); | |
| } | |
| if (! (drum_op & DRUM_READ_SYSDATA)) { | |
| fprintf (sim_log, "\n\t\t "); | |
| for (i=0; i<drum_nwords; ++i) | |
| print_word (data[i]); | |
| } | |
| fprintf (sim_log, "\n"); | |
| } | |
| #endif | |
| /* | |
| * Запись на барабан. | |
| */ | |
| void drum_write (UNIT *u) | |
| { | |
| int ctlr; | |
| t_value *sysdata; | |
| ctlr = (u == &drum_unit[1]); | |
| sysdata = ctlr ? &memory [020] : &memory [010]; | |
| fseek (u->fileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); | |
| sim_fwrite (sysdata, 8, 8, u->fileref); | |
| sim_fwrite (&memory [drum_memory], 8, 1024, u->fileref); | |
| if (ferror (u->fileref)) | |
| longjmp (cpu_halt, SCPE_IOERR); | |
| } | |
| void drum_write_sector (UNIT *u) | |
| { | |
| int ctlr; | |
| t_value *sysdata; | |
| ctlr = (u == &drum_unit[1]); | |
| sysdata = ctlr ? &memory [020] : &memory [010]; | |
| fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, | |
| SEEK_SET); | |
| sim_fwrite (&sysdata [drum_sector*2], 8, 2, u->fileref); | |
| fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, | |
| SEEK_SET); | |
| sim_fwrite (&memory [drum_memory], 8, 256, u->fileref); | |
| if (ferror (u->fileref)) | |
| longjmp (cpu_halt, SCPE_IOERR); | |
| } | |
| /* | |
| * Чтение с барабана. | |
| */ | |
| void drum_read (UNIT *u) | |
| { | |
| int ctlr; | |
| t_value *sysdata; | |
| ctlr = (u == &drum_unit[1]); | |
| sysdata = ctlr ? &memory [020] : &memory [010]; | |
| fseek (u->fileref, ZONE_SIZE * drum_zone * 8, SEEK_SET); | |
| if (sim_fread (sysdata, 8, 8, u->fileref) != 8) { | |
| /* Чтение неинициализированного барабана */ | |
| drum_fail |= 0100 >> ctlr; | |
| return; | |
| } | |
| if (! (drum_op & DRUM_READ_SYSDATA) && | |
| sim_fread (&memory[drum_memory], 8, 1024, u->fileref) != 1024) { | |
| /* Чтение неинициализированного барабана */ | |
| drum_fail |= 0100 >> ctlr; | |
| return; | |
| } | |
| if (ferror (u->fileref)) | |
| longjmp (cpu_halt, SCPE_IOERR); | |
| } | |
| void drum_read_sector (UNIT *u) | |
| { | |
| int ctlr; | |
| t_value *sysdata; | |
| ctlr = (u == &drum_unit[1]); | |
| sysdata = ctlr ? &memory [020] : &memory [010]; | |
| fseek (u->fileref, (ZONE_SIZE*drum_zone + drum_sector*2) * 8, SEEK_SET); | |
| if (sim_fread (&sysdata [drum_sector*2], 8, 2, u->fileref) != 2) { | |
| /* Чтение неинициализированного барабана */ | |
| drum_fail |= 0100 >> ctlr; | |
| return; | |
| } | |
| if (! (drum_op & DRUM_READ_SYSDATA)) { | |
| fseek (u->fileref, (ZONE_SIZE*drum_zone + 8 + drum_sector*256) * 8, | |
| SEEK_SET); | |
| if (sim_fread (&memory[drum_memory], 8, 256, u->fileref) != 256) { | |
| /* Чтение неинициализированного барабана */ | |
| drum_fail |= 0100 >> ctlr; | |
| return; | |
| } | |
| } | |
| if (ferror (u->fileref)) | |
| longjmp (cpu_halt, SCPE_IOERR); | |
| } | |
| static void clear_memory (t_value *p, int nwords) | |
| { | |
| while (nwords-- > 0) | |
| *p++ = SET_PARITY (0, PARITY_NUMBER); | |
| } | |
| /* | |
| * Выполнение обращения к барабану. | |
| */ | |
| void drum (int ctlr, uint32 cmd) | |
| { | |
| UNIT *u = &drum_unit[ctlr]; | |
| drum_op = cmd; | |
| if (drum_op & DRUM_PAGE_MODE) { | |
| /* Обмен страницей */ | |
| drum_nwords = 1024; | |
| drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; | |
| drum_sector = 0; | |
| drum_memory = (cmd & DRUM_PAGE) >> 2 | (cmd & DRUM_BLOCK) >> 8; | |
| if (drum_dev.dctrl) | |
| besm6_debug ("### %s МБ %c%d зона %02o память %05o-%05o", | |
| (drum_op & DRUM_READ) ? "чтение" : "запись", | |
| ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, | |
| drum_memory, drum_memory + drum_nwords - 1); | |
| if (drum_op & DRUM_READ) { | |
| clear_memory (ctlr ? &memory [020] : &memory [010], 8); | |
| if (! (drum_op & DRUM_READ_SYSDATA)) | |
| clear_memory (&memory[drum_memory], 1024); | |
| } | |
| } else { | |
| /* Обмен сектором */ | |
| drum_nwords = 256; | |
| drum_zone = (cmd & (DRUM_UNIT | DRUM_CYLINDER)) >> 2; | |
| drum_sector = cmd & DRUM_SECTOR; | |
| drum_memory = (cmd & (DRUM_PAGE | DRUM_PARAGRAF)) >> 2 | (cmd & DRUM_BLOCK) >> 8; | |
| if (drum_dev.dctrl) | |
| besm6_debug ("### %s МБ %c%d зона %02o сектор %d память %05o-%05o", | |
| (drum_op & DRUM_READ) ? "чтение" : "запись", | |
| ctlr + '1', (drum_zone >> 5 & 7), drum_zone & 037, | |
| drum_sector & 3, | |
| drum_memory, drum_memory + drum_nwords - 1); | |
| if (drum_op & DRUM_READ) { | |
| clear_memory (ctlr ? &memory [020 + drum_sector*2] : | |
| &memory [010 + drum_sector*2], 2); | |
| if (! (drum_op & DRUM_READ_SYSDATA)) | |
| clear_memory (&memory[drum_memory], 256); | |
| } | |
| } | |
| if ((drum_dev.flags & DEV_DIS) || ! u->fileref) { | |
| /* Device not attached. */ | |
| drum_fail |= 0100 >> ctlr; | |
| return; | |
| } | |
| drum_fail &= ~(0100 >> ctlr); | |
| if (drum_op & DRUM_READ_OVERLAY) { | |
| /* Not implemented. */ | |
| longjmp (cpu_halt, SCPE_NOFNC); | |
| } | |
| if (drum_op & DRUM_READ) { | |
| if (drum_op & DRUM_PAGE_MODE) | |
| drum_read (u); | |
| else | |
| drum_read_sector (u); | |
| } else { | |
| if (drum_op & DRUM_PARITY_FLAG) { | |
| besm6_log ("### запись МБ с неправильной чётностью не реализована"); | |
| longjmp (cpu_halt, SCPE_NOFNC); | |
| } | |
| if (u->flags & UNIT_RO) { | |
| /* Read only. */ | |
| longjmp (cpu_halt, SCPE_RO); | |
| } | |
| if (drum_op & DRUM_PAGE_MODE) | |
| drum_write (u); | |
| else | |
| drum_write_sector (u); | |
| } | |
| /*if (drum_dev.dctrl && sim_log) | |
| log_io (u);*/ | |
| /* Гасим главный регистр прерываний. */ | |
| if (u == &drum_unit[0]) | |
| GRP &= ~GRP_DRUM1_FREE; | |
| else | |
| GRP &= ~GRP_DRUM2_FREE; | |
| /* Ждём события от устройства. | |
| * Согласно данным из книжки Мазного Г.Л., | |
| * даём 20 мсек на обмен, или 200 тыс.тактов. */ | |
| /*sim_activate (u, 20*MSEC);*/ | |
| sim_activate (u, 20*USEC); /* Ускорим для отладки. */ | |
| } | |
| /* | |
| * Событие: закончен обмен с МБ. | |
| * Устанавливаем флаг прерывания. | |
| */ | |
| t_stat drum_event (UNIT *u) | |
| { | |
| if (u == &drum_unit[0]) | |
| GRP |= GRP_DRUM1_FREE; | |
| else | |
| GRP |= GRP_DRUM2_FREE; | |
| return SCPE_OK; | |
| } | |
| /* | |
| * Опрос ошибок обмена командой 033 4035. | |
| */ | |
| int drum_errors () | |
| { | |
| return drum_fail; | |
| } |