579 lines
18 KiB
C
579 lines
18 KiB
C
/*
|
||
* 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);
|
||
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((char *)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];
|
||
}
|