From f46c048bb696216c36bdefd6d7d146320c4136dd Mon Sep 17 00:00:00 2001 From: Leo Broukhis Date: Mon, 6 Feb 2017 23:04:40 -0800 Subject: [PATCH] BESM6: Implemented punchcard output. --- BESM6/besm6_cpu.c | 27 +- BESM6/besm6_defs.h | 14 + BESM6/besm6_punch.c | 3 +- BESM6/besm6_punchcard.c | 505 ++++++++++++++++++++++++++++ Visual Studio Projects/BESM6.vcproj | 4 + descrip.mms | 2 +- makefile | 2 +- 7 files changed, 541 insertions(+), 16 deletions(-) create mode 100644 BESM6/besm6_punchcard.c diff --git a/BESM6/besm6_cpu.c b/BESM6/besm6_cpu.c index 4b238eae..a4a82b1d 100644 --- a/BESM6/besm6_cpu.c +++ b/BESM6/besm6_cpu.c @@ -76,11 +76,7 @@ extern const char *scp_errors[]; 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 ) @@ -285,6 +281,7 @@ DEVICE *sim_devices[] = { &clock_dev, &printer_dev, &fs_dev, + &pi_dev, &tty_dev, /* терминалы - телетайпы, видеотоны, "Консулы" */ 0 }; @@ -651,12 +648,16 @@ static void cmd_033 () /* besm6_debug(">>> гашение АС: %08o", (uint32) ACC & BITS(24));*/ break; case 0154: case 0155: - /* TODO: управление выводом на перфокарты */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); + /* + * Punchcard output: the two selected bits control the motor + * and the culling mechanism. + */ + pi_control (Aex & 1, (uint32) ACC & 011); break; - case 0160: case 0167: - /* TODO: управление электромагнитами пробивки перфокарт */ - longjmp (cpu_halt, STOP_UNIMPLEMENTED); + case 0160: case 0161: case 0162: case 0163: + case 0164: case 0165: case 0166: case 0167: + /* Punchcard output: activating the punching solenoids, 20 at a time. */ + pi_write (Aex & 7, (uint32) ACC & BITS(20)); break; case 0170: case 0171: /* TODO: пробивка строки на перфоленте */ @@ -742,6 +743,11 @@ static void cmd_033 () * группами по 8 штук каждые несколько секунд. */ ACC = 0; 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 */ + ACC = pi_read (Aex & 7); + break; case 04170: case 04171: case 04172: case 04173: /* TODO: считывание контрольного кода * строки перфоленты */ @@ -764,9 +770,6 @@ static void cmd_033 () } 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)*/ diff --git a/BESM6/besm6_defs.h b/BESM6/besm6_defs.h index 6daa5753..84b14c7e 100644 --- a/BESM6/besm6_defs.h +++ b/BESM6/besm6_defs.h @@ -146,6 +146,7 @@ extern DEVICE clock_dev; extern DEVICE printer_dev; extern DEVICE tty_dev; extern DEVICE fs_dev; +extern DEVICE pi_dev; extern jmp_buf cpu_halt; /* @@ -310,6 +311,12 @@ extern void mmu_setup (void); extern void mmu_setprotection (int idx, t_value word); extern void mmu_print_brz (void); +/* + * Utility functions + */ +extern void gost_putc(unsigned char, FILE *); +extern int odd_parity(unsigned char); + /* * Выполнение обращения к барабану. */ @@ -348,6 +355,13 @@ int vt_is_idle (void); void fs_control (int num, uint32 cmd); int fs_read (int num); +/* + * Вывод на перфокарты. + */ +void pi_control (int num, uint32 cmd); +void pi_write (int num, uint32 cmd); +int pi_read (int num); + /* * Отладочная выдача. */ diff --git a/BESM6/besm6_punch.c b/BESM6/besm6_punch.c index 3f3440a6..625a4d90 100644 --- a/BESM6/besm6_punch.c +++ b/BESM6/besm6_punch.c @@ -1,5 +1,5 @@ /* - * besm6_punch.c: BESM-6 punchcard/punchtape devices + * besm6_punch.c: BESM-6 punchtape devices * * Copyright (c) 2009, Leonid Broukhis * @@ -29,7 +29,6 @@ #include "besm6_defs.h" t_stat fs_event (UNIT *u); -t_stat uvvk_event (UNIT *u); UNIT fs_unit [] = { { UDATA (fs_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, diff --git a/BESM6/besm6_punchcard.c b/BESM6/besm6_punchcard.c new file mode 100644 index 00000000..bae4bc19 --- /dev/null +++ b/BESM6/besm6_punchcard.c @@ -0,0 +1,505 @@ +/* + * besm6_punchcard.c: BESM-6 punchcard devices + * + * Copyright (c) 2017, 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" + +#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) }, + { UDATA (pi_event, UNIT_SEQ+UNIT_ATTABLE, 0) }, +}; + +/* + * Each line has 3 phases: + * - striking: the "PUNCH" interrupt line goes high, + * puncher solenoids can be activated + * - moving: solenoids are turned off, the "PUNCH" interrupt line goes low + * - checking: the "CHECK" interrupt goes high, + * querying of the check brushes can be done. + */ +typedef enum { + PI_STRIKE, + PI_MOVE, + PI_CHECK, + PI_LAST = PI_STRIKE + 3*12 - 1, + PI_PAUSE, + PI_IDLE +} pi_state_t; + +typedef struct { + /* + * A 3-card long tract, with 12 lines per card, + * represented as 4 20-bit registers each. + */ + uint32 image[3][12][4]; + int cur; /* FIFO position */ + int running; /* continue with the next card */ + pi_state_t state; + void (*punch_fn)(int, int); +} pi_t; + +/* + * There are 2 card punchers */ +pi_t PI[2]; + +#define PI1_READY (1<<15) +#define PI2_READY (1<<13) + +#define PI1_START (1<<14) +#define PI2_START (1<<12) + +// #define NEGATIVE_RDY + +#ifndef NEGATIVE_RDY +# define ENB_RDY2 SET_RDY2 +# define DIS_RDY2 CLR_RDY2 +# define IS_RDY2 ISSET_RDY2 +#else +# define ENB_RDY2 CLR_RDY2 +# define DIS_RDY2 SET_RDY2 +# define IS_RDY2 ISCLR_RDY2 +#endif + +#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) + +/* + * Per one line of a punched card. + */ +#define PI_RATE (20*MSEC) + +const uint32 pi_punch_mask[2] = { PRP_PCARD1_PUNCH, PRP_PCARD2_PUNCH }; +const uint32 pi_check_mask[2] = { PRP_PCARD1_CHECK, PRP_PCARD2_CHECK }; +const uint32 pi_ready_mask[2] = { PI1_READY, PI2_READY }; +const uint32 pi_start_mask[2] = { PI1_START, PI2_START }; + +REG pi_reg[] = { + { REGDATA ( "READY", READY2, 2, 4, 12, 1, NULL, NULL, 0, 0, 0) }, + { 0 } +}; + +MTAB pi_mod[] = { + { 0 } +}; + +t_stat pi_reset (DEVICE *dptr); +t_stat pi_attach (UNIT *uptr, CONST char *cptr); +t_stat pi_detach (UNIT *uptr); + +DEVICE pi_dev = { + "PI", pi_unit, pi_reg, pi_mod, + 2, 8, 19, 1, 8, 50, + NULL, NULL, &pi_reset, NULL, &pi_attach, &pi_detach, + NULL, DEV_DISABLE | DEV_DEBUG +}; + + +/* + * Outputs 12 lines of 80 characters plus an empty line. + */ +static void pi_punch_dots(int unit, int card) { + UNIT *u = &pi_unit[unit]; + FILE * f = u->fileref; + int l, p, c; + for (l = 0; l < 12; ++l) { + for (p = 0; p < 4; ++p) + for (c = 19; c >= 0; --c) + putc((PI[unit].image[card][l][p] >> c) & 1 ? 'O' : '.', f); + putc('\n', f); + } + putc('\n', f); +} + +static void pi_to_bytes(int unit, int card, unsigned char buf[120]) { + int byte = 0; + int cnt = 0; + int l, p, c; + for (l = 0; l < 12; ++l) { + for (p = 0; p < 4; ++p) { + for (c = 19; c >= 0; --c) { + int bit = (PI[unit].image[card][l][p] >> c) & 1 ? 1 : 0; + buf[byte] <<= 1; + buf[byte] |= bit; + if (++cnt == 8) { + cnt = 0; + ++byte; + } + } + } + } +} + +/* + * Outputs 120 bytes, read linewise. + */ +static void pi_punch_binary(int unit, int card) { + UNIT *u = &pi_unit[unit]; + FILE * f = u->fileref; + unsigned char buf[120]; + pi_to_bytes(unit, card, buf); + fwrite(buf, 120, 1, f); +} + +/* + * Outputs a visual representation of the card + * using 3 lines of 40 Braille patterns, plus an empty line. + */ +static void pi_punch_visual(int unit, int card) { + UNIT *u = &pi_unit[unit]; + FILE * f = u->fileref; + // Print 3 lines of 40 Braille characters per line representing a punchcard. + unsigned char bytes[3][40]; + int line, col, p, c; + memset(bytes, 0, 120); + for (line = 0; line < 12; ++line) { + for (p = 0; p < 4; ++p) + for (c = 19; c >= 0; --c) { + int bit = (PI[unit].image[card][line][p] >> c) & 1 ? 1 : 0; + int col = p*20 + 19-c; + if (bit) { + /* + * Braille Unicode codepoints are U+2800 plus + * an 8-bit mask of punches according to the map + * 0 3 + * 1 4 + * 2 5 + * 6 7 + */ + bytes[line/4][col/2] |= + "\x01\x08\x02\x10\x04\x20\x40\x80"[line%4*2+col%2]; + } + } + } + for (line = 0; line < 3; ++line) { + for (col = 0; col < 40; ++col) { + fprintf(f, "\342%c%c", + 0240+(bytes[line][col] >> 6), + 0200 + (bytes[line][col] & 077)); + } + putc('\n', f); + } + putc('\n', f); +} + +/* + * Attempts to interpret a card as GOST-10859 with odd parity; + * if fails, dumps visual. + */ +static void pi_punch_gost(int unit, int card) { + UNIT *u = &pi_unit[unit]; + FILE * f = u->fileref; + unsigned char buf[120]; + int len; + int cur; + int zero_expected = 0; + pi_to_bytes(unit, card, buf); + /* + * Bytes in the buffer must have odd parity, with the exception + * of optional zero bytes at the end of lines and at the end of a card. + * Trailing zeros are trimmed, intermediate zeros become blanks. + * The first character in each line must be valid. + */ + for (len = 120; len && !buf[len-1]; --len); + for (cur = 0; cur < len; ++cur) { + if (cur % 10 == 0) { + /* A new line */ + zero_expected = 0; + } + if (zero_expected) { + if (buf[cur]) + break; + } else if (!buf[cur]) { + if (cur % 10 == 0) { + /* The first char in a line is zero */ + break; + } + zero_expected = 1; + } else if (!odd_parity(buf[cur]) || (buf[cur] & 0177) >= 0140) { + break; + } + } + if (cur != len) { + /* Bad parity or invalid codepoint detected */ + pi_punch_visual(unit, card); + } else { + for (cur = 0; cur < len; ++cur) { + if (buf[cur]) { + gost_putc(buf[cur] & 0177, f); + } else { + putc(' ', f); + } + } + putc('\n', f); + } +} + +/* + * Dumps the last card in the FIFO and advances the FIFO pointer + * (this is equivalent to advancing the FIFO pointer and dumping + * the "current" card). + */ +static void pi_output (int unit, int cull) { + int card; + + PI[unit].cur = card = (PI[unit].cur + 1) % 3; + + if (cull) { + besm6_debug("<<< PI-%d: Culling bad card", unit); + } else { + (*PI[unit].punch_fn)(unit, card); + } + pi_unit[unit].pos = ftell(pi_unit[unit].fileref); + memset(PI[unit].image[card], 0, sizeof(PI[unit].image[card])); +} + +/* + * Reset routine + */ +t_stat pi_reset (DEVICE *dptr) +{ + sim_cancel (&pi_unit[0]); + sim_cancel (&pi_unit[1]); + PI[0].state = PI[1].state = PI_IDLE; + DIS_RDY2(PI1_READY | PI2_READY); + if (pi_unit[0].flags & UNIT_ATT) + ENB_RDY2(PI1_READY|PI1_START); + if (pi_unit[1].flags & UNIT_ATT) + ENB_RDY2(PI2_READY|PI2_START); + return SCPE_OK; +} + +/* + * Punching mode switches: + * -b - raw binary, line-wise, 120 bytes per p/c; + * -v - a visual form using Unicode Braille patterns; + * -d - a visual form using dots and Os; + * -g or -u - attempts to interpret the card as GOST/UPP text. + * The default is -v. + */ +t_stat pi_attach (UNIT *u, CONST char *cptr) +{ + t_stat s; + int unit = u - pi_unit; + PI[unit].punch_fn = NULL; + while (sim_switches & + (SWMASK('B')|SWMASK('V')|SWMASK('D')|SWMASK('G')|SWMASK('U'))) { + if (PI[unit].punch_fn) { + return SCPE_ARG; + } + if (sim_switches & SWMASK('B')) { + PI[unit].punch_fn = pi_punch_binary; + sim_switches &= ~SWMASK('B'); + } else if (sim_switches & SWMASK('V')) { + PI[unit].punch_fn = pi_punch_visual; + sim_switches &= ~SWMASK('V'); + } else if (sim_switches & SWMASK('D')) { + PI[unit].punch_fn = pi_punch_dots; + sim_switches &= ~SWMASK('D'); + } else if (sim_switches & SWMASK('G')) { + PI[unit].punch_fn = pi_punch_gost; + sim_switches &= ~SWMASK('G'); + } else if (sim_switches & SWMASK('U')) { + PI[unit].punch_fn = pi_punch_gost; + sim_switches &= ~SWMASK('U'); + } + } + if (PI[unit].punch_fn == NULL) { + PI[unit].punch_fn = pi_punch_visual; + } + s = attach_unit (u, cptr); + if (s != SCPE_OK) + return s; + ENB_RDY2(pi_ready_mask[unit]); + return SCPE_OK; +} + +t_stat pi_detach (UNIT *u) +{ + int unit = u - pi_unit; + DIS_RDY2(pi_ready_mask[unit]); + return detach_unit (u); +} + +void pi_control (int num, uint32 cmd) +{ + UNIT *u = &pi_unit[num]; + if (pi_dev.dctrl) + besm6_debug("<< PI_IDLE) { + /* Starting a new card */ + PI[unit].state = PI_STRIKE; + } + switch (PI[unit].state) { + case PI_LAST: + /* + * At the last check interrupt, + * the "permission to start" flag is cleared. + */ + DIS_RDY2(pi_start_mask[unit]); + break; + case PI_PAUSE: + /* + * The permission to start is re-enabled. + */ + ENB_RDY2(pi_start_mask[unit]); + PI[unit].state = PI_IDLE; + if (PI[unit].running) { + if (pi_dev.dctrl) + besm6_debug ("<<< PI-%d re-enabled", unit); + sim_activate(u, PI_RATE); + PI[unit].running = 0; + } else { + /* + * The unit is going idle without an explicit "stop" command: + * The last card (the separator) falls into the "good" bin. + */ + pi_output(unit, 0); + } + break; + default: + break; + } + if (pi_dev.dctrl) + besm6_debug ("<<< PI-%d event, state %d", unit, PI[unit].state); + if (PI[unit].state <= PI_LAST) { + switch (PI[unit].state % 3) { + case PI_STRIKE: + /* Punch interrupt */ + PRP |= pi_punch_mask[unit]; + sim_activate(u, PI_RATE/3); + break; + case PI_MOVE: + /* Punchers off */ + PRP &= ~pi_punch_mask[unit]; + sim_activate(u, 2*PI_RATE/3); + break; + case PI_CHECK: + /* Check interrupt */ + PRP |= pi_check_mask[unit]; + sim_activate(u, PI_RATE); + } + } + return SCPE_OK; +} + +/* + * Writing to the register punches the current card. + */ +void pi_write (int num, uint32 val) +{ + int unit = num >> 2; + int card = PI[unit].cur; + int pos = (num & 3) ^ 3; + int line = PI[unit].state / 3; + if (line > 11 || PI[unit].state % 3 != PI_STRIKE) { + besm6_debug("<<< PI-%d, writing out of turn, useless", num); + return; + } + if (pi_dev.dctrl) { + besm6_debug("Card %d line %d pos %d <- val %05x", + card, line, pos, val); + } + PI[unit].image[card][line][pos] = val; +} + +/* + * Reading from the register reads the previous card + * and returns the inverted value. + */ +int pi_read (int num) +{ + int unit = num >> 2; + int pos = (num & 3) ^ 3; + int line = PI[unit].state / 3; + int card = (PI[unit].cur + 2) % 3; + if (line > 11 || PI[unit].state % 3 != PI_CHECK) { + /* Reading out of turn */ + return 0xFFFFF; + } else { + if (pi_dev.dctrl) { + besm6_debug("Card %d line %d pos %d -> val %05x", + card, line, pos, PI[unit].image[card][line][pos]); + } + return PI[unit].image[card][line][pos] ^ 0xFFFFF; + } +} + diff --git a/Visual Studio Projects/BESM6.vcproj b/Visual Studio Projects/BESM6.vcproj index c984f8a9..e457e16d 100644 --- a/Visual Studio Projects/BESM6.vcproj +++ b/Visual Studio Projects/BESM6.vcproj @@ -220,6 +220,10 @@ RelativePath="..\BESM6\besm6_punch.c" > + + diff --git a/descrip.mms b/descrip.mms index c42bbf4c..6c2c51a5 100644 --- a/descrip.mms +++ b/descrip.mms @@ -664,7 +664,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_PUNCH.C + $(BESM6_DIR)BESM6_PUNCHCARD.C,$(BESM6_DIR)BESM6_PUNCH.C BESM6_OPTIONS = /INCL=($(SIMH_DIR),$(BESM6_DIR))/DEF=($(CC_DEFS),"USE_INT64=1") # diff --git a/makefile b/makefile index e233e877..b25b00a6 100644 --- a/makefile +++ b/makefile @@ -1438,7 +1438,7 @@ BESM6D = 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_punch.c ${BESM6D}/besm6_punchcard.c ifneq (,$(BESM6_BUILD)) ifneq (,$(and ${VIDEO_LDFLAGS}, $(or $(and $(call find_include,SDL2/SDL_ttf),$(call find_lib,SDL2_ttf)), $(and $(call find_include,SDL/SDL_ttf),$(call find_lib,SDL_ttf)))))