From 5e539dc0b2b514b94c951d356e4ed32fba73ff82 Mon Sep 17 00:00:00 2001 From: Leo Broukhis Date: Sat, 2 Jan 2021 01:02:15 -0800 Subject: [PATCH] BESM6: Implemented punched card input and punched tape output --- BESM6/besm6_cpu.c | 21 +- BESM6/besm6_defs.h | 21 +- BESM6/besm6_pl.c | 143 +++++++ BESM6/besm6_punch.c | 4 +- BESM6/besm6_punchcard.c | 11 - BESM6/besm6_vu.c | 579 ++++++++++++++++++++++++++++ Visual Studio Projects/BESM6.vcproj | 8 + descrip.mms | 2 +- makefile | 3 +- 9 files changed, 767 insertions(+), 25 deletions(-) create mode 100644 BESM6/besm6_pl.c create mode 100644 BESM6/besm6_vu.c 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)