318 lines
10 KiB
C
318 lines
10 KiB
C
/* sds_cp.c - SDS-930 Card Punch
|
|
|
|
Copyright (c) 2020, Ken Rector
|
|
|
|
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
|
|
KEN RECTOR 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 Ken Rector shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Ken Rector.
|
|
|
|
03-Mar-20 kenr Initial Version
|
|
|
|
*/
|
|
|
|
/*
|
|
This card punch simulator uses sim_card.c to write output records
|
|
in CBN format. Data is passed directly to sim_card.c when binary
|
|
mode is specified by the buffer control EOM. When BCD mode is
|
|
specified by the EOM, output data is translated into Hollerith code
|
|
from SDS Internal Code as defined by the SDS 930 Computer Reference Manual
|
|
|
|
The SDS card punch protocol defined by the 930 Computer Reference manual
|
|
specifies that the output image be sent to the buffer 12 times, once
|
|
for each row. In this simulator the card image is only written after
|
|
termination (TOP) of the twelfth image output.
|
|
|
|
The Symbol assembler punch routine uses the PBT (Punch Buffer Test)
|
|
before issueing a connect EOM to determine if it needs to write 12 rows
|
|
per card, or just 1. To make Symbol work right we always return TRUE,
|
|
(skip) for this test.
|
|
|
|
I can't find anything in the computer reference manuals that describes
|
|
how this should work. Why did Symbol do this?
|
|
|
|
*/
|
|
|
|
|
|
#include "sds_defs.h"
|
|
#include "sim_card.h"
|
|
|
|
#define CARD_IN_PUNCH 00004000 /* Card ready to punch */
|
|
|
|
#define STATUS u3
|
|
|
|
extern uint32 xfr_req;
|
|
extern int32 stop_invins, stop_invdev, stop_inviop;
|
|
|
|
uint16 cp_buffer[80]; /* card output image */
|
|
int32 cp_bptr = 0; /* buf ptr */
|
|
int32 cp_blnt = 0; /* buf length */
|
|
int32 cp_row = 0; /* row counter */
|
|
int32 cp_chr = 0;
|
|
int32 cp_eor;
|
|
int32 cp_inst; /* saved instr */
|
|
|
|
t_stat cp_devio(uint32 fnc, uint32 inst, uint32 *dat);
|
|
t_stat cp_svc(UNIT *);
|
|
t_stat cp_attach(UNIT * uptr, CONST char *file);
|
|
t_stat cp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
t_stat cp_detach(UNIT * uptr);
|
|
t_stat cp_wrend(UNIT * uptr);
|
|
t_stat cp_show_cap (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
void cp_set_err (UNIT *uptr);
|
|
|
|
|
|
|
|
DSPT cp_tplt[] = { { 1, 0 }, { 0, 0 } }; /* template */
|
|
|
|
DIB cp_dib = { CHAN_W, DEV_CP, XFR_CP, cp_tplt, &cp_devio };
|
|
|
|
UNIT cp_unit = {UDATA(&cp_svc, UNIT_ATTABLE , 0), 2000 };
|
|
|
|
MTAB cp_mod[] = {
|
|
{MTAB_XTD | MTAB_VDV, 0, "CHANNEL", "CHANNEL",
|
|
&set_chan, &show_chan,
|
|
NULL, "Device Channel"},
|
|
{MTAB_XTD | MTAB_VDV, 0, "FORMAT", "FORMAT",
|
|
&sim_card_set_fmt, &sim_card_show_fmt,
|
|
NULL,"Card Format"},
|
|
{ MTAB_XTD|MTAB_VDV, 0, "CAPACITY", NULL,
|
|
NULL, &cp_show_cap, NULL, "Stacker Count" },
|
|
{0}
|
|
};
|
|
|
|
REG cp_reg[] = {
|
|
{ BRDATA (BUFF, cp_buffer, 16, 16, sizeof(cp_buffer)/sizeof(*cp_buffer)), REG_HRO},
|
|
{ DRDATA (BPTR, cp_bptr, 18), PV_LEFT },
|
|
{ DRDATA (BLNT, cp_blnt, 18), PV_LEFT },
|
|
{ FLDATA (XFR, xfr_req, XFR_V_CP) },
|
|
{ ORDATA (INST, cp_inst, 24) },
|
|
{ DRDATA (POS, cp_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE cp_dev = {
|
|
"CP", &cp_unit, cp_reg, cp_mod,
|
|
1, 8, 15, 1, 8, 8,
|
|
NULL, NULL, NULL, NULL, &cp_attach, &cp_detach,
|
|
&cp_dib, DEV_DISABLE | DEV_DEBUG | DEV_CARD, 0, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
/* Convert SDS BCD character into hollerith code */
|
|
|
|
uint16 sdsbcd_to_hol(uint8 bcd) {
|
|
uint16 hol;
|
|
|
|
/* Handle space correctly */
|
|
if (bcd == 0) /* 0 to row 10 */
|
|
return 0x200;
|
|
if (bcd == 060) /* 60 no punch */
|
|
return 0;
|
|
|
|
/* Convert to top column */
|
|
switch (bcd & 060) {
|
|
default:
|
|
case 000:
|
|
hol = 0x000; /* no zone */
|
|
break;
|
|
case 020:
|
|
hol = 0x800; /* 0x200 row 12 */
|
|
break;
|
|
case 040:
|
|
hol = 0x400; /* row 11 */
|
|
break;
|
|
case 060:
|
|
hol = 0x200; /* row 10 */
|
|
break;
|
|
}
|
|
|
|
/* Convert to 0-9 row */
|
|
bcd &= 017;
|
|
if (bcd > 9) {
|
|
hol |= 0x2; /* row 8 */
|
|
bcd -= 8;
|
|
}
|
|
if (bcd != 0)
|
|
hol |= 1 << (9 - bcd);
|
|
return hol;
|
|
}
|
|
|
|
t_stat cp_devio(uint32 fnc, uint32 inst, uint32 *dat) {
|
|
UNIT *uptr = &cp_unit;
|
|
int32 new_ch;
|
|
uint8 chr;
|
|
t_stat r;
|
|
uint32 t;
|
|
|
|
switch (fnc) {
|
|
case IO_CONN:
|
|
new_ch = I_GETEOCH (inst); /* get new chan */
|
|
if (new_ch != cp_dib.chan) /* wrong chan? err */
|
|
return SCPE_IERR;
|
|
if (sim_is_active(uptr))
|
|
CRETIOP;
|
|
if (uptr->flags & UNIT_ATT) {
|
|
cp_inst = inst;
|
|
cp_blnt = 0;
|
|
cp_bptr = 0;
|
|
xfr_req = xfr_req & ~XFR_CP; /* clr xfr flag */
|
|
sim_activate (uptr, uptr->wait); /* start timer */
|
|
}
|
|
else {
|
|
cp_set_err (uptr); /* no, err, disc */
|
|
CRETIOP;
|
|
}
|
|
break;
|
|
case IO_EOM1: /* I/O Control EOM */
|
|
break;
|
|
case IO_DISC: /* disconnect TOP */
|
|
xfr_req = xfr_req & ~XFR_CP; /* clr xfr flag */
|
|
cp_row++;
|
|
if (cp_row >= 12) {
|
|
if ((r = cp_wrend(uptr)) != SCPE_OK)
|
|
return r;
|
|
uptr->STATUS &= ~CARD_IN_PUNCH;
|
|
}
|
|
sim_cancel (uptr); /* deactivate unit */
|
|
break;
|
|
case IO_WREOR: /* write eor */
|
|
break;
|
|
case IO_SKS:
|
|
new_ch = I_GETSKCH (inst); /* get chan # */
|
|
if (new_ch != cp_dib.chan) /* wrong chan? */
|
|
return SCPE_IERR;
|
|
t = I_GETSKCND (inst); /* get skip cond */
|
|
switch (t) { /* case sks cond */
|
|
case 010: /* sks 12046 */
|
|
// PBT
|
|
// /* skip if punch buffer empty */
|
|
*dat = 1;
|
|
break;
|
|
case 020: /* sks 14046 */
|
|
// CPT
|
|
/* skip if punch is ready to accept connection */
|
|
if ((uptr->flags & UNIT_ATT) &&
|
|
!(uptr->STATUS & CARD_IN_PUNCH))
|
|
*dat = 1;
|
|
break;
|
|
}
|
|
break;
|
|
case IO_WRITE:
|
|
if (!(uptr->STATUS & CARD_IN_PUNCH))
|
|
break;
|
|
chr = (*dat) & 077;
|
|
xfr_req = xfr_req & ~XFR_CP; /* clr xfr flag */
|
|
if (cp_bptr < cp_blnt) {
|
|
if (cp_inst & 01000) {
|
|
if (cp_chr & 1) /* column binary */
|
|
cp_buffer[cp_bptr++] |= chr;
|
|
else
|
|
cp_buffer[cp_bptr] = (chr << 6);
|
|
cp_chr++;
|
|
}
|
|
else {
|
|
cp_buffer[cp_bptr++] = sdsbcd_to_hol(chr); /* bcd */
|
|
}
|
|
chan_set_ordy (cp_dib.chan);
|
|
}
|
|
break;
|
|
case IO_READ:
|
|
CRETINS;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* punch service */
|
|
t_stat cp_svc(UNIT *uptr) {
|
|
|
|
uptr->STATUS |= CARD_IN_PUNCH;
|
|
cp_bptr = 0;
|
|
cp_blnt = 80;
|
|
cp_chr = 0;
|
|
chan_set_ordy (cp_dib.chan);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cp_wrend(UNIT * uptr) {
|
|
t_stat st;
|
|
|
|
st = sim_punch_card(uptr, cp_buffer);
|
|
cp_row = 0;
|
|
if (st != CDSE_OK) {
|
|
cp_set_err(uptr);
|
|
return SCPE_IOERR;
|
|
}
|
|
uptr->STATUS = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Fatal error */
|
|
void cp_set_err (UNIT *uptr)
|
|
{
|
|
chan_set_flag (cp_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */
|
|
chan_disc (cp_dib.chan); /* disconnect */
|
|
xfr_req = xfr_req & ~XFR_CP; /* clear xfr */
|
|
sim_cancel (uptr); /* stop */
|
|
cp_bptr = 0; /* buf empty */
|
|
return;
|
|
}
|
|
|
|
t_stat cp_attach(UNIT * uptr, CONST char *cptr) {
|
|
t_stat r;
|
|
|
|
sim_card_set_fmt (uptr,0,"CBN",NULL);
|
|
if ((r = sim_card_attach(uptr, cptr)) != SCPE_OK)
|
|
return r;
|
|
cp_row = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat cp_detach(UNIT * uptr) {
|
|
|
|
if (uptr->STATUS & CARD_IN_PUNCH)
|
|
sim_punch_card(uptr, cp_buffer);
|
|
return sim_card_detach(uptr);
|
|
}
|
|
|
|
/* Channel assignment routines */
|
|
|
|
t_stat cp_set_chan (UNIT *uptr, int32 val, CONST char *sptr, void *desc)
|
|
{
|
|
t_stat r;
|
|
r = set_chan (uptr, val, sptr, desc);
|
|
return r;
|
|
}
|
|
|
|
t_stat cp_show_cap (FILE *st, UNIT *uptr, int32 val, CONST void *desc) {
|
|
int n;
|
|
|
|
if ((n = sim_card_output_hopper_count(uptr)) == 0)
|
|
fprintf(st,"stacker empty");
|
|
else {
|
|
if (n == 1)
|
|
fprintf(st,"1 card");
|
|
else
|
|
fprintf(st,"%d cards",n);
|
|
fprintf(st," in stacker");
|
|
}
|
|
return SCPE_OK;
|
|
}
|