506 lines
19 KiB
C
506 lines
19 KiB
C
/* i7094_cd.c: IBM 711/721 card reader/punch
|
|
|
|
Copyright (c) 2003-2021, Robert M. Supnik
|
|
|
|
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
|
|
ROBERT M SUPNIK 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 Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
cdr 711 card reader
|
|
cdp 721 card punch
|
|
|
|
09-Jun-21 RMS Removed use of ftell on output for pipe compatibility
|
|
13-Mar-17 RMS Annotated fall through in switch
|
|
19-Mar-12 RMS Fixed declaration of sim_switches (Mark Pizzolato)
|
|
19-Jan-07 RMS Added UNIT_TEXT
|
|
13-Jul-06 RMS Fixed problem with 80 column full cards
|
|
|
|
Cards are represented as ASCII text streams terminated by newlines.
|
|
This allows cards to be created and edited as normal files. Two
|
|
formats are supported:
|
|
|
|
column binary each character represents 6b of a 12b column
|
|
text each character represents all 12b of a column
|
|
|
|
Internally, the 7094 works only with column binary and is limited
|
|
to 72 columns of data. Each row of the card is represented by 72b
|
|
of data (two 36b words). A complete card image consists of 12 rows
|
|
(24 36b words).
|
|
*/
|
|
|
|
#include "i7094_defs.h"
|
|
|
|
#define CD_BINLNT 24 /* bin buf length */
|
|
#define CD_CHRLNT 80 /* char buf length */
|
|
|
|
#define CDS_INIT 0 /* card in motion */
|
|
#define CDS_DATA 1 /* data transfer */
|
|
#define CDS_END 2 /* card complete */
|
|
|
|
#define UNIT_V_CBN (UNIT_V_UF + 0) /* column binary file */
|
|
#define UNIT_V_PCA (UNIT_V_UF + 1) /* A vs H punch flag */
|
|
#define UNIT_CBN (1 << UNIT_V_CBN)
|
|
#define UNIT_PCA (1 << UNIT_V_PCA)
|
|
|
|
uint32 cdr_sta = 0; /* state */
|
|
uint32 cdr_bptr = 0; /* buffer ptr */
|
|
uint32 cdr_tstart = 27500; /* timing */
|
|
uint32 cdr_tstop = 27500;
|
|
uint32 cdr_tleft = 150;
|
|
uint32 cdr_tright = 4000;
|
|
t_uint64 cdr_bbuf[CD_BINLNT]; /* col binary buf */
|
|
|
|
uint32 cdp_sta = 0; /* state */
|
|
uint32 cdp_bptr = 0; /* buffer ptr */
|
|
uint32 cdp_tstart = 35000; /* timing */
|
|
uint32 cdp_tstop = 35000;
|
|
uint32 cdp_tleft = 150;
|
|
uint32 cdp_tright = 15500;
|
|
t_uint64 cdp_chob = 0;
|
|
uint32 cdp_chob_v = 0;
|
|
t_uint64 cdp_bbuf[CD_BINLNT]; /* col binary buf */
|
|
|
|
t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit);
|
|
t_stat cdr_reset (DEVICE *dptr);
|
|
t_stat cdr_svc (UNIT *uptr);
|
|
t_stat cdr_boot (int32 unitno, DEVICE *dptr);
|
|
t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit);
|
|
t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 flags);
|
|
t_stat cdp_reset (DEVICE *dptr);
|
|
t_stat cdp_svc (UNIT *uptr);
|
|
t_stat cdp_card_end (UNIT *uptr);
|
|
t_stat cd_attach (UNIT *uptr, CONST char *cptr);
|
|
t_stat cd_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
char colbin_to_bcd (uint32 cb);
|
|
|
|
|
|
/* Card reader data structures
|
|
|
|
cdr_dev CDR descriptor
|
|
cdr_unit CDR unit descriptor
|
|
cdr_reg CDR register list
|
|
*/
|
|
|
|
DIB cdr_dib = { &cdr_chsel, NULL };
|
|
|
|
UNIT cdr_unit = {
|
|
UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0)
|
|
};
|
|
|
|
REG cdr_reg[] = {
|
|
{ ORDATA (STATE, cdr_sta, 2) },
|
|
{ DRDATA (BPTR, cdr_bptr, 5), PV_LEFT },
|
|
{ BRDATA (BUF, cdr_bbuf, 8, 36, CD_BINLNT) },
|
|
{ DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ DRDATA (TSTART, cdr_tstart, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TSTOP, cdr_tstop, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TLEFT, cdr_tleft, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TRIGHT, cdr_tright, 24), PV_LEFT + REG_NZ },
|
|
{ NULL } };
|
|
|
|
MTAB cdr_mod[] = {
|
|
{ UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode },
|
|
{ UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE cdr_dev = {
|
|
"CDR", &cdr_unit, cdr_reg, cdr_mod,
|
|
1, 10, 31, 1, 8, 7,
|
|
NULL, NULL, &cdr_reset,
|
|
&cdr_boot, &cd_attach, NULL,
|
|
&cdr_dib, DEV_DISABLE
|
|
};
|
|
|
|
/* CDP data structures
|
|
|
|
cdp_dev CDP device descriptor
|
|
cdp_unit CDP unit descriptor
|
|
cdp_reg CDP register list
|
|
*/
|
|
|
|
DIB cdp_dib = { &cdp_chsel, &cdp_chwr };
|
|
|
|
UNIT cdp_unit = {
|
|
UDATA (&cdp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0)
|
|
};
|
|
|
|
REG cdp_reg[] = {
|
|
{ ORDATA (STATE, cdp_sta, 2) },
|
|
{ ORDATA (CHOB, cdp_chob, 36) },
|
|
{ FLDATA (CHOBV, cdp_chob_v, 0) },
|
|
{ DRDATA (BPTR, cdp_bptr, 5), PV_LEFT },
|
|
{ BRDATA (BUF, cdp_bbuf, 8, 36, CD_BINLNT) },
|
|
{ DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT },
|
|
{ DRDATA (TSTART, cdp_tstart, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TSTOP, cdp_tstop, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TLEFT, cdp_tleft, 24), PV_LEFT + REG_NZ },
|
|
{ DRDATA (TRIGHT, cdp_tright, 24), PV_LEFT + REG_NZ },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB cdp_mod[] = {
|
|
{ UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode },
|
|
{ UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode },
|
|
{ UNIT_PCA, UNIT_PCA, "business set", "BUSINESS", NULL },
|
|
{ UNIT_PCA, 0, "Fortran set", "FORTRAN", NULL },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE cdp_dev = {
|
|
"CDP", &cdp_unit, cdp_reg, cdp_mod,
|
|
1, 10, 31, 1, 8, 7,
|
|
NULL, NULL, &cdp_reset,
|
|
NULL, &cd_attach, NULL,
|
|
&cdp_dib, DEV_DISABLE
|
|
};
|
|
|
|
/* Card reader select */
|
|
|
|
t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit)
|
|
{
|
|
if (sel & CHSL_NDS) /* nds? nop */
|
|
return ch6_end_nds (ch);
|
|
|
|
switch (sel) { /* case on data sel */
|
|
|
|
case CHSL_RDS: /* read */
|
|
if ((cdr_unit.flags & UNIT_ATT) == 0) /* not attached? */
|
|
return SCPE_UNATT;
|
|
if (sim_is_active (&cdr_unit)) /* busy? */
|
|
return ERR_STALL;
|
|
cdr_sta = CDS_INIT; /* initial state */
|
|
sim_activate (&cdr_unit, cdp_tstart); /* start reader */
|
|
break;
|
|
|
|
default: /* other */
|
|
return STOP_ILLIOP; /* not allowed */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit timeout */
|
|
|
|
t_stat cdr_svc (UNIT *uptr)
|
|
{
|
|
uint32 i, col, row, bufw, colbin;
|
|
char cdr_cbuf[(2 * CD_CHRLNT) + 2 + 1];
|
|
t_uint64 dat = 0;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
return SCPE_UNATT;
|
|
switch (cdr_sta) { /* case on state */
|
|
|
|
case CDS_INIT: /* initial state */
|
|
for (i = 0; i < CD_BINLNT; i++) /* clear bin buf */
|
|
cdr_bbuf[i] = 0;
|
|
for (i = 0; i < ((2 * CD_CHRLNT) + 2); i++) /* clear char buf */
|
|
cdr_cbuf[i] = ' ';
|
|
cdr_sta = CDS_DATA; /* data state */
|
|
cdr_bptr = 0; /* init buf ptr */
|
|
if (fgets (cdr_cbuf, (uptr->flags & UNIT_CBN)? (2 * CD_CHRLNT) + 2: CD_CHRLNT + 2,
|
|
uptr->fileref)) {}; /* read card */
|
|
if (feof (uptr->fileref)) /* eof? */
|
|
return ch6_err_disc (CH_A, U_CDR, CHF_EOF); /* set EOF, disc */
|
|
if (ferror (uptr->fileref)) { /* error? */
|
|
sim_perror ("CDR I/O error");
|
|
clearerr (uptr->fileref);
|
|
return SCPE_IOERR; /* stop */
|
|
}
|
|
uptr->pos = ftell (uptr->fileref); /* update position */
|
|
for (i = 0; i < (2 * CD_CHRLNT); i++) /* convert to BCD */
|
|
cdr_cbuf[i] = ascii_to_bcd[cdr_cbuf[i] & 0177] & 077;
|
|
for (col = 0; col < 72; col++) { /* process 72 columns */
|
|
if (uptr->flags & UNIT_CBN) /* column binary? */
|
|
colbin = (((uint32) cdr_cbuf[2 * col]) << 6) |
|
|
((uint32) cdr_cbuf[(2 * col) + 1]); /* 2 chars -> col bin */
|
|
else colbin = bcd_to_colbin[cdr_cbuf[col] & 077]; /* cvt to col binary */
|
|
dat = bit_masks[35 - (col % 36)]; /* mask for column */
|
|
for (row = 0; row < 12; row++) { /* rows 9..0, 11, 12 */
|
|
bufw = (row * 2) + (col / 36); /* index to buffer */
|
|
if (colbin & col_masks[row]) /* row bit set? */
|
|
cdr_bbuf[bufw] |= dat;
|
|
}
|
|
}
|
|
/* fall through */
|
|
case CDS_DATA: /* data state */
|
|
dat = cdr_bbuf[cdr_bptr++]; /* get next word */
|
|
if (cdr_bptr >= CD_BINLNT) { /* last word? */
|
|
cdr_sta = CDS_END; /* end state */
|
|
ch6_req_rd (CH_A, U_CDR, dat, CH6DF_EOR); /* req chan, dat, EOR */
|
|
sim_activate (uptr, cdr_tstop);
|
|
}
|
|
else {
|
|
ch6_req_rd (CH_A, U_CDR, dat, 0); /* req chan, dat */
|
|
sim_activate (uptr, (cdr_bptr & 1)? cdr_tleft: cdr_tright);
|
|
}
|
|
break;
|
|
|
|
case CDS_END: /* end state */
|
|
if (ch6_qconn (CH_A, U_CDR)) { /* if cdr still conn */
|
|
cdr_sta = CDS_INIT; /* return to init */
|
|
sim_activate (uptr, 1); /* next card */
|
|
}
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Card reader reset */
|
|
|
|
t_stat cdr_reset (DEVICE *dptr)
|
|
{
|
|
uint32 i;
|
|
|
|
for (i = 0; i < CD_BINLNT; i++) /* clear buffer */
|
|
cdr_bbuf[i] = 0;
|
|
cdr_sta = 0; /* clear state */
|
|
cdr_bptr = 0; /* clear buf ptr */
|
|
sim_cancel (&cdr_unit); /* stop reader */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Card reader bootstrap */
|
|
|
|
#define BOOT_START 01000
|
|
#define BOOT_SIZE (sizeof (boot_rom) / sizeof (t_uint64))
|
|
|
|
static const t_uint64 boot_rom[] = {
|
|
INT64_C(00762000001000) + U_CDR, /* RDSA CDR */
|
|
INT64_C(00544000000000) + BOOT_START + 4, /* LCHA *+3 */
|
|
INT64_C(00544000000000), /* LCHA 0 */
|
|
INT64_C(00021000000001), /* TTR 1 */
|
|
INT64_C(05000030000000), /* IOCT 3,,0 */
|
|
};
|
|
|
|
t_stat cdr_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
uint32 i;
|
|
extern t_uint64 *M;
|
|
|
|
for (i = 0; i < BOOT_SIZE; i++)
|
|
WriteP (BOOT_START + i, boot_rom[i]);
|
|
PC = BOOT_START;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reader/punch attach */
|
|
|
|
t_stat cd_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
|
|
r = attach_unit (uptr, cptr);
|
|
if (r != SCPE_OK) /* attach */
|
|
return r;
|
|
if (sim_switches & SWMASK ('T')) /* text? */
|
|
uptr->flags = uptr->flags & ~UNIT_CBN;
|
|
else if (sim_switches & SWMASK ('C')) /* column binary? */
|
|
uptr->flags = uptr->flags | UNIT_CBN;
|
|
else if (match_ext (cptr, "TXT")) /* .txt? */
|
|
uptr->flags = uptr->flags & ~UNIT_CBN;
|
|
else if (match_ext (cptr, "CBN")) /* .cbn? */
|
|
uptr->flags = uptr->flags | UNIT_CBN;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reader/punch set mode - valid only if not attached */
|
|
|
|
t_stat cd_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
return (uptr->flags & UNIT_ATT)? SCPE_NOFNC: SCPE_OK;
|
|
}
|
|
|
|
/* Card punch select */
|
|
|
|
t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit)
|
|
{
|
|
if (sel & CHSL_NDS) /* nds? nop */
|
|
return ch6_end_nds (ch);
|
|
|
|
switch (sel) { /* case on cmd */
|
|
|
|
case CHSL_WRS: /* write */
|
|
if ((cdp_unit.flags & UNIT_ATT) == 0) /* not attached? */
|
|
return SCPE_UNATT;
|
|
if (sim_is_active (&cdp_unit)) /* busy? */
|
|
return ERR_STALL;
|
|
cdp_sta = CDS_INIT; /* initial state */
|
|
sim_activate (&cdp_unit, cdp_tstart); /* start punch */
|
|
break;
|
|
|
|
default: /* other */
|
|
return STOP_ILLIOP; /* not allowed */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Channel write routine - write word to buffer, write card when full */
|
|
|
|
t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 eorfl)
|
|
{
|
|
cdp_chob = val & DMASK; /* store data */
|
|
cdp_chob_v = 1; /* buffer valid */
|
|
if (cdp_sta == CDS_DATA) {
|
|
cdp_bbuf[cdp_bptr++] = cdp_chob; /* store data */
|
|
if ((cdp_bptr >= CD_BINLNT) || eorfl) { /* end card or end rec? */
|
|
ch6_set_flags (CH_A, U_CDP, CHF_EOR); /* set eor */
|
|
return cdp_card_end (&cdp_unit); /* write card */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
/* Unit timeout */
|
|
|
|
t_stat cdp_svc (UNIT *uptr)
|
|
{
|
|
uint32 i;
|
|
|
|
switch (cdp_sta) { /* case on state */
|
|
|
|
case CDS_INIT: /* initial state */
|
|
for (i = 0; i < CD_BINLNT; i++) /* clear bin buffer */
|
|
cdp_bbuf[i] = 0;
|
|
cdp_sta = CDS_DATA; /* data state */
|
|
cdp_bptr = 0; /* init pointer */
|
|
ch6_req_wr (CH_A, U_CDP); /* request channel */
|
|
cdp_chob = 0; /* clr, inval buffer */
|
|
cdp_chob_v = 0;
|
|
sim_activate (uptr, cdp_tleft); /* go again */
|
|
break;
|
|
|
|
case CDS_DATA: /* data state */
|
|
if (!ch6_qconn (CH_A, U_CDP)) /* chan disconnect? */
|
|
return cdp_card_end (uptr); /* write card */
|
|
if (cdp_chob_v) /* valid? clear */
|
|
cdp_chob_v = 0;
|
|
else ind_ioc = 1; /* no, io check */
|
|
ch6_req_wr (CH_A, U_CDP); /* req channel */
|
|
sim_activate (uptr, (cdp_bptr & 1)? cdp_tleft: cdp_tright);
|
|
break;
|
|
|
|
case CDS_END: /* end state */
|
|
if (ch6_qconn (CH_A, U_CDP)) { /* if cdp still conn */
|
|
cdp_sta = CDS_INIT; /* return to init */
|
|
sim_activate (uptr, 1); /* next card */
|
|
}
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Card end - write card image to file, transition to end state */
|
|
|
|
t_stat cdp_card_end (UNIT *uptr)
|
|
{
|
|
uint32 i, col, row, bufw, colbin;
|
|
const char *pch;
|
|
char bcd, cdp_cbuf[(2 * CD_CHRLNT) + 2];
|
|
t_uint64 dat;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */
|
|
return SCPE_UNATT;
|
|
if (uptr->flags & UNIT_PCA)
|
|
pch = bcd_to_ascii_a;
|
|
else pch = bcd_to_ascii_h;
|
|
for (col = 0; col < ((2 * CD_CHRLNT) + 1); col++)
|
|
cdp_cbuf[col] = ' '; /* clear char buf */
|
|
for (col = 0; col < 72; col++) { /* process 72 columns */
|
|
colbin = 0;
|
|
dat = bit_masks[35 - (col % 36)]; /* mask for column */
|
|
for (row = 0; row < 12; row++) { /* proc 12 rows */
|
|
bufw = (row * 2) + (col / 36); /* index to buffer */
|
|
if (cdp_bbuf[bufw] & dat)
|
|
colbin |= col_masks[row];
|
|
}
|
|
if (cdp_unit.flags & UNIT_CBN) { /* column binary? */
|
|
cdp_cbuf[2 * col] = pch[(colbin >> 6) & 077];
|
|
cdp_cbuf[(2 * col) + 1] = pch[colbin & 077];
|
|
}
|
|
else { /* text */
|
|
bcd = colbin_to_bcd (colbin); /* column bin -> BCD */
|
|
cdp_cbuf[col] = pch[bcd & 077]; /* -> ASCII */
|
|
}
|
|
}
|
|
for (i = ((2 * CD_CHRLNT) + 1); (i > 0) &&
|
|
(cdp_cbuf[i - 1] == ' '); --i) ; /* trim spaces */
|
|
cdp_cbuf[i++] = '\n'; /* append nl */
|
|
cdp_cbuf[i++] = 0; /* append nul */
|
|
fputs (cdp_cbuf, uptr->fileref); /* write card */
|
|
if (ferror (uptr->fileref)) { /* error? */
|
|
sim_perror ("CDP I/O error");
|
|
clearerr (uptr->fileref);
|
|
return SCPE_IOERR;
|
|
}
|
|
uptr->pos = uptr->pos + strlen (cdp_cbuf); /* update position */
|
|
cdp_sta = CDS_END; /* end state */
|
|
sim_cancel (uptr); /* cancel current */
|
|
sim_activate (uptr, cdp_tstop); /* long timer */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Card punch reset */
|
|
|
|
t_stat cdp_reset (DEVICE *dptr)
|
|
{
|
|
uint32 i;
|
|
|
|
for (i = 0; i < 24; i++) /* clear buffer */
|
|
cdp_bbuf[i] = 0;
|
|
cdp_sta = 0; /* clear state */
|
|
cdp_bptr = 0; /* clear buf ptr */
|
|
cdp_chob = 0;
|
|
cdp_chob_v = 0;
|
|
sim_cancel (&cdp_unit); /* stop punch */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Column binary to BCD
|
|
|
|
This is based on documentation in the IBM 1620 manual and may not be
|
|
accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit
|
|
pattern, and the appropriate bits are set. (Double punches inclusive
|
|
OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected;
|
|
since the 7094 only reads column binary, double punches are ignored.
|
|
|
|
Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
|
|
The for loop works right to left, so the table is reversed. */
|
|
|
|
static const char row_val[12] = {
|
|
011, 010, 007, 006, 005, 004,
|
|
003, 002, 001, 020, 040, 060
|
|
};
|
|
|
|
char colbin_to_bcd (uint32 cb)
|
|
{
|
|
uint32 i;
|
|
char bcd;
|
|
|
|
for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */
|
|
if (cb & (1 << i))
|
|
bcd |= row_val[i];
|
|
}
|
|
return bcd;
|
|
}
|