diff --git a/BESM6/besm6_cpu.c b/BESM6/besm6_cpu.c
index fcbbddcd..ae5c5684 100644
--- a/BESM6/besm6_cpu.c
+++ b/BESM6/besm6_cpu.c
@@ -73,7 +73,7 @@ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
GRP_CHAN5_FREE | GRP_CHAN6_FREE |\
GRP_CHAN7_FREE )
-#define PRP_WIRED_BITS (PRP_UVVK1_END | PRP_UVVK2_END |\
+#define PRP_WIRED_BITS (PRP_VU1_END | PRP_VU2_END |\
PRP_PCARD1_PUNCH | PRP_PCARD2_PUNCH |\
PRP_PTAPE1_PUNCH | PRP_PTAPE2_PUNCH )
@@ -278,6 +278,8 @@ DEVICE *sim_devices[] = {
&clock_dev,
&printer_dev,
&fs_dev,
+ &pl_dev,
+ &vu_dev,
&pi_dev,
&tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */
0
@@ -361,7 +363,7 @@ t_stat cpu_reset (DEVICE *dptr)
M[i] = 0;
/* Punchcard readers not yet implemented thus not ready */
- READY2 |= 042000000;
+ /* READY2 |= 042000000; */
/* Регистр 17: БлП, БлЗ, ПОП, ПОК, БлПр */
M[PSW] = PSW_MMAP_DISABLE | PSW_PROT_DISABLE | PSW_INTR_HALT |
@@ -637,8 +639,8 @@ static void cmd_033 ()
*/
break;
case 0150: case 0151:
- /* TODO: reading from punchcards */
- longjmp (cpu_halt, STOP_UNIMPLEMENTED);
+ /* sending commands to the punched card readers */
+ vu_control (Aex - 0150, (uint32) (ACC & 017));
break;
case 0153:
/* гашение аппаратуры сопряжения с терминалами */
@@ -657,8 +659,11 @@ static void cmd_033 ()
pi_write (Aex & 7, (uint32) ACC & BITS(20));
break;
case 0170: case 0171:
- /* TODO: пробивка строки на перфоленте */
- longjmp (cpu_halt, STOP_UNIMPLEMENTED);
+ /* пробивка строки на перфоленте */
+ pl_control (Aex & 1, (uint32) ACC & BITS(8));
+ break;
+ case 0172: case 0173:
+ besm6_debug(">>> Potential plotter output: %03o", (uint32) ACC & BITS(8));
break;
case 0174: case 0175:
/* Выдача кода в пульт оператора */
@@ -740,6 +745,10 @@ static void cmd_033 ()
* группами по 8 штук каждые несколько секунд. */
ACC = 0;
break;
+ case 04150: case 04154:
+ /* считывание строки с устройства ввода с перфоленты */
+ ACC = vu_read ((Aex - 04150) >> 2);
+ break;
case 04160: case 04161: case 04162: case 04163:
case 04164: case 04165: case 04166: case 04167:
/* Punchcard output: reading a punched line for checking, 20 bit at a time */
diff --git a/BESM6/besm6_defs.h b/BESM6/besm6_defs.h
index 84b14c7e..a563779a 100644
--- a/BESM6/besm6_defs.h
+++ b/BESM6/besm6_defs.h
@@ -146,6 +146,8 @@ extern DEVICE clock_dev;
extern DEVICE printer_dev;
extern DEVICE tty_dev;
extern DEVICE fs_dev;
+extern DEVICE pl_dev;
+extern DEVICE vu_dev;
extern DEVICE pi_dev;
extern jmp_buf cpu_halt;
@@ -355,6 +357,17 @@ int vt_is_idle (void);
void fs_control (int num, uint32 cmd);
int fs_read (int num);
+/*
+ * Punchtape output.
+ */
+void pl_control (int num, uint32 cmd);
+
+/*
+ * Punchcard input.
+ */
+void vu_control (int num, uint32 cmd);
+int vu_read (int num);
+
/*
* Вывод на перфокарты.
*/
@@ -399,8 +412,8 @@ t_value besm6_unpack (t_value val, t_value mask);
#define GRP_PRN2_SYNC 02000000000000000LL /* 47 */
#define GRP_DRUM1_FREE 01000000000000000LL /* 46 */
#define GRP_DRUM2_FREE 00400000000000000LL /* 45 */
-#define GRP_UVVK1_SYNC 00200000000000000LL /* 44 */
-#define GRP_UVVK2_SYNC 00100000000000000LL /* 43 */
+#define GRP_VU1_SYNC 00200000000000000LL /* 44 */
+#define GRP_VU2_SYNC 00100000000000000LL /* 43 */
#define GRP_FS1_SYNC 00040000000000000LL /* 42 */
#define GRP_FS2_SYNC 00020000000000000LL /* 41 */
#define GRP_TIMER 00010000000000000LL /* 40 */
@@ -443,8 +456,8 @@ t_value besm6_unpack (t_value val, t_value mask);
/*
* Bits of the peripheral interrupt register ПРП (PRP)
*/
-#define PRP_UVVK1_END 010000000 /* 22 */
-#define PRP_UVVK2_END 004000000 /* 21 */
+#define PRP_VU1_END 010000000 /* 22 */
+#define PRP_VU2_END 004000000 /* 21 */
#define PRP_PCARD1_CHECK 002000000 /* 20 */
#define PRP_PCARD2_CHECK 001000000 /* 19 */
#define PRP_PCARD1_PUNCH 000400000 /* 18 */
diff --git a/BESM6/besm6_pl.c b/BESM6/besm6_pl.c
new file mode 100644
index 00000000..87a7a997
--- /dev/null
+++ b/BESM6/besm6_pl.c
@@ -0,0 +1,143 @@
+/*
+ * besm6_punch.c: BESM-6 punchtape output
+ *
+ * Copyright (c) 2020, Leonid Broukhis
+ *
+ * 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"
+
+t_stat pl_event (UNIT *u);
+
+UNIT pl_unit [] = {
+ { UDATA (pl_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
+ { UDATA (pl_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
+};
+
+#define PL1_READY 04000
+#define PL2_READY 02000
+
+#define SET_RDY2(x) do READY2 |= x; while (0)
+#define CLR_RDY2(x) do READY2 &= ~(x); while (0)
+#define ISSET_RDY2(x) ((READY2 & (x)) != 0)
+#define ISCLR_RDY2(x) ((READY2 & (x)) == 0)
+
+#define PL_RATE (int)(12.5*MSEC)
+
+unsigned char PL[2];
+
+t_stat pl_reset (DEVICE *dptr);
+t_stat pl_attach (UNIT *uptr, CONST char *cptr);
+t_stat pl_detach (UNIT *uptr);
+
+DEVICE pl_dev = {
+ "PL", pl_unit, NULL, NULL,
+ 2, 8, 19, 1, 8, 50,
+ NULL, NULL, &pl_reset, NULL, &pl_attach, &pl_detach,
+ NULL, DEV_DISABLE | DEV_DEBUG
+};
+
+t_stat pl_reset (DEVICE *dptr)
+{
+ sim_cancel (&pl_unit[0]);
+ sim_cancel (&pl_unit[1]);
+ CLR_RDY2(PL1_READY | PL2_READY);
+ if (pl_unit[0].flags & UNIT_ATT) {
+ SET_RDY2(PL1_READY);
+ }
+ if (pl_unit[1].flags & UNIT_ATT) {
+ SET_RDY2(PL2_READY);
+ }
+ if (pl_dev.dctrl)
+ besm6_debug("reset READY2 := %08o", READY2);
+ return SCPE_OK;
+}
+
+t_stat pl_attach (UNIT *u, CONST char *cptr)
+{
+ t_stat s;
+ int num = u - pl_unit;
+ s = attach_unit (u, cptr);
+ if (s != SCPE_OK)
+ return s;
+ SET_RDY2(PL1_READY >> num);
+ if (pl_dev.dctrl)
+ besm6_debug("attach READY2 := %08o", READY2);
+ return SCPE_OK;
+}
+
+t_stat pl_detach (UNIT *u)
+{
+ int num = u - pl_unit;
+ CLR_RDY2(PL1_READY >> num);
+ if (pl_dev.dctrl)
+ besm6_debug("detach READY2 := %08o", READY2);
+ return detach_unit (u);
+}
+
+void pl_control (int num, uint32 cmd)
+{
+ UNIT *u = &pl_unit[num];
+ FILE *f = u->fileref;
+
+ if (! ISSET_RDY2(PL1_READY >> num)) {
+ if (pl_dev.dctrl)
+ besm6_debug("<<< PL80-%d not ready", num);
+ return;
+ }
+ putc(cmd & 0xff, f);
+ PRP &= ~(PRP_PTAPE1_PUNCH >> num);
+ CLR_RDY2(PL1_READY >> num);
+ sim_activate_after(u, PL_RATE);
+ if (pl_dev.dctrl) {
+ besm6_debug("PL%d: punching %03o", num, cmd & 0xff);
+ besm6_debug("punch READY2 := %08o", READY2);
+ }
+}
+
+unsigned char unicode_to_gost (unsigned short val);
+
+/*
+ * The UPP code is the GOST 10859 code with odd parity.
+ * UPP stood for "unit for preparation of punchards".
+ */
+static unsigned char unicode_to_upp (unsigned short ch) {
+ unsigned char ret;
+ ch = ret = unicode_to_gost (ch);
+ ch = (ch & 0x55) + ((ch >> 1) & 0x55);
+ ch = (ch & 0x33) + ((ch >> 2) & 0x33);
+ ch = (ch & 0x0F) + ((ch >> 4) & 0x0F);
+ return (ch & 1) ? ret : ret | 0x80;
+}
+
+t_stat pl_event (UNIT *u)
+{
+ int num = u - pl_unit;
+ PRP |= PRP_PTAPE1_PUNCH >> num;
+ SET_RDY2(PL1_READY >> num);
+ if (pl_dev.dctrl) {
+ besm6_debug("PL%d event, READY2 := %08o", num, READY2);
+ }
+ return SCPE_OK;
+}
diff --git a/BESM6/besm6_punch.c b/BESM6/besm6_punch.c
index 625a4d90..93744365 100644
--- a/BESM6/besm6_punch.c
+++ b/BESM6/besm6_punch.c
@@ -208,7 +208,7 @@ static unsigned char unicode_to_upp (unsigned short ch) {
return (ch & 1) ? ret : ret | 0x80;
}
-static int utf8_getc (FILE *fin);
+int utf8_getc (FILE *fin);
/*
* Событие: читаем очередной символ с перфоленты в регистр.
@@ -461,7 +461,7 @@ unicode_to_gost (unsigned short val)
* Read Unicode symbol from file.
* Convert from UTF-8 encoding.
*/
-static int
+int
utf8_getc (FILE *fin)
{
int c1, c2, c3;
diff --git a/BESM6/besm6_punchcard.c b/BESM6/besm6_punchcard.c
index 8cc5ac16..2e5cac4f 100644
--- a/BESM6/besm6_punchcard.c
+++ b/BESM6/besm6_punchcard.c
@@ -28,17 +28,6 @@
*/
#include "besm6_defs.h"
-#if 0
-/*
- * Punchcard input not yet implemented.
- */
-t_stat uvvk_event (UNIT *u); /* punched card reader */
-UNIT uvvk_unit [] = {
- { UDATA (uvvk_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
- { UDATA (uvvk_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
-};
-#endif
-
t_stat pi_event (UNIT *u); /* punched card writer */
UNIT pi_unit [] = {
{ UDATA (pi_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
diff --git a/BESM6/besm6_vu.c b/BESM6/besm6_vu.c
new file mode 100644
index 00000000..f88f3e5f
--- /dev/null
+++ b/BESM6/besm6_vu.c
@@ -0,0 +1,579 @@
+/*
+ * besm6_vu.c: BESM-6 punchcard reader
+ *
+ * Copyright (c) 2020, Leonid Broukhis
+ *
+ * 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"
+
+t_stat vu_event (UNIT *u); /* punched card reader */
+UNIT vu_unit [] = {
+ { UDATA (vu_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
+ { UDATA (vu_event, UNIT_SEQ+UNIT_ATTABLE, 0) },
+};
+
+/* Dispak seems to only care about the NOTREADY flag,
+ * the proper behavior of FEED and MAYSTART may vary.
+ */
+#define VU1_NOTREADY (1<<23)
+#define VU1_FEED (1<<22)
+#define VU1_MAYSTART (1<<21)
+#define VU2_NOTREADY (1<<19)
+#define VU2_FEED (1<<18)
+#define VU2_MAYSTART (1<<17)
+
+#define SET_RDY2(x) do READY2 |= x; while (0)
+#define CLR_RDY2(x) do READY2 &= ~(x); while (0)
+#define ISSET_RDY2(x) ((READY2 & (x)) != 0)
+#define ISCLR_RDY2(x) ((READY2 & (x)) == 0)
+
+#define VU_RATE_CPM 600
+
+/* Interrupts are every 2 columns */
+#define CARD_LEN (80/2)
+#define DFLT_DELAY (60*1000*MSEC/VU_RATE_CPM/CARD_LEN)
+
+/*
+ * The lines are first converted to GOST 10859; some GOST codes need to be known to the emulator.
+ */
+#define GOST_DOT 016 /* unpunched position */
+#define GOST_O 056 /* punched position */
+
+/* 6 "open quote" characters and an end-of-card indicator, can be entered as `````` */
+#define DISP_END "\032\032\032\032\032\032\377"
+
+unsigned int vu_col_dly = DFLT_DELAY;
+unsigned int vu_end_dly = DFLT_DELAY/20; /* that seems to work */
+unsigned int vu_card_dly = 10*DFLT_DELAY;
+unsigned int vu_updkstart[2], vu_updkend[2];
+
+unsigned int VU[2];
+
+REG vu_reg[] = {
+ { REGDATA ( Готов, READY2, 2, 8, 16, 1, NULL, NULL, 0, 0, 0) },
+ { ORDATA ( ВУ-0, VU[0], 24) },
+ { ORDATA ( ВУ-1, VU[1], 24) },
+ { 0 }
+};
+
+t_stat vu_set_coldly (UNIT *u, int32 val, CONST char *cptr, void *desc)
+{
+ if (cptr && atoi(cptr) > 0) {
+ vu_col_dly = atoi(cptr);
+ return SCPE_OK;
+ } else
+ sim_printf("Integer value required\n");
+ return SCPE_ARG;
+}
+
+t_stat vu_set_enddly (UNIT *u, int32 val, CONST char *cptr, void *desc)
+{
+ if (cptr && atoi(cptr) > 0) {
+ vu_end_dly = atoi(cptr);
+ return SCPE_OK;
+ } else
+ sim_printf("Integer value required\n");
+ return SCPE_ARG;
+}
+
+t_stat vu_set_carddly (UNIT *u, int32 val, CONST char *cptr, void *desc)
+{
+ if (cptr && atoi(cptr) > 0) {
+ vu_card_dly = atoi(cptr);
+ return SCPE_OK;
+ } else
+ sim_printf("Integer value required\n");
+ return SCPE_ARG;
+}
+
+t_stat vu_show_coldly (FILE *st, UNIT *u, int32 v, CONST void *dp)
+{
+ fprintf(st, "Column delay is %d", vu_col_dly);
+ return SCPE_OK;
+}
+
+t_stat vu_show_enddly (FILE *st, UNIT *u, int32 v, CONST void *dp)
+{
+ fprintf(st, "Delay before the end of card is %d", vu_end_dly);
+ return SCPE_OK;
+}
+
+t_stat vu_show_carddly (FILE *st, UNIT *u, int32 v, CONST void *dp)
+{
+ fprintf(st, "Card delay is %d", vu_card_dly);
+ return SCPE_OK;
+}
+
+t_stat vu_set_updk (UNIT *u, int32 val, CONST char *cptr, void *desc)
+{
+ unsigned start, end;
+ int num = u - vu_unit;
+ if (!cptr) {
+ sim_printf("Range set to MAX\n");
+ vu_updkstart[num] = 1;
+ vu_updkend[num] = 0;
+ return SCPE_OK;
+ }
+ if (sscanf(cptr, "%u-%u", &start, &end) != 2 ||
+ (start == 0 && end != 0) || (end != 0 && end < start)) {
+ sim_printf("Range required, e.g. 10-100, or 0-0 to disable.\n");
+ return SCPE_ARG;
+ }
+ vu_updkstart[num] = start;
+ vu_updkend[num] = end;
+ return SCPE_OK;
+}
+
+t_stat vu_show_updk (FILE *st, UNIT *u, int32 v, CONST void *dp)
+{
+ int num = u - vu_unit;
+ if (vu_updkstart[num] == 0 && vu_updkend[num] == 0)
+ fprintf(st, "UPDK disabled");
+ else if (vu_updkend[num] == 0)
+ fprintf(st, "UPDK card %d to EOF", vu_updkstart[num]);
+ else
+ fprintf(st, "UPDK cards %d-%d", vu_updkstart[num], vu_updkend[num]);
+ return SCPE_OK;
+}
+
+MTAB vu_mod[] = {
+ { MTAB_XTD|MTAB_VDV,
+ 0, "COLDLY", "COLDLY", &vu_set_coldly, &vu_show_coldly, NULL,
+ "Delay between pair-of-columns interrupts,\n"
+ "and between the last column interrupt and posedge of the end-of-card signal." },
+ { MTAB_XTD|MTAB_VDV,
+ 0, "ENDDLY", "ENDDLY", &vu_set_enddly, &vu_show_enddly, NULL,
+ "Duration of the end-of-card signal." },
+ { MTAB_XTD|MTAB_VDV,
+ 0, "CARDDLY", "CARDDLY", &vu_set_carddly, &vu_show_carddly, NULL,
+ "Delay between the negedge of the end-of-card signal and the next card interrupt." },
+ { MTAB_XTD|MTAB_VUN,
+ 0, "UPDK", "UPDK", &vu_set_updk, &vu_show_updk, NULL,
+ "Range of cards to be converted to UPDK, e.g. SET UPDK 10-100. Use 0-0 to disable." },
+ { 0 }
+};
+
+
+t_stat vu_reset (DEVICE *dptr);
+t_stat vu_attach (UNIT *uptr, CONST char *cptr);
+t_stat vu_detach (UNIT *uptr);
+
+DEVICE vu_dev = {
+ "VU", vu_unit, vu_reg, vu_mod,
+ 2, 8, 19, 1, 8, 50,
+ NULL, NULL, &vu_reset, NULL, &vu_attach, &vu_detach,
+ NULL, DEV_DISABLE | DEV_DEBUG
+};
+
+
+typedef enum {
+ VU_IDLE,
+ VU_STARTING,
+ VU_COL,
+ VU_COL_LAST = VU_COL + CARD_LEN - 1,
+ VU_TAIL, VU_TAIL2
+} VU_state;
+
+VU_state vu_state[2], vu_next[2];
+
+int vu_isfifo[2];
+
+// Each card can hold up to 120 bytes; potentially valid GOST chars, expressible in UPDK, are 0-0177.
+// True spaces are 017; bytes past the end of line (empty columns) are 0377.
+
+unsigned char vu_gost[2][120];
+unsigned short vu_image[2][80];
+unsigned int vu_cardcnt[2];
+
+/*
+ * Reset routine
+ */
+t_stat vu_reset (DEVICE *dptr)
+{
+ sim_cancel (&vu_unit[0]);
+ sim_cancel (&vu_unit[1]);
+ vu_state[0] = vu_state[1] = VU_IDLE;
+ SET_RDY2(VU1_NOTREADY | VU2_NOTREADY);
+ if (vu_unit[0].flags & UNIT_ATT) {
+ CLR_RDY2(VU1_NOTREADY);
+ }
+ if (vu_unit[1].flags & UNIT_ATT) {
+ CLR_RDY2(VU2_NOTREADY);
+ }
+ return SCPE_OK;
+}
+
+/*
+ * Attaches a text file in UTF-8. By default the lines are converted to the linewise GOST/UPP
+ * code as it allows each card to contain up to 120 characters. The columnwise GOST/UPDK code
+ * is not supported yet.
+ */
+t_stat vu_attach (UNIT *u, CONST char *cptr)
+{
+ t_stat s;
+ int num = u - vu_unit;
+
+ s = attach_unit (u, cptr);
+ if (s != SCPE_OK)
+ return s;
+ vu_isfifo[num] = (0 == sim_set_fifo_nonblock (u->fileref));
+ CLR_RDY2(VU1_NOTREADY >> (num*4));
+ vu_cardcnt[num] = 0;
+ return SCPE_OK;
+}
+
+t_stat vu_detach (UNIT *u)
+{
+ int num = u - vu_unit;
+ SET_RDY2(VU1_NOTREADY >>(num*4));
+ return detach_unit (u);
+}
+
+/*
+ * Controlling the card reader.
+ */
+void vu_control (int num, uint32 cmd)
+{
+ UNIT *u = &vu_unit[num];
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d cmd %o", num, cmd);
+ if (ISSET_RDY2(VU1_NOTREADY >> (num*4))) {
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d not ready", num, cmd);
+ return;
+ }
+ if (cmd & 010) {
+ // Resetting the column buffer.
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d buffer reset", num);
+ VU[num] = 0;
+ cmd &= ~010;
+ }
+ switch (cmd) {
+ case 2: /* stop */
+ sim_cancel (u);
+ vu_state[num] = VU_IDLE;
+ SET_RDY2(VU1_MAYSTART >> (num*4));
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d OFF", num);
+ if (vu_state[num] == VU_TAIL) {
+ if (! vu_isfifo[num]) {
+ vu_detach(u);
+ return;
+ }
+ }
+ break;
+ case 4: /* read card */
+ case 1: /* read deck */
+ vu_state[num] = VU_STARTING;
+ CLR_RDY2(VU1_MAYSTART >> (num*4));
+ vu_next[num] = cmd == 1 ? VU_STARTING : VU_IDLE;
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d %s read.", num, cmd == 1 ? "DECK" : "CARD");
+ sim_activate (u, vu_col_dly);
+ break;
+ case 0:
+ break;
+ default:
+ besm6_debug ("<<< VU-%d unknown cmd %o", num, cmd);
+ }
+}
+
+extern unsigned char unicode_to_gost(unsigned short);
+extern unsigned short gost_to_unicode(unsigned char);
+
+void uni2utf8(unsigned short ch, char buf[5]) {
+ int i = 0;
+ if (ch < 0x80) {
+ buf[i++] = ch & 0x7F;
+ } else if (ch < 0x800) {
+ buf[i++] = (ch >> 6 | 0xc0);
+ buf[i++] = ((ch & 0x3f) | 0x80);
+ } else {
+ buf[i++] = (ch >> 12 | 0xe0);
+ buf[i++] = (((ch >> 6) & 0x3f) | 0x80);
+ buf[i++] = ((ch & 0x3f) | 0x80);
+ }
+ buf[i] = '\0';
+}
+
+/*
+ * Converts a string consisting of 0-9+- digits, plus, or minus to a 12-bit map of punches.
+ */
+static int punch(const char * s) {
+ int r = 0;
+ while (*s) {
+ r |= *s >= '0' ? 4 << (*s - '0') : *s == '+' ? 1 : *s == '-' ? 2 : 0;
+ ++s;
+ }
+ return r;
+}
+
+/*
+ * The UPDK code is a modified
+ * [GOST 10859-CARD](https://ub.fnwi.uva.nl/computermuseum//DWcodes.html#A056)
+ * for better distinctiveness wrt other column codes.
+ * The UPDK codes are taken from Maznyj, "Programming in the Dubna system".
+ */
+static unsigned short gost_to_updk (unsigned char ch) {
+ unsigned short ret;
+ // Assuming that bits in the card are 9876543210-+
+ // Bits from the upper and lower halves are XORed
+ static char * upper[4] = { "", "+0", "-0", "+-" };
+ static char * lower[2][16] = {
+ { "0", "1", "2", "3", "4", "5", "6", "7",
+ "8", "9", "082", "083", "084", "085", "086", "087" },
+ { "390", "391", "392", "39210", "394", "395", "396", "397",
+ "398", "39801", "39802", "39821", "39804", "39805", "39806", "39807" }
+ };
+ if (ch == 0377 /* filler */ || ch == 017 /* space */) {
+ ret = 0;
+ } else {
+ ret = punch(upper[(ch>>4)&3]) ^ punch(lower[ch>=0100][ch&0xF]);
+ }
+ return ret;
+}
+
+/*
+ * The UPP code is the GOST 10859 code with odd parity.
+ * UPP stood for "unit for preparation of punchards".
+ */
+static unsigned char gost_to_upp (unsigned char ch) {
+ unsigned char ret = ch;
+ ch = (ch & 0x55) + ((ch >> 1) & 0x55);
+ ch = (ch & 0x33) + ((ch >> 2) & 0x33);
+ ch = (ch & 0x0F) + ((ch >> 4) & 0x0F);
+ return (ch & 1) ? ret : ret | 0x80;
+}
+
+static void display_card(int num) {
+ if (vu_updkstart[num] != 0 || vu_updkend[num] != 0) {
+ char buf[80];
+ int i, j;
+ for (i = 0; i < 12; ++i) {
+ for (j = 0; j < 80; ++j)
+ buf[j] = (vu_image[num][j] >> i) & 1 ? 'O' : '.';
+ besm6_debug("<<< VU-%d: %.80s", num, buf);
+ }
+ besm6_debug("<<< VU-%d: ###", num);
+ }
+}
+
+static void reverse_card(int num, int raw) {
+ char content[500];
+ int i, j;
+ memset(vu_image[num], 0, 160);
+ content[0] = 0;
+ for (i = 0; i < 120; ++i) {
+ unsigned char ch = vu_gost[num][i];
+ int mask = 1 << (i / 10);
+ int pos = 8 * (i % 10);
+ if (!raw) {
+ if (ch == 0377)
+ break;
+ ch = gost_to_upp(ch);
+ }
+ for (j = 7; j >= 0; --j) {
+ if (ch & 1)
+ vu_image[num][pos+j] |= mask;
+ ch >>= 1;
+ }
+ }
+}
+
+extern int utf8_getc(FILE*);
+
+static int
+is_prettycard (unsigned char *s)
+{
+ int i;
+ for (i=0; i<80; ++i)
+ if (s[i] != GOST_DOT && s[i] != GOST_O) {
+ return 0;
+ }
+ for (i = 80; i < 120; ++i)
+ if (s[i] != 0377)
+ return 0;
+ return 1;
+}
+
+
+static int chad (int num, int bit, char val)
+{
+ int index = bit / 8;
+
+ switch (val) {
+ case GOST_O:
+ vu_gost[num][index] <<= 1;
+ vu_gost[num][index] |= 1;
+ return 0;
+ case GOST_DOT:
+ vu_gost[num][index] <<= 1;
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+int
+prettycard (UNIT *u)
+{
+ int bit, ch;
+ int num = u - vu_unit;
+ for (bit = 0; bit < 80; bit++) {
+ /* The first line is good, no need to check */
+ chad(num, bit, vu_gost[num][bit]);
+ }
+ for (bit = 80; bit < 12*80; bit++) {
+ ch = utf8_getc(u->fileref);
+ if (ch == '\n' && bit % 80 == 0)
+ ch = utf8_getc(u->fileref);
+ ch = unicode_to_gost(ch);
+ if (chad(num, bit, ch))
+ return -1;
+ if (bit % 80 == 79) {
+ do ch = utf8_getc(u->fileref); while (ch == '\r');
+ if (ch != '\n')
+ return -1;
+ }
+ }
+ /* there may be an empty line after a card */
+ ch = getc(u->fileref);
+ if (ch != '\n')
+ ungetc(ch, u->fileref);
+ return 0;
+}
+
+
+/*
+ * Event: reading two characters (two columns) into the register, sending an interrupt.
+ */
+t_stat vu_event (UNIT *u)
+{
+ int num = u - vu_unit;
+ if (vu_state[num] == VU_STARTING) {
+ // Reading a line and forming the GOST array.
+ int ch;
+ do
+ ch = utf8_getc(u->fileref);
+ while (ch == '\r');
+ if (ch == EOF) {
+ if (vu_dev.dctrl) {
+ besm6_debug("<<< VU-%d: EOF, detaching", num);
+ }
+ vu_state[num] = VU_IDLE;
+ vu_detach(u);
+ } else {
+ int endline = 0, i;
+ ++vu_cardcnt[num];
+ for (i = 0; i < 120; ++i) {
+ if (endline) {
+ vu_gost[num][i] = 0377;
+ } else {
+ int gost;
+ if (ch == EOF || ch == '\n') {
+ endline = 1;
+ gost = 0377;
+ } else {
+ gost = unicode_to_gost(ch);
+ }
+ vu_gost[num][i] = gost;
+ if (!endline && i != 119)
+ do
+ ch = utf8_getc(u->fileref);
+ while (ch == '\r');
+ }
+ }
+ if (!endline) {
+ int ch;
+ do
+ ch = utf8_getc(u->fileref);
+ while (ch == '\n' || ch == EOF);
+
+ }
+ if (0 == strncmp(vu_gost[num], DISP_END, 7)) {
+ // The "dispatcher's end" card, end of card image mode.
+ memset(vu_image[num], 0, 160);
+ vu_image[num][0] = vu_image[num][40] = 0xFFF;
+ } else if (is_prettycard(vu_gost[num])) {
+ if (prettycard(u) < 0) {
+ sim_printf("VU-%d: A badly formatted card image at card %d, garbage will follow",
+ num, vu_cardcnt[num]);
+ }
+ reverse_card(num, 1); /* raw */
+ } else if (vu_updkstart[num] != 0 && vu_cardcnt[num] >= vu_updkstart[num] &&
+ (vu_updkend[num] == 0 || vu_cardcnt[num] <= vu_updkend[num])) {
+ int i;
+ for (i = 0; i < 80; ++i)
+ vu_image[num][i] = gost_to_updk(vu_gost[num][i]);
+ } else {
+ reverse_card(num, 0); /* add parity */
+ }
+
+ if (vu_dev.dctrl) {
+ display_card(num);
+ besm6_debug("<<< VU-%d: card start", num);
+ }
+
+ GRP |= GRP_VU1_SYNC >> num;
+ sim_activate(u, vu_col_dly);
+ vu_state[num] = VU_COL;
+ VU[num] = 0;
+ }
+ } else if (VU_COL <= vu_state[num] && vu_state[num] <= VU_COL_LAST) {
+ int pos = (vu_state[num]++ - VU_COL) * 2;
+ VU[num] = (vu_image[num][pos] << 12) | vu_image[num][pos+1];
+ if (vu_dev.dctrl) {
+ besm6_debug("<<< VU-%d: cols %d-%d: reg %06x", num, pos+1, pos+2, VU[num]);
+ }
+ GRP |= GRP_VU1_SYNC >> num;
+ sim_activate (u, vu_col_dly);
+ } else if (vu_state[num] == VU_TAIL) {
+ PRP |= num == 0 ? PRP_VU1_END : PRP_VU2_END;
+ vu_state[num] = VU_TAIL2;
+ sim_activate(u, vu_end_dly);
+ if (vu_dev.dctrl) {
+ besm6_debug("<<< VU-%d: ------", num);
+ }
+ } else if (vu_state[num] == VU_TAIL2) {
+ PRP &= ~(num == 0 ? PRP_VU1_END : PRP_VU2_END);
+ SET_RDY2(VU1_FEED >> (num*4));
+ if (vu_next[num] == VU_STARTING) {
+ sim_activate (u, vu_card_dly);
+ }
+ vu_state[num] = vu_next[num];
+ if (vu_dev.dctrl) {
+ besm6_debug("<<< VU-%d: ======", num);
+ }
+ } else {
+ besm6_debug("<<< VU-%d: spurious event", num);
+ }
+
+ return SCPE_OK;
+}
+
+int vu_read(int num) {
+ if (vu_dev.dctrl)
+ besm6_debug("<<< VU-%d: reg %06x", num, VU[num]);
+
+ return VU[num];
+}
diff --git a/Visual Studio Projects/BESM6.vcproj b/Visual Studio Projects/BESM6.vcproj
index f0f8a767..bb49d3e2 100644
--- a/Visual Studio Projects/BESM6.vcproj
+++ b/Visual Studio Projects/BESM6.vcproj
@@ -215,6 +215,10 @@
RelativePath="..\BESM6\besm6_panel.c"
>
+
+
@@ -235,6 +239,10 @@
RelativePath="..\BESM6\besm6_tty.c"
>
+
+
diff --git a/descrip.mms b/descrip.mms
index 977814ba..b7c00d6f 100644
--- a/descrip.mms
+++ b/descrip.mms
@@ -762,7 +762,7 @@ BESM6_LIB = $(LIB_DIR)BESM6-$(ARCH).OLB
BESM6_SOURCE = $(BESM6_DIR)BESM6_CPU.C,$(BESM6_DIR)BESM6_SYS.C,$(BESM6_DIR)BESM6_MMU.C,\
$(BESM6_DIR)BESM6_ARITH.C,$(BESM6_DIR)BESM6_DISK.C,$(BESM6_DIR)BESM6_DRUM.C,\
$(BESM6_DIR)BESM6_TTY.C,$(BESM6_DIR)BESM6_PANEL.C,$(BESM6_DIR)BESM6_PRINTER.C,\
- $(BESM6_DIR)BESM6_PUNCHCARD.C,$(BESM6_DIR)BESM6_PUNCH.C
+ $(BESM6_DIR)BESM6_PUNCHCARD.C,$(BESM6_DIR)BESM6_PUNCH.C,$(BESM6_DIR)BESM6_PL.C,$(BESM6_DIR)BESM6_VU.C
BESM6_OPTIONS = /INCL=($(SIMH_DIR),$(BESM6_DIR))/DEF=($(CC_DEFS),"USE_INT64=1")
#
diff --git a/makefile b/makefile
index 676d7885..df0c0e3b 100644
--- a/makefile
+++ b/makefile
@@ -1935,7 +1935,8 @@ BESM6D = ${SIMHD}/BESM6
BESM6 = ${BESM6D}/besm6_cpu.c ${BESM6D}/besm6_sys.c ${BESM6D}/besm6_mmu.c \
${BESM6D}/besm6_arith.c ${BESM6D}/besm6_disk.c ${BESM6D}/besm6_drum.c \
${BESM6D}/besm6_tty.c ${BESM6D}/besm6_panel.c ${BESM6D}/besm6_printer.c \
- ${BESM6D}/besm6_punch.c ${BESM6D}/besm6_punchcard.c
+ ${BESM6D}/besm6_pl.c \
+ ${BESM6D}/besm6_punch.c ${BESM6D}/besm6_punchcard.c ${BESM6D}/besm6_vu.c
ifneq (,$(BESM6_BUILD))
BESM6_OPT = -I ${BESM6D} -DUSE_INT64 $(BESM6_PANEL_OPT)